又一个有趣的面试题

又一个有趣的面试题

大家还记得前些天的那个火柴棍式的面试题吗?很有趣吧。下面是我今天在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 ,请勿用于任何商业用途)

好烂啊有点差凑合看看还不错很精彩 (17 人打了分,平均分: 3.82 )
Loading...

又一个有趣的面试题》的相关评论

  1. 如果ABC三个条件分别有三个不同的锁;如果case1要执行就必须三个锁都满足;
    但如果case2只要A条件满足了,第一部分就可以执行完了,异步的满足三个条件情况下,case 2 比case 1要快吧?

  2. 如果A,B,C 都是 读 机械磁盘 的操作,且分别读3个不同位置的连续磁盘块。那么Case2比Case1快。
    同样,如果A,B,C在同一个循环内需要换页而连续执行N遍A不需要换页的情况下,也是Case2比Case1块。

  3. 空间局部性? 如果ABC在一次循环中都倾向于读同一块空间, 而在N次循环中都分别要不同得空间。那CASE1必CASE2块?
    时间局部性? CASE2 每次都是执行同一个代码,CASE1没个循环执行不同代码,CASE2比CASE1快?

  4. 如果A,B,C的运算量很大,但各自的数据刚好能填满CPU的cache,那么2有可能比1快

  5. int a;
    int b;
    int c;
    在退栈的时候应该快一些吧(小弟学艺尚浅,胡乱说的)

  6. Stack Exchange上的Data Local、Code Local的想法我觉得比较好。

    我曾遇到这样一个问题,在一组(可能上亿个)整数(范围0~1亿)找top1000的数,发现,使用归并排序效果比计数排序(未优化的)要好。
    原因是,归并排序数据的局部性好,这样CPU可以将很多数据放到cache中(没记错的话访问时间是5ns),而计数排序需要随机对内存访问(几百ns)。

  7. 编译器优化是需要考虑的,循环中如果语句比较少,互相依赖少,编译器会容易做 loop unroll 来尽量填满cpu 的pipeline ( 在SGI STL 中,有很多手动的loop unroll)

  8. 吼吼今天老师上课刚讲过这个问题,假如A、B、C的操作的数据是在硬盘上而不是内存上,那么每次操作位置接近的一个block里面的数据就比每次操作都data seek要快很多。

  9. 如果访问网络资源,如ABC共用同一远程数据库连接则CASE1快(频繁的打开关闭连接也是很大的花销)
    另外附加一个扩展问题:
    什么情况下CASE1与CASE2等价!这题目就更有意思了!!!

  10. 如果一个算法比另一个算法更快(处理同一规模问题),原因有二
    一:算法1比算法2在运行时代码总数更少即耗CPU等资源低。
    二:如果两算法代码运行时的总量相等,那么更快的那个算法它的资源竞争更少!

    否则它们两个是同一算法!

  11. 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)


    這樣回答會不會被轟出門去…?

  12. 在哪本书上看到过这个问题。主要说的就是楼上说的CPU Cache是有限的,语句块较小的循环语句被优化。如果语句块太大,将无法优化。是C2可能比C1快的一个原因。

  13. 既然考虑了cache之类的问题,那或许也应该考虑BTB了,如果A、B、C都包含一些跳转语句的话,放在一起可能超过BTB的大小导致预测失败的情况增多,CPU多次清空流水线,降低效率;而分开后则有可能都不大于BTB的长度,预测成功的次数大大增加,提高了效率。

  14. 第一感觉 case2 比 case1 快 就是从 上下文的切换上。如果A\B\C需要 很多的上下文切换的话。

  15. 嗯 话说我只是一名Web开发者。底层理解不够。

    不过如果A是登陆 B是浏览 C是退出的情况下的话 Case2是比Case1快的

    因为每次登陆时候都要首先判断是否成功登陆 然后设置浏览器cookies和服务器端的会话状态
    退出就将这些信息清理
    不过如果你登陆一次之后再登陆的话就不用重新植入cookies了 因为会话状态还存在 所以直接判断登陆成功 退出亦然

  16. 当CASE2中死循环 而CASE1中顺利执行 就会CASE1比CASE2快;
    所以A为 i–;B为i++;C为空

  17. A模块中有一个需要判断是否已经初始化的开关量,如果未初始化则进行初始化操作,然后后续一些A的代码;
    B模块与A模块相同,都需要判断这个是否初始化的开关量,如果为初始化则进行初始化,然后后续一些B的代码;
    C模块则是取消初始化,则也同样需要判断该开关量,如果已经初始化,则取消初始化,然后一些C的代码。
    在这种情况下,case1比case2慢。case1每次都需要经历初始化,取消初始化的操作;而case2只需要最后集中进行一次即可。

  18. @jerome
    并不需要A,B,C三个锁都满足…满足一个就执行一个,不过会堵塞吧
    这种情况下,Case2 也是一样的处理,case1还是会快一点

  19. (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,故要快。

  20. a : BufferedOutputStream.write
    b : BufferedOutputStream.flush

    如果在第一个循环中每次循环都调用上面的 a 或 b,每次都会 刷新缓冲的输出流

    而如果在第一个循环中调用 a ,而在第二个循环中调用 b 的话,调用 b 的时候,只有第一次会刷新 缓冲的输出流,而之后的 flush 由于已经刷新了缓冲的输出流又没有新的数据,其调用便是不起效果的,因此在循环次数很高的情况下,多个循环会大于一个循环的效率,当然这是一种钻牛角尖的想法:)

  21. Soleil :
    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)

    這樣回答會不會被轟出門去…?

    NB

  22. 想到连个场景:
    一、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。

  23. 没学过算法,不知道怎么分析。以后学了可能会解决。先看看其他人的答案吧!

回复 柳秦晓 取消回复

您的电子邮箱地址不会被公开。 必填项已用*标注