V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
atwoodSoInterest
V2EX  ›  编程

赋值和比较哪个更快?

  •  1
     
  •   atwoodSoInterest · 2019-05-23 14:13:56 +08:00 · 6329 次点击
    这是一个创建于 2019 天前的主题,其中的信息可能已经有所发展或是发生改变。

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

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

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

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

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

    糟糕 是我看错结果了。。。
    我也是赋值比较快
    sorry
    hmzt
        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
    yejinmo
        18
    yejinmo  
       2019-05-23 18:25:30 +08:00
    抛去哪个快的疑问,比较后再赋值应该更符合逻辑吧,比如存在 setter 的这种情况
    atwoodSoInterest
        19
    atwoodSoInterest  
    OP
       2019-05-23 19:09:56 +08:00
    @jmc891205 哈哈,没关系,难免失手啊。给的资料很棒啊,不过还是没有找到 mov 比 cmp 快的依据。
    @hmzt 看了给的资料,发现资料里写的 cmp 和 mov 都是只用 2clock 啊,为什么一个会更快啊,是我漏了什么细节吗?
    @yejinmo 这只是个比较 geek 的想法,忽然想求索一下。真正 coding 的时候,可读性的优先级是远高于性能的。
    hmzt
        20
    hmzt  
       2019-05-24 09:24:20 +08:00
    @atwoodSoInterest 因为 cmp 的两个操作数有一个来自内存,所以要 5clock,如果都在寄存器里,确实一样快
    atwoodSoInterest
        21
    atwoodSoInterest  
    OP
       2019-05-24 18:08:31 +08:00
    @hmzt 这个超出我知识范畴了,是怎么分辨来自内存还是来自寄存器的啊?还请赐教~
    c4f36e5766583218
        22
    c4f36e5766583218  
       2019-05-28 12:36:46 +08:00
    年轻的时候我也纠结过这个问题。
    atwoodSoInterest
        23
    atwoodSoInterest  
    OP
       2019-05-28 17:56:08 +08:00
    @c4f36e5766583218 哈哈,所以纠结的结果呢
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   1014 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 25ms · UTC 18:57 · PVG 02:57 · LAX 10:57 · JFK 13:57
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.