
coding 中经常遇到一种情况。对一个变量赋值,是直接赋值,还是判断之后再赋值。 体现在代码上,大概是下面这种情况( i 和 num 都是 int ): if (i > 0) num = i; 或者 num = i; 我的疑问是到底哪个更快,自己用 C#做了测试:在循环一亿次的情况下,直接赋值速度更快。但是判断赋值会有部分是执行了 if 中的代码的,所以会导致了实验不纯粹。
然后我又用下面的代码测试了下,单纯的对比赋值和比较的效率
static void Main(string[] args) { int num; Console.WriteLine(DateTime.Now.ToString("mm:ss.fff")); for (int i = 0; i < 100000000; i++) { if (i > 1000) { //num = int.MaxValue; } } Console.WriteLine(DateTime.Now.ToString("mm:ss.fff")); Console.WriteLine("---"); Console.WriteLine(DateTime.Now.ToString("mm:ss.fff")); for (int i = 0; i < 100000000; i++) { num = int.MaxValue; } Console.WriteLine(DateTime.Now.ToString("mm:ss.fff")); Console.ReadLine(); } 结果发现还是直接赋值更快,在我本机上,判断执行一亿次速度是 226ms,赋值执行一亿次速度是 168ms。(大家也可以自己去 https://try.dot.net/试试)
最后从结果上来看,在整型的情况下,赋值是会比判断快的。
但是问题就来了,为什么呢? 我自己脑洞了一个答案,赋值到中间语言的时候就是 mov,比较到中间语言的时候是 cmp,mov 就直接把 01 丢到变量里去就可以了;但是 cmp 的话就要做减法,要做借位啊这那的操作,所以是赋值会更快。
强行解释了一波,感觉不是很信服。所以还请大家集思广益,破除疑惑~
1 whitev2 2019 年 5 月 23 日 只做判断,分支中没有任何操作,会不会直接把分支优化掉了? |
2 atwoodSoInterest OP @whitev2 如果是被优化了的话,应该是只做判断的更快才对。现在的现象是,直接赋值更快。 |
3 autoxbc 2019 年 5 月 23 日 赋值有副作用,就这一条就够了 |
4 marcong95 2019 年 5 月 23 日 因为你有个分支,那就自然多了一个跳转指令了? |
5 Counter 2019 年 5 月 23 日 底层的知识比较薄弱,抱歉不了解。。。 另外,mvvm 场景如果直接赋值的话,是不是就会有一些后续操作会被触发(比如重新渲染控件)? |
6 atwoodSoInterest OP |
7 jmc891205 2019 年 5 月 23 日 用 c 试了一遍 比较后赋值比赋值快一点 看 gcc 生成的汇编 前者比后者多一条条件转移指令 |
8 xenme 2019 年 5 月 23 日 这很好理解,比较多一个操作,肯定慢 发现异常的话,一定是有优化了,底层代码的实际逻辑并不是比较后再赋值 |
9 huluhulu 2019 年 5 月 23 日 多一次比较, 当然比较慢啊. 代码更多, 汇编指令也更多. 不明白楼主的疑惑在哪里... |
10 atwoodSoInterest OP |
11 atwoodSoInterest OP @huluhulu 后面的例子里,去掉了赋值,所以就没有“多一次”的操作了。就只是纯粹的“赋值”和“比较”的效率对比。示例代码里已经把判断分支里的赋值操作注释掉啦 |
12 momocraft 2019 年 5 月 23 日 每种 CPU 的各指令所需周期是有资料的 高级语言+多进程时因素就太多了,我们其实也不知道 CPU 上跑的是什么 |
13 jmc891205 2019 年 5 月 23 日 @atwoodSoInterest Agner Fog's instruction tables: https://www.agner.org/optimize/instruction_tables.pdf |
14 atwoodSoInterest OP @jmc891205 就近把上面示例代码翻译成了 js,发现还是赋值快。 var num = 0; console.time('判断操作耗时'); for (var i = 0; i < 100000000; i++) { if (i > 1000) { //没有赋值 } } console.timeEnd('判断操作耗时'); console.log('---'); console.time('赋值操作耗时'); for (var i = 0; i < 100000000; i++) { num = i; } console.timeEnd('赋值操作耗时'); VM1182:10 判断操作耗时: 208.4228515625ms VM1182:11 --- VM1182:17 赋值操作耗时: 168.830078125ms undefined |
15 jmc891205 2019 年 5 月 23 日 @atwoodSoInterest 我也不知道诶 可能有两个原因 一是我把两段写在两个文件里 分别编译后在命令行里用 time 统计的时间 二是我是在一台 CPU 是 Intel Xeon 系列的服务器上测试的 |
16 jmc891205 2019 年 5 月 23 日 |
17 hmzt 2019 年 5 月 23 日 @momocraft 是可以查到,不过依然不能解除疑惑,80386 的 mov 指令,从内存 mov 到寄存器要 4clock,反过来就只要 2 个 clock,为什么读取内存的用时是写入的两倍( https://pdos.csail.mit.edu/6.828/2007/readings/i386/MOV.htm) 不过就楼主的问题,理论上赋值更快 赋值 mov eax, a mov b, eax 比较 mov eax, a cmp b, eax |
18 yejinmo 2019 年 5 月 23 日 抛去哪个快的疑问,比较后再赋值应该更符合逻辑吧,比如存在 setter 的这种情况 |
19 atwoodSoInterest OP |
20 hmzt 2019 年 5 月 24 日 @atwoodSoInterest 因为 cmp 的两个操作数有一个来自内存,所以要 5clock,如果都在寄存器里,确实一样快 |
21 atwoodSoInterest OP @hmzt 这个超出我知识范畴了,是怎么分辨来自内存还是来自寄存器的啊?还请赐教~ |
22 c4f36e5766583218 2019 年 5 月 28 日 年轻的时候我也纠结过这个问题。 |
23 atwoodSoInterest OP @c4f36e5766583218 哈哈,所以纠结的结果呢 |