又一个有趣的面试题
大家还记得前些天的那个火柴棍式的面试题吗?很有趣吧。下面是我今天在StackExchange上看到的一个有趣的面试题。大家不妨一起来思考一下。问题如下——
有两个相同功能代码如下,请在在A,B,C是什么的情况下,请给出三个原因case 1比case 2快,还有三个原因case 2会比case 1要执行的快。(不考虑编译器优化)
for (i=0; i<N; ++i){ A; B; C; }
for (i=0; i<N; ++i){ A; } for (i=0; i<N; ++i){ B; } for (i=0; i<N; ++i){ C; }
我的第一个反应是——
- case1 要快一些,因为只有一个i++的i<N的操作,而case 2却有三个,这在点上,case 1就比case 2要快。
- case2如果要快的话,有一个原因是,A, B, C其中一个需要去先获得一个资源(比如一个锁),在case1下,每次都要去拿这个资源,而case2下,只需要拿一次然后。但这个可能是不对的,因为我无法想出一个相同的语句块放在case 1中会和放在case 2中有差别。(不过可能比较接近了)
继续思考:这个题有点像是“同步和异步”的问题,case 1是同步,case 2是异步,所以,异步快于同步,也许可以从这个方向出发,写出A, B, C的语句块。
不过,其要三个原因啊。各位,你们有想法吗?
—-更新 1—-
刚才在twitter上与人讨论,发现又有一种情况,case 2要比case 1要快。比如,A, B, C分别访问是不同的内存块(数组),那么case 1就得在不同的内存块上来回切换寻址,而case2则可以连续地访问内存块。访问连续的内存效率要高。尤其是三块大内存。
—-更新 2—
正如本贴评论中所说的,CPU的cache也是其中一个因素。大家对底层知识了解的都很不错啊。赞一个。
(转载本站文章请注明作者和出处 酷 壳 – CoolShell ,请勿用于任何商业用途)
《又一个有趣的面试题》的相关评论
如果ABC三个条件分别有三个不同的锁;如果case1要执行就必须三个锁都满足;
但如果case2只要A条件满足了,第一部分就可以执行完了,异步的满足三个条件情况下,case 2 比case 1要快吧?
如果abc都是++i的话1 就比2快
我觉得题目的思想是类似这样的
如果A,B,C 都是 读 机械磁盘 的操作,且分别读3个不同位置的连续磁盘块。那么Case2比Case1快。
同样,如果A,B,C在同一个循环内需要换页而连续执行N遍A不需要换页的情况下,也是Case2比Case1块。
空间局部性? 如果ABC在一次循环中都倾向于读同一块空间, 而在N次循环中都分别要不同得空间。那CASE1必CASE2块?
时间局部性? CASE2 每次都是执行同一个代码,CASE1没个循环执行不同代码,CASE2比CASE1快?
A B C分别操作不同的连续内存的话,case 2会比1快
如果ABC都是++i,那就不满足题设“有两个相同功能代码”@adri1
如果A,B,C的运算量很大,但各自的数据刚好能填满CPU的cache,那么2有可能比1快
int a;
int b;
int c;
在退栈的时候应该快一些吧(小弟学艺尚浅,胡乱说的)
Stack Exchange上的Data Local、Code Local的想法我觉得比较好。
我曾遇到这样一个问题,在一组(可能上亿个)整数(范围0~1亿)找top1000的数,发现,使用归并排序效果比计数排序(未优化的)要好。
原因是,归并排序数据的局部性好,这样CPU可以将很多数据放到cache中(没记错的话访问时间是5ns),而计数排序需要随机对内存访问(几百ns)。
原文说 A,B,C之间不共享任何资源哦
编译器优化是需要考虑的,循环中如果语句比较少,互相依赖少,编译器会容易做 loop unroll 来尽量填满cpu 的pipeline ( 在SGI STL 中,有很多手动的loop unroll)
当脑筋急转弯想怎样…A是break,B和C是sleep…之类的…
真折腾。。。。有时间折腾这个还不如多思考一下人生呢。
请问你用的这个是什么编译器
吼吼今天老师上课刚讲过这个问题,假如A、B、C的操作的数据是在硬盘上而不是内存上,那么每次操作位置接近的一个block里面的数据就比每次操作都data seek要快很多。
如果访问网络资源,如ABC共用同一远程数据库连接则CASE1快(频繁的打开关闭连接也是很大的花销)
另外附加一个扩展问题:
什么情况下CASE1与CASE2等价!这题目就更有意思了!!!
如果一个算法比另一个算法更快(处理同一规模问题),原因有二
一:算法1比算法2在运行时代码总数更少即耗CPU等资源低。
二:如果两算法代码运行时的总量相等,那么更快的那个算法它的资源竞争更少!
否则它们两个是同一算法!
case1比較快:
1. ABC皆為空白
2. A: break; B: continue; C:continue;
3. A: i=N-2; B: i=N-1; C:i=N;
case2比較快:
1. A: hereisA : sleep(1); goto hereisB;
B: hereisB : sleep(1); goto hereisC;
C: hereisC: sleep(1);
2. A: fseek(pFile, filelen, SEEK_SET); B: fseek(pFile, 0, SEEK_END); C: fseek(pFile, filelen, SEEK_SET);
3. A: use GDI to draw a line. B: use GDI to draw a bitmap. C: use GDI to draw a line.
(硬體繪圖加速器內的command queue會因為繪圖模式的改變而破壞掉cached instruction)
—
這樣回答會不會被轟出門去…?
a中改变n或i的值,c再把n或i变回来,case2的b循环就能变快或变慢
http://igoro.com/archive/gallery-of-processor-cache-effects/
自从看过这篇文章后,对于这类问题,第一反应就是利用cache了
在哪本书上看到过这个问题。主要说的就是楼上说的CPU Cache是有限的,语句块较小的循环语句被优化。如果语句块太大,将无法优化。是C2可能比C1快的一个原因。
see see
既然考虑了cache之类的问题,那或许也应该考虑BTB了,如果A、B、C都包含一些跳转语句的话,放在一起可能超过BTB的大小导致预测失败的情况增多,CPU多次清空流水线,降低效率;而分开后则有可能都不大于BTB的长度,预测成功的次数大大增加,提高了效率。
第一感觉 case2 比 case1 快 就是从 上下文的切换上。如果A\B\C需要 很多的上下文切换的话。
glibc的string里很多宏都用do…while,大概也是这个原理吧……
低层掌握的不行
嗯 话说我只是一名Web开发者。底层理解不够。
不过如果A是登陆 B是浏览 C是退出的情况下的话 Case2是比Case1快的
因为每次登陆时候都要首先判断是否成功登陆 然后设置浏览器cookies和服务器端的会话状态
退出就将这些信息清理
不过如果你登陆一次之后再登陆的话就不用重新植入cookies了 因为会话状态还存在 所以直接判断登陆成功 退出亦然
无头绪啊,学习吧。
当CASE2中死循环 而CASE1中顺利执行 就会CASE1比CASE2快;
所以A为 i–;B为i++;C为空
我請你!
@Soleil 果然牛叉,佩服。。。
A模块中有一个需要判断是否已经初始化的开关量,如果未初始化则进行初始化操作,然后后续一些A的代码;
B模块与A模块相同,都需要判断这个是否初始化的开关量,如果为初始化则进行初始化,然后后续一些B的代码;
C模块则是取消初始化,则也同样需要判断该开关量,如果已经初始化,则取消初始化,然后一些C的代码。
在这种情况下,case1比case2慢。case1每次都需要经历初始化,取消初始化的操作;而case2只需要最后集中进行一次即可。
@jerome
并不需要A,B,C三个锁都满足…满足一个就执行一个,不过会堵塞吧
这种情况下,Case2 也是一样的处理,case1还是会快一点
(1)A:i++;
B : i++;
C : i++;
case1比case2快。
(2)A:i=n;
B : i=n;
C : i=i++;
case2比case1快。程序执行一次之后case2的A、B已经执行完,而case1的每次循环要执行A、B之后再C,case2只需执行C,故要快。
a : BufferedOutputStream.write
b : BufferedOutputStream.flush
如果在第一个循环中每次循环都调用上面的 a 或 b,每次都会 刷新缓冲的输出流
而如果在第一个循环中调用 a ,而在第二个循环中调用 b 的话,调用 b 的时候,只有第一次会刷新 缓冲的输出流,而之后的 flush 由于已经刷新了缓冲的输出流又没有新的数据,其调用便是不起效果的,因此在循环次数很高的情况下,多个循环会大于一个循环的效率,当然这是一种钻牛角尖的想法:)
难道跟x86流水线作业有关系….
NB
cache是分块的,可能缓存不命中。参看《深入理解计算机原理》
折腾的人生才有意思@srdgame
如果A,B,C操作了i和N呢?
想到连个场景:
一、A、B、C本身是复杂的循环。
比如C语言,超出堆栈后分配为堆,对于多层循环表现明显。 这时候case 2 比cese 1 快。
二、A、B、C分别操纵内存之外的其他资源,想到的实例如A、B、C分别操纵3个不同的数据库,A、B、C均从创建连接到操作完成。 这时候case 2 可能比case 1快。
因为case 2能够密集的使用外部资源特性,比如数据库连接cache和数据库的cache。
没学过算法,不知道怎么分析。以后学了可能会解决。先看看其他人的答案吧!
有的情况下两段代码的功能是不一样的。
@haibarahu
。。。sigh。。。前提是功能一样。。