YYCache 设计思路

cache_all_the_things

iOS 开发中总会用到各种缓存,最初我是用的一些开源的缓存库,但到总觉得缺少某些功能,或某些 API 设计的不够好用。YYCache (https://github.com/ibireme/YYCache) 是我新造的一个轮子,下面说一下这个轮子的设计思路。

 

内存缓存

通常一个缓存是由内存缓存和磁盘缓存组成,内存缓存提供容量小但高速的存取功能,磁盘缓存提供大容量但低速的持久化存储。相对于磁盘缓存来说,内存缓存的设计要更简单些,下面是我调查的一些常见的内存缓存。

NSCache 是苹果提供的一个简单的内存缓存,它有着和 NSDictionary 类似的 API,不同点是它是线程安全的,并且不会 retain key。我在测试时发现了它的几个特点:NSCache 底层并没有用 NSDictionary 等已有的类,而是直接调用了 libcache.dylib,其中线程安全是由 pthread_mutex 完成的。另外,它的性能和 key 的相似度有关,如果有大量相似的 key (比如 "1""2""3", ...),NSCache 的存取性能会下降得非常厉害,大量的时间被消耗在 CFStringEqual() 上,不知这是不是 NSCache 本身设计的缺陷。

TMMemoryCache 是 TMCache 的内存缓存实现,最初由 Tumblr 开发,但现在已经不再维护了。TMMemoryCache 实现有很多 NSCache 并没有提供的功能,比如数量限制、总容量限制、存活时间限制、内存警告或应用退到后台时清空缓存等。TMMemoryCache 在设计时,主要目标是线程安全,它把所有读写操作都放到了同一个 concurrent queue 中,然后用 dispatch_barrier_async 来保证任务能顺序执行。它错误的用了大量异步 block 回调来实现存取功能,以至于产生了很大的性能和死锁问题。

PINMemoryCache 是 Tumblr 宣布不在维护 TMCache 后,由 Pinterest 维护和改进的一个内存缓存。它的功能和接口基本和 TMMemoryCache 一样,但修复了性能和死锁的问题。它同样也用 dispatch_semaphore 来保证线程安全,但去掉了dispatch_barrier_async,避免了线程切换带来的巨大开销,也避免了可能的死锁。

YYMemoryCache 是我开发的一个内存缓存,相对于 PINMemoryCache 来说,我去掉了异步访问的接口,尽量优化了同步访问的性能,用 OSSpinLock 来保证线程安全。另外,缓存内部用双向链表和 NSDictionary 实现了 LRU 淘汰算法,相对于上面几个算是一点进步吧。

下面的单线程的 Memory Cache 性能基准测试:

memory_cache_bench_result

可以看到 YYMemoryCache 的性能不错,仅次于 NSDictionary + OSSpinLock;
NSCache 的写入性能稍差,读取性能不错;
PINMemoryCache 的读写性能也还可以,但读取速度差于 NSCache;
TMMemoryCache 性能太差以至于图上都看不出来了。

磁盘缓存

为了设计一个比较好的磁盘缓存,我调查了大量的开源库,包括 TMDiskCache、PINDiskCache、SDWebImage、FastImageCache 等,也调查了一些闭源的实现,包括 NSURLCache、Facebook 的 FBDiskCache 等。他们的实现技术大致分为三类:基于文件读写、基于 mmap 文件内存映射、基于数据库。

TMDiskCache, PINDiskCache, SDWebImage 等缓存,都是基于文件系统的,即一个 Value 对应一个文件,通过文件读写来缓存数据。他们的实现都比较简单,性能也都相近,缺点也是同样的:不方便扩展、没有元数据、难以实现较好的淘汰算法、数据统计缓慢。

FastImageCache 采用的是 mmap 将文件映射到内存。用过 MongoDB 的人应该很熟悉 mmap 的缺陷:热数据的文件不要超过物理内存大小,不然 mmap 会导致内存交换严重降低性能;另外内存中的数据是定时 flush 到文件的,如果数据还未同步时程序挂掉,就会导致数据错误。抛开这些缺陷来说,mmap 性能非常高。

NSURLCache、FBDiskCache 都是基于 SQLite 数据库的。基于数据库的缓存可以很好的支持元数据、扩展方便、数据统计速度快,也很容易实现 LRU 或其他淘汰算法,唯一不确定的就是数据库读写的性能,为此我评测了一下 SQLite 在真机上的表现。iPhone 64G 下,SQLite 写入性能比直接写文件要高,但读取性能取决于数据大小:当单条数据小于 20K 时,数据越小 SQLite 读取性能越高;单条数据大于 20K 时,直接写为文件速度会更快一些。这和 SQLite 官网的描述基本一致。另外,直接从官网下载最新的 SQLite 源码编译,会比 iOS 系统自带的 sqlite3.dylib 性能要高很多。基于 SQLite 的这种表现,磁盘缓存最好是把 SQLite 和文件存储结合起来:key-value 元数据保存在 SQLite 中,而 value 数据则根据大小不同选择 SQLite 或文件存储。NSURLCache 选定的数据大小的阈值是 16K;FBDiskCache 则把所有 value 数据都保存成了文件。

我的 YYDiskCache 也是采用的 SQLite 配合文件的存储方式,在 iPhone 64G 上的性能基准测试结果见下图。在存取小数据 (NSNumber) 时,YYDiskCache 的性能远远高出基于文件存储的库;而较大数据的存取性能则比较接近了。但得益于 SQLite 存储的元数据,YYDiskCache 实现了 LRU 淘汰算法、更快的数据统计,更多的容量控制选项。

disk_cache_bench_result

备注:

关于锁:

OSSpinLock 自旋锁,性能最高的锁。原理很简单,就是一直 do while 忙等。它的缺点是当等待时会消耗大量 CPU 资源,所以它不适用于较长时间的任务。对于内存缓存的存取来说,它非常合适。

dispatch_semaphore 是信号量,但当信号总量设为 1 时也可以当作锁来。在没有等待情况出现时,它的性能比 pthread_mutex 还要高,但一旦有等待情况出现时,性能就会下降许多。相对于 OSSpinLock 来说,它的优势在于等待时不会消耗 CPU 资源。对磁盘缓存来说,它比较合适。

关于 Realm:

Realm 是一个比较新的数据库,针对移动应用所设计。它的 API 对于开发者来说非常友好,比 SQLite、CoreData 要易用很多,但相对的坑也有不少。我在测试 SQLite 性能时,也尝试对它做了些简单的评测。我从 Realm 官网下载了它提供的 benchmark 项目,更新 SQLite 到官网最新的版本,并启用了 SQLite 的 sqlite3_stmt 缓存。我的评测结果显示 Realm 在写入性能上差于 SQLite,读取小数据时也差 SQLite 不少,读取较大数据时 Realm 有很大的优势。当然这只是我个人的评测,可能并不能反映真实项目中具体的使用情况。我想看看它的实现原理,但发现 Realm 的核心 realm-core 是闭源的(评论里 Realm 员工提到目前有在 Apache 2.0 授权下的开源计划),能知道的是 Realm 应该用 了 mmap 把文件映射到内存,所以才在较大数据读取时获得很高的性能。另外我注意到添加了 Realm 的 App 会在启动时向某几个 IP 发送数据,评论中有 Realm 员工反馈这是发送匿名统计数据,并且只针对模拟器和 Debug 模式。这部分代码目前是开源的,并且可以通过环境变量 REALM_DISABLE_ANALYTICS 来关闭,如果有使用 Realm 的可以注意一下。

转载于:https://www.cnblogs.com/fengmin/p/5318770.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/430705.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

几何画板椭圆九种画法_如何利用几何画板自定义工具画椭圆

椭圆在数学中占有一定的比重,是中学时代必学的圆锥曲线之一,也是历来考试的重点和难点。几何画板作为专业的几何绘图工具,可以用来画椭圆图形。前面的教程学习了在几何画板中利用椭圆定义画椭圆的方法,其实我们还可以利用自定义工…

python 下的数据结构与算法---6:6大排序算法

顶先最后推荐:哈哈,意思是放到顶部强调其重要性,但是应该我总结的六种算法看完了后再看的一篇醍醐灌顶的文章 一:冒泡排序(Bubble Sort) 原理:假设有n个数,第一轮时:从第…

python将整数逆序_python练手入门小项目:字符串的妙用

题目来源:小行:python练手入门小项目详解(一)1 给任意一个的正整数,要求:一、求它是几位数,二、逆序打印出各位数字。代码:def reversed_integer(number):assert type(number) is in…

精通 ASP.NET MVC 4 学习笔记(一)

这里记录着从 P132 到 P192 的内容。水分很足&#xff0c;大部分是书上的代码&#xff0c;我只加了一些基于我自己的理解的能帮助初学者看懂的注释&#xff0c;并且把书中的部分内容做了一些的拓展。 建立数据层 设置 DI 容器 /// <summary>/// 设置 DI 容器/// </sum…

python之 tkinter中菜单栏快速学习(菜单栏,弹出菜单等)

python之tkinter中菜单栏的快速学习 本文主要讲述菜单栏的一些应用&#xff0c;附上代码&#xff0c;供大家学习。 import tkinter#导入tkinter库 toptkinter.Tk()#创建一个主窗口 top.geometry(400x80000)#指定主窗口大小 top.wm_title(Menu菜单功能的实现) #设置标题名称 ma…

如何命令行远程连接centos_如何使用windows远程控制centOS桌面

如何远程控制centOS桌面? 如何使用windows远程控制centOS桌面?1.查看本机是否有安装vnc(centOS5默认有安装vnc)rpm -q vnc vnc-server如果显示结果为&#xff1a;package vnc is not installedvnc-server-4.1.2-14.e15_3.1那恭喜你&#xff0c;机器上已经安装了vnc&#xff0…

【大数据】Hadoop入门预告版

背景 当今世界&#xff0c;大数据无处不在&#xff0c;它影响到了我们的工作、生活和学习&#xff0c;并将继续施加更大的影响。 大数据用于描述这样的数据组&#xff0c;其规模超出了日常软件在可容忍期限内获取、管理和加工数据的能力。一些网络技术领先的公司持续地投资于昂…

Python ——告白小程序,添加微信号(快来设置你的freestyle吧)

朋友过生日&#xff0c;然后就在搜一些小程序&#xff0c;哈哈哈O(∩_∩)O哈哈~ 然后就搜到了一些表白的小程序&#xff0c; 然后再自己改了改&#xff0c;增加了一个添加微信的功能。然后加载了背景音乐&#xff0c;啦啦啦啦啦&#xff0c;赶紧告白起来吧 图片和音乐可以根据自…

airpodspro窃听模式_AirPods Pro实时收听怎么关闭? AirPods Pro实时收听的使用方法

苹果全新AirPods Pro增加了许多新功能。首先,是控制播放。苹果在AirPods Pro中放弃了敲击手势,并用杆上的力传感器取代了它。控制音乐或视频只需要简单的短按即可。一次按压即可播放或暂停当前播放。两次按压将跳至下一曲目,而三次挤压则向后移动。可以通过按压任一AirPods …

UML基础知识点

UML : unified Modeling Language 统一建模语言1.对系统问题进行分析和建模2.非专利的第三代建模和规约语言3.UML是一种开放的方法、用于说明、可视化、构建、编写一个正在开发的、面向对象的、软件密集系统的开放方法4.UML集成了Booch、OMT和面向对象软件工程的概念面向对…

如何实现listbox选项,然后双击鼠标实现选项的删除

如何实现双击鼠标左键然后删除列表框中的选项呢&#xff1f; 只需要两步&#xff1a; 第一步&#xff0c;建立listbox控件与鼠标事件的绑定并给一个跳转函数; 如本例中的listbox控件&#xff0c;绑定事件<Double-Button-1>&#xff0c;其含义是指双击鼠标左键&#xf…

因变量 方差膨胀系数_请问如何计算潜变量的方差膨胀因子(VIF)?

这里提供一个 Python 实现的方法和原理趣分析方差膨胀因子是非常经典缓解多元共线性的方法&#xff0c;原理简单&#xff0c;实现优雅&#xff0c;效果拔群。源代码源数据可私聊俺获取&#xff0c;每天固定时间查看和回复。原理趣析多重线性回归模型的主要假设之一是我们的预测…

下拉到底部加载更多

var p1; active_list();function active_list() {$.ajax({type: "GET",url: "{:U(User/glodLogList_data)}",data: {page:p},dataType: "json",success: function(data){if(datanull){$("#add_more").html(没有更多了);$("#add_m…

Print() 语句以及数字赋值语句 中if-else的使用

#print语句以及num里面if else 的变态语句 x10 num110 if x>8 else 20 #如果中间语句成立为True,则执行输出左边语句&#xff1b;否则为False 则输出右边语句 print(num1) num110 if x>15else 20 #如果中间语句成立为True,则执行输出左边语句&#xff1b;否则为False 则输…

幻世机器人_2014年科幻动作《幻世追踪/启动机械码》BD中英双字幕

◎译  名 幻世追踪/启动机械码(台)◎片  名 Vice◎年  代 2015◎国  家 美国◎类  别 动作/科幻/惊悚/冒险◎语  言 英语◎字  幕 中英双字幕◎IMDB评分 4.0/10 from 3,848 users◎文件格式 BD-RMVB◎视频尺寸 1280 x 720◎文件大小 1CD◎片  长 96…

单链表逆置

单链表的逆置问题&#xff0c;常常遇到&#xff0c;今天总结如下&#xff1a; 方法&#xff1a;头插法&#xff1a; 图示&#xff1a; 代码&#xff1a; //翻转单链表 ListNode* Revers(ListNode* pHead) {ListNode* newhead NULL;ListNode* cur pHead;while(cur){ListNode* …

if-else多级嵌套,输入3/4/5个数寻找最大值(太多了容易乱!!!)

哎呀呀&#xff0c;心累啊&#xff0c;玩这个多级嵌套&#xff0c;眼花~ 多级嵌套有序3个数&#xff08;找最大值&#xff09; a,b,ceval(input("a,b,c")) if a>b:pass #a (a,b)if a>c:print("max",a)if b>c:print(b,c)else:print(c,b)else:p…

页面 接收跳转数据_【实战案例】在线教育渠道落地页数据分析

商业数智化工作坊“ 与多位来自高校的专家学者与业界导师共同探索商业数字化、智能化发展趋势、技术及实践培养数字思维&#xff0c;掌握数智化工具&#xff0c;实现管理能力跃迁”1背景与目标通过对跟渠道落地页数据的分析&#xff0c;优化用户路径&#xff0c;提高各环节的转…

竞态条件和临界区

1. 临界区和竞态条件&#xff1a; 临界区&#xff1a;访问和操作共享数据的代码段&#xff1b; 竞态条件&#xff1a;当有多个线程同时进入临界区时&#xff0c;执行结果取决于线程的执行顺序&#xff1b; 如下述代码&#xff0c;当多个线程同时调用func函数&#xff0c;对共享…

MATLAB程序实现经纬度转换成平面坐标

近期搜了下经纬度坐标转换成直角坐标的程序和原理啥的&#xff0c;哎&#xff0c;真难啊&#xff0c;其实道理都懂吧&#xff0c;下面直接贴代码&#xff0c; 主要是加了个for循环而言&#xff0c;优化输出效果。 %%%MATLAB程序实现经纬度转换成平面尔坐标&#xff1a; M_PI3…