赋值和比较哪个更快?

2019-05-23 14:13:56 +08:00
 atwoodSoInterest

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 的话就要做减法,要做借位啊这那的操作,所以是赋值会更快。

强行解释了一波,感觉不是很信服。所以还请大家集思广益,破除疑惑~

6180 次点击
所在节点    编程
23 条回复
whitev2
2019-05-23 15:29:06 +08:00
只做判断,分支中没有任何操作,会不会直接把分支优化掉了?
atwoodSoInterest
2019-05-23 15:34:35 +08:00
@whitev2 如果是被优化了的话,应该是只做判断的更快才对。现在的现象是,直接赋值更快。
autoxbc
2019-05-23 15:43:51 +08:00
赋值有副作用,就这一条就够了
marcong95
2019-05-23 15:46:04 +08:00
因为你有个分支,那就自然多了一个跳转指令了?
Counter
2019-05-23 15:53:02 +08:00
底层的知识比较薄弱,抱歉不了解。。。
另外,mvvm 场景如果直接赋值的话,是不是就会有一些后续操作会被触发(比如重新渲染控件)?
atwoodSoInterest
2019-05-23 16:07:03 +08:00
@autoxbc 什么副作用呢?不是太明白哎。
@marcong95 if 分支就是用来测试“比较”操作耗时的,多了一个跳转指令是什么意思啊?
@Counter mvvm 还是要看具体框架的实现机制了吧
jmc891205
2019-05-23 16:34:56 +08:00
用 c 试了一遍 比较后赋值比赋值快一点
看 gcc 生成的汇编 前者比后者多一条条件转移指令
xenme
2019-05-23 16:36:48 +08:00
这很好理解,比较多一个操作,肯定慢

发现异常的话,一定是有优化了,底层代码的实际逻辑并不是比较后再赋值
huluhulu
2019-05-23 17:01:30 +08:00
多一次比较, 当然比较慢啊. 代码更多, 汇编指令也更多.
不明白楼主的疑惑在哪里...
atwoodSoInterest
2019-05-23 17:02:58 +08:00
@jmc891205 哦~没想到换了语言,结果就不一样了,这真是出乎意料啊。我等下换其他语言试试。我后来的例子里把 “比较后赋值” 换成了 “只比较” ,因为比较后赋值不好比较两个操作的效率。话说,老哥你知道在汇编里,cmp 指令和 mov 指令哪个更快吗?我找了下没找到有相关资料的。
@xenme 开始的那个例子不太好,就是会有这种迷惑的信息。后来的例子熟练使用了高中学的控制变量法,去掉了赋值这个干扰信息,所以就只是 “比较” 和 “赋值” 这两个操作的速度比较。
atwoodSoInterest
2019-05-23 17:05:04 +08:00
@huluhulu 后面的例子里,去掉了赋值,所以就没有“多一次”的操作了。就只是纯粹的“赋值”和“比较”的效率对比。示例代码里已经把判断分支里的赋值操作注释掉啦
momocraft
2019-05-23 17:07:47 +08:00
每种 CPU 的各指令所需周期是有资料的

高级语言+多进程时因素就太多了,我们其实也不知道 CPU 上跑的是什么
jmc891205
2019-05-23 17:13:22 +08:00
atwoodSoInterest
2019-05-23 17:16:13 +08:00
@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
jmc891205
2019-05-23 17:27:39 +08:00
@atwoodSoInterest
我也不知道诶 可能有两个原因
一是我把两段写在两个文件里 分别编译后在命令行里用 time 统计的时间
二是我是在一台 CPU 是 Intel Xeon 系列的服务器上测试的
jmc891205
2019-05-23 17:37:07 +08:00
@atwoodSoInterest

糟糕 是我看错结果了。。。
我也是赋值比较快
sorry
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
yejinmo
2019-05-23 18:25:30 +08:00
抛去哪个快的疑问,比较后再赋值应该更符合逻辑吧,比如存在 setter 的这种情况
atwoodSoInterest
2019-05-23 19:09:56 +08:00
@jmc891205 哈哈,没关系,难免失手啊。给的资料很棒啊,不过还是没有找到 mov 比 cmp 快的依据。
@hmzt 看了给的资料,发现资料里写的 cmp 和 mov 都是只用 2clock 啊,为什么一个会更快啊,是我漏了什么细节吗?
@yejinmo 这只是个比较 geek 的想法,忽然想求索一下。真正 coding 的时候,可读性的优先级是远高于性能的。
hmzt
2019-05-24 09:24:20 +08:00
@atwoodSoInterest 因为 cmp 的两个操作数有一个来自内存,所以要 5clock,如果都在寄存器里,确实一样快

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/566943

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX