代码之家  ›  专栏  ›  技术社区  ›  Jayesh

哪些代码在C中执行得更快?

  •  1
  • Jayesh  · 技术社区  · 8 年前

    我看到了以下代码(上的问题3 PUZZLERSWORLD.COM 现场):

    for (i = 0; i < 1000; i++)
        for (j = 0; j < 100; j++)
            x = y;
    

    for (i = 0; i < 100; i++)
        for (j = 0; j < 1000; j++)
            x = y;
    

    哪些代码执行得更快?

    选项:

    a) Code 1 and Code 2 are of same speed,
    b) Code 1,
    c) Code 2,
    d) Can't Say
    

    答案:

    c)
    

    为什么第二个代码比第一个快?

    7 回复  |  直到 8 年前
        1
  •  13
  •   Grzegorz Szpetkowski    8 年前

    x y volatile

    x = y;
    

    例子:

    int f(int y)
    {
        int x;
    
        for (int i = 0; i < 1000; i++)
            for (int j = 0; j < 100; j++)
                x = y;
        return x;
    }
    

    产量:

    f(int):
            mov     eax, edi
            ret
    
        2
  •  6
  •   CodeMonkey    8 年前

    答复 d)

    我会停止使用那个网站作为学习资源,去找另一个。

        3
  •  6
  •   user2371524 user2371524    8 年前

    假定 i j j 太频繁了。这些假设 保留一些硬件,但它不太可能产生可测量的差异。

    正确答案是:“ a) ,使用任何合适的编译器 “,因为由一个好的编译器创建的代码只执行一个 x = y;


    至于“如何检查”,您有两种可能:

    • gcc -S
    • 经常
        4
  •  1
  •   Fabel    8 年前

    x = y; f(i,j); )它可能会决定在代码1中展开内部循环,而不是在代码2中展开,从而使代码1更快(可能至少是速度的两倍,但这当然取决于ISA和ABI)。

        5
  •  1
  •   0___________    8 年前

    根据变量的类型,as编译器将以不同的方式优化代码。

    答案是:如果启用任何优化,则代码对foo的执行完全相同,如果不稳定,则随着寄存器加载次数的增加而存在差异

    int x, y;
    volatile int m,n;
    
    
    void foo(void)
    {
    for(int i=0; i<1000; i++)
       for(int j=0; j<100; j++)
          x = y;
    
    
    for(int i=0; i<100; i++)
       for(int j=0; j<1000; j++)
          x = y;
    }
    
    void foo1(void)
    {
    for(int i=0; i<1000; i++)
       for(int j=0; j<100; j++)
          m = n;
    
    
    for(int i=0; i<100; i++)
       for(int j=0; j<1000; j++)
          m = n;
    }
    

    生成的代码ARM:(这里是x86 verison: https://godbolt.org/g/tSRKxy

    foo():
            ldr     r3, .L2
            ldr     r2, [r3, #4]
            str     r2, [r3]
            bx      lr
    .L2:
            .word   .LANCHOR0
    foo1():
            mov     r0, #1000
            ldr     r3, .L14
    .L6:
            mov     r2, #100
    .L5:
            ldr     r1, [r3, #8]
            subs    r2, r2, #1
            str     r1, [r3, #12]
            bne     .L5
            subs    r0, r0, #1
            bne     .L6
            mov     r0, #100
    .L8:
            mov     r2, #1000
    .L7:
            ldr     r1, [r3, #8]
            subs    r2, r2, #1
            str     r1, [r3, #12]
            bne     .L7
            subs    r0, r0, #1
            bne     .L8
            bx      lr
    .L14:
            .word   .LANCHOR0
    x:
    y:
    n:
    m:
    
        6
  •  1
  •   stefan bachert    8 年前

    循环有一些开销

    代码1中内部循环的开销调用了1000次,而代码2中调用了100次。

    因此代码2更快或相同

    即使编译器优化得很好并删除了循环,代码2也永远不会变慢

    我选择选项e)

    证明

    • 将编译器用于这两种代码
    • 只使用理性优化,不使用随机或奇怪的优化

    编译器根本没有进行优化,因为应用了内部循环的开销,代码2更快。符合选项D)或E)

    案例b)

    两个代码的结果相同。选项A)或E)

    b2) 将两个循环合并为一个

    b3) 组合对循环进行重新排序(这在原始代码中是一个愚蠢的子优化)

    两个代码的结果相同。选项A)或E)


    Fabel“反例”无效,因为没有理由在代码1中应用优化,但在代码2中不应用优化

        7
  •  1
  •   chqrlie    8 年前

    该问题缺少任何记录答案的重要信息。

    例如,如果我们有以下程序:

    int main(void) {
        int i, j, x, y;
        for (i = 0; i < 1000; i++)
            for (j = 0; j < 100; j++)
                x = y;
    }
    

    int main(void) {
        int i, j, x, y;
        for (i = 0; i < 100; i++)
            for (j = 0; j < 1000; j++)
                x = y;
    }
    

    y .

    如果我们有:

    int main(void) {
        int i;
        unsigned char j;
        int x, y = 0;
        for (i = 0; i < 1000; i++)
            for (j = 0; j < 100; j++)
                x = y;
    }
    

    int main(void){
    无符号字符j;
    int x,y=0;
    对于(i=0;i<1000;i++)
    对于(j=0;j<100;j++)
    x=y;
    }
    

    有了像样的编译器和正确的定义,代码几乎完全优化了,所以请回答 a)

    int main(void) {
        int i, j, x, y = 0;
        for (i = 0; i < 100; i++)
            for (j = 0; j < 1000; j++)
                x = y;
    }
    

    编译时使用 Godbolt's compiler explorer ,产生:

    main:
            xorl    %eax, %eax
            ret
    

    c)

    在绩效分析中,谦逊是一种基本美德。使用分析工具并仔细编写完整的基准测试代码,以确定优化是否有用。编写复杂代码以试图优化并获取产生错误结果的代码是一个常见错误。


    请注意,链接页面中充满了断开的问题。例如Q2:

    #include
    fun(int i)
    {
        int j=0;
        while (i & i-1) {
            j++;   
        }
        printf("%d",j);
    }
    
    main(){
        f(100);
    }
    

    选项: a. 100 b. 99 c. 2 d. 3

    代码不编译,如果被纠正,它有一个无限循环,因此没有一个答案是正确的。出于好奇,我们提供了一个正确的函数来计算 i

    void fun(unsigned i) {
        int j = 0;
        while (i) {
            i = i & (i - 1);
            j++;   
        }
        printf("%d\n", j);
    }
    

    在10个问题中,有7个答案不正确,其余3个(Q1、Q4和Q5)是有语法错误的技巧性问题。质量很差的Q&确实是一个网站。