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-05-23 15:29:06 +08:00
只做判断,分支中没有任何操作,会不会直接把分支优化掉了?
|
2
atwoodSoInterest OP @whitev2 如果是被优化了的话,应该是只做判断的更快才对。现在的现象是,直接赋值更快。
|
3
autoxbc 2019-05-23 15:43:51 +08:00
赋值有副作用,就这一条就够了
|
4
marcong95 2019-05-23 15:46:04 +08:00
因为你有个分支,那就自然多了一个跳转指令了?
|
5
Counter 2019-05-23 15:53:02 +08:00
底层的知识比较薄弱,抱歉不了解。。。
另外,mvvm 场景如果直接赋值的话,是不是就会有一些后续操作会被触发(比如重新渲染控件)? |
6
atwoodSoInterest OP |
7
jmc891205 2019-05-23 16:34:56 +08:00
用 c 试了一遍 比较后赋值比赋值快一点
看 gcc 生成的汇编 前者比后者多一条条件转移指令 |
8
xenme 2019-05-23 16:36:48 +08:00
这很好理解,比较多一个操作,肯定慢
发现异常的话,一定是有优化了,底层代码的实际逻辑并不是比较后再赋值 |
9
huluhulu 2019-05-23 17:01:30 +08:00
多一次比较, 当然比较慢啊. 代码更多, 汇编指令也更多.
不明白楼主的疑惑在哪里... |
10
atwoodSoInterest OP |
11
atwoodSoInterest OP @huluhulu 后面的例子里,去掉了赋值,所以就没有“多一次”的操作了。就只是纯粹的“赋值”和“比较”的效率对比。示例代码里已经把判断分支里的赋值操作注释掉啦
|
12
momocraft 2019-05-23 17:07:47 +08:00
每种 CPU 的各指令所需周期是有资料的
高级语言+多进程时因素就太多了,我们其实也不知道 CPU 上跑的是什么 |
13
jmc891205 2019-05-23 17:13:22 +08:00
@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-05-23 17:27:39 +08:00
@atwoodSoInterest
我也不知道诶 可能有两个原因 一是我把两段写在两个文件里 分别编译后在命令行里用 time 统计的时间 二是我是在一台 CPU 是 Intel Xeon 系列的服务器上测试的 |
16
jmc891205 2019-05-23 17:37:07 +08:00
|
17
hmzt 2019-05-23 17:41:27 +08:00
@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-05-23 18:25:30 +08:00
抛去哪个快的疑问,比较后再赋值应该更符合逻辑吧,比如存在 setter 的这种情况
|
19
atwoodSoInterest OP |
20
hmzt 2019-05-24 09:24:20 +08:00
@atwoodSoInterest 因为 cmp 的两个操作数有一个来自内存,所以要 5clock,如果都在寄存器里,确实一样快
|
21
atwoodSoInterest OP @hmzt 这个超出我知识范畴了,是怎么分辨来自内存还是来自寄存器的啊?还请赐教~
|
22
c4f36e5766583218 2019-05-28 12:36:46 +08:00
年轻的时候我也纠结过这个问题。
|
23
atwoodSoInterest OP @c4f36e5766583218 哈哈,所以纠结的结果呢
|