博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
[性能优化]DateFormatter轻度优化探索
阅读量:5783 次
发布时间:2019-06-18

本文共 5238 字,大约阅读时间需要 17 分钟。

为什么写这篇文章

1.之前在一些性能优化的文章中,看到有提到“创建DateFormatter开销会比较大”,也有的文章里面说是“设置日期格式”这个方法较为耗时,但实际上测试发现是“生成字符串”这个方法较为耗时,所以我觉得可以纠正一些这些说法

let formatter = DateFormatter()//创建DateFormatter实例对象formatter.dateFormat = "yyyy年MM月dd日"//设置日期格式string = formatter.string(from: date)//生成字符串

2.很多同学可能只是跟我之前一样,只是知道这个方法比较耗时,但是对于进行缓存优化后的效果对比并不清楚,所以自己写了一个小Demo,对优化前后进行一些性能测试,方便大家参考,也方便大家在项目中使用。

运行时间对比

class ViewController: UIViewController {    override func viewDidLoad() {        super.viewDidLoad()        testInOldWay(1)        testInNewWay(1)                testInOldWay(10)        testInNewWay(10)                testInOldWay(100)        testInNewWay(100)                testInOldWay(1000)        testInNewWay(1000)                testInOldWay(10000)        testInNewWay(10000)                testInOldWay(100000)        testInNewWay(100000)                testInOldWay(1000000)        testInNewWay(1000000)    }    //不进行缓存    func testInOldWay(_ times: Int) {        var string = ""        let date = Date.init()        let startTime = CFAbsoluteTimeGetCurrent();        for _ in 0..

日志输出

使用oldWay计算1次,总耗时7.187008857727051 ms使用newWay计算1次,总耗时0.1609325408935547 ms使用oldWay计算10次,总耗时0.552058219909668 ms使用newWay计算10次,总耗时0.05888938903808594 ms使用oldWay计算100次,总耗时4.320979118347168 ms使用newWay计算100次,总耗时0.6080865859985352 ms使用oldWay计算1000次,总耗时47.60599136352539 ms使用newWay计算1000次,总耗时5.526900291442871 ms使用oldWay计算10000次,总耗时427.8249740600586 ms使用newWay计算10000次,总耗时45.81403732299805 ms使用oldWay计算100000次,总耗时4123.620986938477 ms使用newWay计算100000次,总耗时459.98501777648926 ms使用oldWay计算1000000次,总耗时40522.77398109436 ms使用newWay计算1000000次,总耗时4625.54395198822 ms

执行时间统计:

在测试中,我们发现执行一次formatter的创建和设置日期格式需要7.187008857727051 ms,执行10次却只需要0.552058219909668 ms,这是因为第一次执行let formatter = DateFormatter()这行代码时可能会涉及到DateFormatter类相关的一些初始资源的初始化,而后续执行十次时已经不包含这一过程所需要的耗时,所以看上去执行一次的时间反而长一些,我们在计算性能比较时可以通过增加执行次数,来忽略这些因素的影响,当我们执行1000000次时,不进行缓存使用oldWay计算需要40522.77398109436 ms,而一次初始化的开销最大为第一次的执行的耗时7.187008857727051 ms,

7.18/40522.77 = 0.0177%

时间占比为0.0177,这些因素的影响已经降低为万分之一了,所以我们可以将执行1000000次时,不使用缓存和使用缓存的执行一次所需平均时间方法耗时

不使用缓存(oldWay,每次创建DateFormatter对象并且设置格式)执行一次耗时:40.52 us使用缓存(oldWay,每次创建DateFormatter对象并且设置格式)执行一次耗时:4.625 us使用缓存的方案的执行时间大概是不使用缓存的方案的时间的11.4%

究竟是创建DateFormatter对象耗时还是设置日期格式耗时呢?

func testPartInOldWay(_ times: Int) {        var string = ""        let date = Date.init()        var startTime1: CFAbsoluteTime = 0;        var startTime2: CFAbsoluteTime = 0;        var startTime3: CFAbsoluteTime = 0;        var startTime4: CFAbsoluteTime = 0;        var duration1: CFAbsoluteTime = 0;        var duration2: CFAbsoluteTime = 0;        var duration3: CFAbsoluteTime = 0;        for i in 0..

输出结果:

执行1次创建DateFormatter对象耗时=0.030994415283203125ms设置日期格式耗时=0.3859996795654297ms生成字符串耗时=1.6570091247558594ms执行10次创建DateFormatter对象耗时=0.019073486328125ms设置日期格式耗时=0.012159347534179688ms生成字符串耗时=0.5759000778198242ms执行100次创建DateFormatter对象耗时=0.0768899917602539ms设置日期格式耗时=0.06973743438720703ms生成字符串耗时=4.322528839111328ms执行1000次创建DateFormatter对象耗时=0.7123947143554688ms设置日期格式耗时=0.702977180480957ms生成字符串耗时=41.77117347717285ms执行10000次创建DateFormatter对象耗时=6.549596786499023ms设置日期格式耗时=5.913138389587402ms生成字符串耗时=370.6216812133789ms执行100000次创建DateFormatter对象耗时=65.13833999633789ms设置日期格式耗时=59.78119373321533ms生成字符串耗时=3586.0002040863037ms执行1000000次创建DateFormatter对象耗时=661.7592573165894ms设置日期格式耗时=575.5696296691895ms生成字符串耗时=35309.07988548279ms

可以从输出结果中发现是string = formatter.string(from: date)这行代码耗费时间最多,所以主要耗时并不在于执行DateFormatter.init()和formatter.dateFormat = "yyyy年MM月dd日",在对我们项目使用Instrument进行分析时,测试结果也证明了这一点

测试环境:iPhone 7

测试系统:iOS 12.1(16B92)

app启动后的60s内,快速滑动feed流页面,在这一过程中,主线程的执行时间大概是10.59s,我们项目中日期处理主要在func detailString(date: Date) -> String这个方法中进行,这个方法的运行时间为730ms,而其中 timeStr = formatter.string(from: date)这行代码的运行时间为628ms,所以也说明了生成日期字符串的方法耗时较多。

在项目中的实际提升

测试环境:iPhone 7

测试系统:iOS 12.1(16B92)

测试时间:app启动后的60s

测试步骤:使用Instruments的Time Profiler启动app,在启动后的60s内,快速滑动列表页。

没有对DateFormatter进行缓存时:

在我们项目中,detailString方法每次调用时会创建DateFormatter,生成日期字符串

let formatter = DateFormatter()formatter.dateFormat = "MM月dd日"timeStr = formatter.string(from: date)

测试结果:

app启动后的60s内,主线程执行时间10.59s,detailString的执行730ms

对DateFormatter进行缓存后:

timeStr = DateFormatterCache.shared.dateFormatterOne.string(from: date)    class DateFormatterCache {        //使用方法        //let timeStr = DateFormatterCache.shared.dateFormatterOne.string(from: publishTime)        static let shared = DateFormatterCache.init()                lazy var dateFormatterOne: DateFormatter = {            let formatter = DateFormatter()            formatter.dateFormat = "MM月dd日"            return formatter    }()

我们通过DateFormatterCache的单例对象shared来获取dateFormatterOne

测试结果:

app启动后的60s内,主线程执行时间10.58s,detailString的执行76ms

从执行时间上对比,缓存后,执行时间是之前的10.4%,对性能的提升还是比较大的

最后

因为系统内部的实现,我们看不到源码,我在私下针对DateFormatter的创建,设置日期格式,生成字符串三个步骤分别做过大量测试,但是也有可能是测试方法的局限性(是通过统计每个步骤调用时间来汇总的,没法通过调用一百万次方法来计算总时间来统计的),暂时来说无法分析出具体是哪一步骤是主要耗时的,但是在项目中,如果使用单例来对创建,设置日期格式这两个步骤来缓存,使用Instrument进行分析时确实可以将运行时间降为不缓存时的10%左右。

Demo在这里

PS:

最近加了一些iOS开发相关的QQ群和微信群,但是感觉都比较水,里面对于技术的讨论比较少,所以自己建了一个iOS开发进阶讨论群,欢迎对技术有热情的同学扫码加入,加入以后你可以得到:

1.技术方案的讨论,会有在大厂工作的高级开发工程师尽可能抽出时间给大家解答问题

2.每周定期会写一些文章,并且转发到群里,大家一起讨论,也鼓励加入的同学积极得写技术文章,提升自己的技术

3.如果有想进大厂的同学,里面的高级开发工程师也可以给大家内推,并且针对性得给出一些面试建议

群已经满100人了,想要加群的小伙伴们可以扫码加这个微信,备注:“加群+昵称”,拉你进群,谢谢了

转载地址:http://jijyx.baihongyu.com/

你可能感兴趣的文章
js实现复选框的操作-------Day41
查看>>
数据结构化与保存
查看>>
[SpringBoot] - 配置文件的多种形式及优先级
查看>>
chrome浏览器开发者工具之同步修改至本地
查看>>
debian7 + wheezy + chromium + flashplayer
查看>>
AOP
查看>>
进阶开发——文档,缓存,ip限速
查看>>
vue中子组件需调用父组件通过异步获取的数据
查看>>
uva 11468 - Substring(AC自己主动机+概率)
查看>>
Mysql 数据备份与恢复,用户创建,授权
查看>>
沉思录
查看>>
Angular.js中的$injector服务
查看>>
构建之法读书笔记01
查看>>
linux - lsof 命令最佳实践
查看>>
kafka性能测试
查看>>
现实世界的Windows Azure:h.e.t软件使用Windows Azure削减50%的成本
查看>>
深入.net框架
查看>>
聚合类新闻client产品功能点详情分析
查看>>
湘潭邀请赛——Alice and Bob
查看>>
js设置定时器
查看>>