计算机组成原理——篇四

导学

  • 之前了解了高速缓存,为了提高效率,我们希望命中率越高越好,这需要合理的调度算法
  • 本篇主要实现双向链表,实现常见的缓存调度策略

双向链表

  • 这部分属于数据结构的内容,但学以致用,在这里实现
  • 不详细介绍概念了,优点(相较于单链表):
    • 可以快速找到当前节点的上一个节点
    • 可以快速去掉链表中的某一个节点(查找快)

链表节点

  • 存放key-value
  • 用python实现,打开PyCharm
  • 节点Node使用类实现:

DoubleLinkedList

  • 初始化链表:容量、头尾指针
  • 正常的对象方法逻辑:参数/返回值、判空、业务逻辑
  • 节点操作可以画示意图(前后指针一定要有着落)
    #!usr/bin/env python
    #-*- coding:utf-8 _*-
    """
    @author:瑞哥
    Summarize:
    """
    class Node:
    	'''
    	定义链表节点
    	params:
    		key	键
    		value	值
    		prev	前驱指针
    		next	后继指针
    	'''
    	def __init__(self, key,value):
    		self.key = key
    		self.value = value
    		self.prev = None
    		self.next = None
    	def __str__(self):	# 输出对象信息,面向用户:例如使用print()时
    		return '{%d: %d}'%(self.key,self.value)
    	def __repr__(self):	# 返回类对象的信息,面向开发者:控制台显示
    		return '{%d: %d}' %(self.key, self.value)
    
    class DoubleLinkedList:
    	'''
    	定义双向链表及方法
    	'''
    	def __init__(self, capacity):
    		'''
    		:param capacity: 链表最大容量
    		head	头节点指针
    		tail	尾节点指针
    		size	当前节点数
    		'''
    		self.capacity = capacity
    		self.head = None
    		self.tail = None
    		self.size = 0
    
    	def __add_head(self, node):	# 双前置下划线,类似private;单前置下划线,私有化属性和方法,import * 禁止导入,类似protected
    		'''
    		头部添加节点
    		:param node: 要保存的节点
    		:return: node
    		'''
    		if not self.head:
    			self.head = node
    			self.tail = node
    			self.head.next = None
    			self.head.prev = None
    		else:
    			node.next = self.head
    			node.prev = None
    			self.head.prev = node
    			self.head = node	# 头指针
    		self.size +=1
    		return node
    
    	def append_front(self, node):
    		self.__add_head(node)
    
    	def __add_tail(self, node):
    		'''
    		尾部添加节点
    		:param node:
    		:return:
    		'''
    		if not self.head:
    			self.head = node
    			self.tail = node
    			self.tail.next = None
    			self.tail.prev = None
    		else:
    			self.tail.next = node
    			node.prev = self.tail
    			self.tail = node	# 重置尾指针
    			self.tail.next = None
    		self.size += 1
    		return node
    
    	def append(self, node):
    		return self.__add_tail(node)
    
    	def __del_tail(self):
    		'''
    		删除尾部元素,弹出尾部节点
    		:return:
    		'''
    		if self.tail == None:
    			return
    		node = self.tail	# 准备返回
    		if not node.prev:
    			self.head=self.tail = None
    		else:
    			self.tail = node.prev
    			self.tail.next = None
    		self.size -= 1
    		return node
    
    	def __del_head(self):
    		'''
    		删除头部元素,也相当于弹出头部节点
    		:return:
    		'''
    		if not self.head:
    			return
    		node = self.head	# 准备返回
    		if not node.next:
    			self.head=self.tail = None
    		else:
    			self.head = node.next
    			self.head.prev = None
    		self.size -= 1
    		return node
    
    	def pop(self):
    		return self.__del_head()
    
    	def __remove(self, node=None):
    		'''
    		删除任意节点;这里Node单独创建,放在列表中,所以移除时可直接传入node,而不是通过传入value直接创建好链表,还得找元素!
    		:param node:传入节点指针
    		:return:
    		任何一门语言,数据类型、数据结构都是基础
    		'''
    		if not node:	# None 它属于NoneType类型(空值),None也是NoneType数据类型的唯一值(常用来判断无返回值的情况,或者初始化指针);与空列表、空字符串是不一样的(讲究个数据类型!)
    		# 可将其理解成指向特殊的“空”,在C中是null,可表示未初始化,查看内存为 cc cc cc cc,但并不是野指针
    			node = self.tail	# 默认删除尾部元素
    
    		if node==self.tail:
    			self.__del_tail()
    		elif node==self.head:
    			self.__del_head()
    		else:
    			node.prev.next = node.next
    			node.next.prev = node.prev
    			self.size -= 1
    		return node
    
    	def remove(self, node=None):	# 节点指针可能为空
    		return self.__remove(node)
    
    	def print(self):
    		'''
    		重载输出方法
    		:return:
    		'''
    		p = self.head
    		line = ''
    		while p:
    			line += '%s'%p	# __str__
    			p = p.next
    			if p:
    				line += '=>'
    		print(line)
    
    if __name__ == '__main__':
    	line = DoubleLinkedList(10)
    	nodes = []
    	for i in range(10):	# 准备kv对
    		node = Node(i, i)	# k-v
    		nodes.append(node)
    
    	line.append(nodes[0])
    	line.print()
    	line.append(nodes[1])
    	line.print()
    	line.append_front(nodes[3])
    	line.print()
    	line.remove(nodes[1])
    	line.print()
    # 从规范来讲,应将外部调用方法统一写在一起
    
  • 这里的方法后面要用到
  • 注意remove(),并不是删除,而是断开链接,node以独立的对象存在

FIFO

  • 使用双向链表实现FIFO算法
  • 关键在于put方法,要考虑到字块已存在和缓存满
  • 使用字典保存KV,方便查找
    #-*- coding:utf-8 _*-
    """
    @author:瑞哥
    @file: FIFO.py	
    Summarize:
    """
    from lru.Double import DoubleLinkedList, Node
    
    class FIFOCache:
        '''
        先进先出cache置换算法
        '''
        def __init__(self, capacity):
            self.capacity = capacity
            self.size = 0
            self.map = {}   # dict:key=node.key value = node 方便查找
            self.list = DoubleLinkedList(capacity)  # 存储KV的队列
    
    	# CPU取缓存
        def get(self, key):
            if key not in self.map:
                return -1
            else:
                node = self.map.get(key)
                return node.value
    
        def put(self,key,value):
            if self.capacity == 0:
                return -1
            if key in self.map: # 加入的元素在队列中,移到尾部(后加入的即最近使用的,后移出缓存)
                node = self.map.get(key)
                self.list.remove(node)
                node.value = value
                self.list.append(node)
            else:
                if self.size == self.capacity:
                    node = self.list.pop()  # __del_head
                    del self.map[node.key]	# 字典方法 del
                node = Node(key, value)
                self.list.append(node)
                self.map[key] = node
                self.size += 1
    
        def print(self):
            self.list.print()
    
    if __name__ == '__main__':
        cache = FIFOCache(2)
        cache.put(1, 1)
        cache.put(2, 2)
        cache.print()
        print(cache.get(1))
        cache.put(3, 3) # 移出1 1
        cache.print()
    

LRU

  • 这个算法普遍采用,很重要
  • 算法思想:
    jizu1
  • 代码结构和FIFO类似;
    • 这里的关键点在get()中,每次被CPU get后,此节点append_front()
    • put已存在节点时,执行get即可
    #!usr/bin/env python
    #-*- coding:utf-8 _*-
    """
    @author:瑞哥
    @file: LRU.py
    @time: 2021/03/23
    
    Summarize:
    """
    from lru.Double import DoubleLinkedList, Node
    
    class LRU:
        def __init__(self, capacity):
            self.capacity = capacity
            self.map = {}
            self.list = DoubleLinkedList(capacity)
    
        def get(self, key):
            if key in self.map: # self.map.keys() 字典的方法很多
                node = self.map[key]    # 这里涉及Python的深浅拷贝,这里属于直接赋值,map中的node是引用,所以能这样取node,如果是深拷贝是不能直接操作此node的
                self.list.remove(node)	# 默认del tail
                self.list.append_front(node)
                return node.value
            else:
                return -1
    
        def put(self, key, value):
            if key in self.map:
                self.get(key)   # 调用即可更新位置到队首 
            else:
                node = Node(key, value)
                if self.list.size >= self.capacity:
                    old_node = self.list.remove()  # 默认删除tail
                    self.map.pop(old_node.key)  # 更新map
                self.list.append(node)
                self.map[key] = node
    
        def print(self):
            self.list.print()
    
    
    if __name__ == '__main__':
        cache = LRU(3)
        cache.put(1, 1)
        cache.put(2, 2)
        cache.put(3, 3)
        cache.print()
        print(cache.get(3))
        cache.print()
    
        cache.put(2, 2)
        cache.print()
    

小结

  • 本篇先实现了双向链表,Node对象简化了节点操作;FIFO和LRU等算法的核心都是get/put方法,LRU操作节点的顺序实现了缓存的合理调度
  • 计算机组成原理和操作系统是相辅相成的,在指令的执行流程部分还有很多细节需要了解(必考),例如虚拟内存、微操作等,就总结在OS
  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值