如果提到性能分析,你会想到什么呢?
可以做耗时分析、内存占用的的分析。可以用 chrome devtools 的 Profiler,包括 performance 和 memory,分别拿到耗时和内存占用的数据,而且还可以用火焰图做可视化分析。
比如 performance,你可以看到每个函数的耗时,通过简单的加减法,就能算出是哪个函数耗时多,然后去优化。
而且,你可以勾选 memory,显示堆内存的变化,可以知道是哪个函数导致的内存增多,然后去优化。
当然,你也可以单独分析 memory 的 timeline,录制一段时间的内存占用情况,然后看这时候的内存中有哪些对象,这样比只知道大小更精确一些。
总之,我们可以通过调试工具的 Profiler 来看到内存和耗时,然后关联到具体的函数,之后着手去优化。
但是,这些都是代码跑起来才能统计的,而且与机器、不同的输入数据等强相关。
如果换一台机器,数据就是另一个样子了。这也是为啥测试的时候要用各种机器测一遍。
那如果想代码不运行就能估算出具体时间和内存占用大小,有什么思路么?
这就是复杂度分析技术做的事情了。
这篇文章我们来学下复杂度分析是如何估算时间的。
如果有这样一行代码:
const name = 'guang';
耗时多少,内存多少?
你可能说得跑跑看,可能耗时 1ms,内存 4 bytes,也可能耗时 2ms,内存 8bytes 等等,不同的机器和运行环境都会有不同。
但我们都把它作为 1 ,这个 1 不是 ms,不是 byte,只是说是一个耗时/内存占用的基本单元,也就是复杂度是 1。
那这样的代码呢?
function calc(n) {
for(let i = 0; i < n; i ++) {
//...
}
}
具体的数值随着 n 的增大而增大,复杂度是 n。
我们分析复杂度的时候,不会分析具体的耗时和内存占用,而是以语句作为复杂度的单元,也就是 1,随着输入的数据规模 n 而变化的复杂度作为 n。
我们知道了 1 和 n,就可以计算这些复杂度了:
function func(n) {
const a = 1;
const b = 2;
for (let i = 0; i < n; i ++) {
//...
}
}
这里面有两条语句,复杂度是 1 + 1,一个循环 n 次的语句,复杂度是 n,所以总复杂度是 2 + n。
复杂度(func) = 2 + n
当这个 n 逐渐变大的时候,比如 n 变成了 10000000,那这个 2 就可以忽略不计了。
也就是
复杂度(func) = O(n)
这个 O 是渐进复杂度的意思,也就是渐渐的增大的时候的复杂度。
有的同学说,这里是 2,所以可以省略,如果这里有 100000 条呢?是不是就不能省略了?
其实也会省略,因为不管多大,它的复杂度总是一个常数,是固定的,不会变化,所以不用分析进去,估算出的耗时或者内存占用加上它那固定的部分就可以了。而变化的部分才需要分析。
当我们计算渐进复杂度 O 的时候,常数会省略掉,因为它是固定的,不会变,而我们只分析变化的部分,也就是与 n 有关的部分。
上面只是有一个输入数据,规模是 n 的时候,复杂度与 n 有关。
如果有两个输入数据,规模分别为 m 和 n 的时候,那都要计算上,不能省略,因为都是变化的。
也就是 O(m + n)、 O(m * n) 这种。
我们明白了什么是 1,什么是 n,什么时候要同时计算 m 和 n,什么是渐进复杂度,为什么常数可以省略之后,就可以看一些实际的复杂度的例子了。
其实复杂度也就这么几种:O(n)、O(n^2)、O(logn)、O(2^n)、O(n!)
function func(n) {
const a = 1;
for(let i = 0; i < n; i ++) {
//...
}
}
这种就是 O(n),我们上面分析过。常数复杂度省略掉。
function func(n) {
for(let i = 0; i < n; i ++) {
for(let j = 0; j < n; j ++) {
//...
}
}
}
这种就是 O(n^2),同理,O(n^3)、O(n^4)等也一个意思,就是嵌套的时候,复杂度相乘。
let n = 100;
let i = 1;
while(i < n){
i = i * 2
}
这段代码要计算多少次,要看 i 乘以几次 2 才大于 100,也就是 log2n
那同理,也有 log3n,log4n 等复杂度,当渐进复杂度的时候,常数是不用计算的,所以都是 O(logn)
const fibnacci = function (n) {
if (n <= 1) return n;
return fibnacci(n - 1) + fibnacci(n - 2);
};
斐波那契数列我们都知道,可以用上面的递归来算。
这样算的话,n 每加 1,就多递归调用了 2 次 fibnacci 函数,也就是复杂度乘以 2 了,所以复杂度是 O(2^n)。
同理,如果 n 每加一,多递归执行 3 次,那就是 O(3^n) 的复杂度。
也就是说,n 每加一,多递归 a 次,那复杂度就是 O(a^n)。
function func(n) {
for(let i=0; i<n; i++) {
func(n-1);
}
}
上一条我们知道了,n 每加 1,多递归常数次,是指数型,那如果如果当 n 每加 1,多递归 n 次,这种就是复杂度 o(n!) 了。
为什么不是 O(n^n) 呢?因为 n 是变化的啊,所以是 O(n!)。
这基本是全部的时间复杂度情况了。当然,这里只是讨论了 n 一个纬度,再多一个纬度 m 的话,也是一样。
下面我们来区分一下这些时间复杂度的优劣。
我们学习了大 O 的渐进时间复杂度表示法,就是为了估算 n 与具体执行时间的关系。
上面分析出的几种时间复杂度,它们与具体执行时间的关系是什么样的呢?可以画出变化函数来分析。
可以看到,随着 n 的增大, O(n!) 和 O(2^n) 是耗时增加最快的,也就是说,这样的代码,n一旦大了,立马会卡死,不用跑我们就能分析出来。
那什么样是的不容易卡死的呢?O(n)、O(nlogn)、o(logn)这种,随着数据规模的增大,耗时也不会增大很多。
所以我们说:
根据这个结论,我们就可以评判一些代码写法的好坏,也就是算法的优劣了。
需要真实去跑代码么?不需要。
空间复杂度也就是堆栈内存的分配与输入数据规模 n 的关系。
这里不包括全局变量,为什么呢?全局变量不会动态变啊,就相当于常数,可以省略,只分析变化的堆栈内存的复杂度就好了。
空间复杂度的分析方式和时间复杂度是类似的,只是不是把每一条语句作为 1,而是只把会分配内存的语句作为 1 来分析。
比如下面这段代码的空间复杂度就是 O(n)。
function func(n) {
let arr = [];
for (let i = 0; i < n; i++) {
arr.push(i);
}
}
分析性能一般通过运行时的 Profiler 来收集数据,然后分析耗时和内存占用,比如 chrome devtoos 的 performance 和 memory 工具。
但是其实不用运行代码,我们也可以通过复杂度来估算出来:
我们把一条语句作为复杂度是 1,而随着输入数据规模 n 变化的为复杂度 n。
我们估算是为了分析出耗时/内存占用随着数据规模 n 的一个变化关系,所以会用 O(n) 来表达这种变化关系,叫做渐进时间复杂度。
求渐进时间复杂度时,常数可以省略,因为它们是固定不变的,而我们只需要分析变化的部分。
复杂度基本就 O(n) O(logn) O(n^2) O(2^n) O(n!) 这几种。
其中要注意的是 O(2^n) 就是当 n 每加一,多递归 2 次,而如果 n 每加 1,多递归 n 次,那么就是 O(n!) 的复杂度。
O(2^n) 和 O(n!) 的复杂度都是随着 n 增加,复杂度急剧增加的,也就是耗时/内存占用会急剧增加,这样的代码很容易卡死,所以是不好的。
而 O(logn) O(n) 都是随着 n 增加,复杂度增加很少的。也就意味了耗时更少,内存占用更少。这样的算法当然也就更好了。
所以我们就是通过复杂度来评价算法好坏的,它就代表了耗时/内存占用,但不是直接表示的,而是抽象的表示。
如果说想得到不同机器、环境下的具体耗时/内存占用,那么就用 Profiler 在运行时收集数据,然后做分析和可视化,否则,其实通过复杂度就能够抽象的估算出来大概的耗时和内存占用。
性能分析不一定得用 Profiler,复杂度分析也行,它能评价一个代码写法(算法)的好坏,进而估算出性能。
- EOF -
本文由哈喽比特于3年以前收录,如有侵权请联系我们。
文章来源:https://mp.weixin.qq.com/s/JRmo1Wkc9w34b_ygaIuTBg
京东创始人刘强东和其妻子章泽天最近成为了互联网舆论关注的焦点。有关他们“移民美国”和在美国购买豪宅的传言在互联网上广泛传播。然而,京东官方通过微博发言人发布的消息澄清了这些传言,称这些言论纯属虚假信息和蓄意捏造。
日前,据博主“@超能数码君老周”爆料,国内三大运营商中国移动、中国电信和中国联通预计将集体采购百万台规模的华为Mate60系列手机。
据报道,荷兰半导体设备公司ASML正看到美国对华遏制政策的负面影响。阿斯麦(ASML)CEO彼得·温宁克在一档电视节目中分享了他对中国大陆问题以及该公司面临的出口管制和保护主义的看法。彼得曾在多个场合表达了他对出口管制以及中荷经济关系的担忧。
今年早些时候,抖音悄然上线了一款名为“青桃”的 App,Slogan 为“看见你的热爱”,根据应用介绍可知,“青桃”是一个属于年轻人的兴趣知识视频平台,由抖音官方出品的中长视频关联版本,整体风格有些类似B站。
日前,威马汽车首席数据官梅松林转发了一份“世界各国地区拥车率排行榜”,同时,他发文表示:中国汽车普及率低于非洲国家尼日利亚,每百户家庭仅17户有车。意大利世界排名第一,每十户中九户有车。
近日,一项新的研究发现,维生素 C 和 E 等抗氧化剂会激活一种机制,刺激癌症肿瘤中新血管的生长,帮助它们生长和扩散。
据媒体援引消息人士报道,苹果公司正在测试使用3D打印技术来生产其智能手表的钢质底盘。消息传出后,3D系统一度大涨超10%,不过截至周三收盘,该股涨幅回落至2%以内。
9月2日,坐拥千万粉丝的网红主播“秀才”账号被封禁,在社交媒体平台上引发热议。平台相关负责人表示,“秀才”账号违反平台相关规定,已封禁。据知情人士透露,秀才近期被举报存在违法行为,这可能是他被封禁的部分原因。据悉,“秀才”年龄39岁,是安徽省亳州市蒙城县人,抖音网红,粉丝数量超1200万。他曾被称为“中老年...
9月3日消息,亚马逊的一些股东,包括持有该公司股票的一家养老基金,日前对亚马逊、其创始人贝索斯和其董事会提起诉讼,指控他们在为 Project Kuiper 卫星星座项目购买发射服务时“违反了信义义务”。
据消息,为推广自家应用,苹果现推出了一个名为“Apps by Apple”的网站,展示了苹果为旗下产品(如 iPhone、iPad、Apple Watch、Mac 和 Apple TV)开发的各种应用程序。
特斯拉本周在美国大幅下调Model S和X售价,引发了该公司一些最坚定支持者的不满。知名特斯拉多头、未来基金(Future Fund)管理合伙人加里·布莱克发帖称,降价是一种“短期麻醉剂”,会让潜在客户等待进一步降价。
据外媒9月2日报道,荷兰半导体设备制造商阿斯麦称,尽管荷兰政府颁布的半导体设备出口管制新规9月正式生效,但该公司已获得在2023年底以前向中国运送受限制芯片制造机器的许可。
近日,根据美国证券交易委员会的文件显示,苹果卫星服务提供商 Globalstar 近期向马斯克旗下的 SpaceX 支付 6400 万美元(约 4.65 亿元人民币)。用于在 2023-2025 年期间,发射卫星,进一步扩展苹果 iPhone 系列的 SOS 卫星服务。
据报道,马斯克旗下社交平台𝕏(推特)日前调整了隐私政策,允许 𝕏 使用用户发布的信息来训练其人工智能(AI)模型。新的隐私政策将于 9 月 29 日生效。新政策规定,𝕏可能会使用所收集到的平台信息和公开可用的信息,来帮助训练 𝕏 的机器学习或人工智能模型。
9月2日,荣耀CEO赵明在采访中谈及华为手机回归时表示,替老同事们高兴,觉得手机行业,由于华为的回归,让竞争充满了更多的可能性和更多的魅力,对行业来说也是件好事。
《自然》30日发表的一篇论文报道了一个名为Swift的人工智能(AI)系统,该系统驾驶无人机的能力可在真实世界中一对一冠军赛里战胜人类对手。
近日,非营利组织纽约真菌学会(NYMS)发出警告,表示亚马逊为代表的电商平台上,充斥着各种AI生成的蘑菇觅食科普书籍,其中存在诸多错误。
社交媒体平台𝕏(原推特)新隐私政策提到:“在您同意的情况下,我们可能出于安全、安保和身份识别目的收集和使用您的生物识别信息。”
2023年德国柏林消费电子展上,各大企业都带来了最新的理念和产品,而高端化、本土化的中国产品正在不断吸引欧洲等国际市场的目光。
罗永浩日前在直播中吐槽苹果即将推出的 iPhone 新品,具体内容为:“以我对我‘子公司’的了解,我认为 iPhone 15 跟 iPhone 14 不会有什么区别的,除了序(列)号变了,这个‘不要脸’的东西,这个‘臭厨子’。