为什么启用优化时sin / cos会变慢?

时间:2011-08-08 04:00:54

标签: c++ performance

在阅读了与sin / cos(Why is std::sin() and std::cos() slower than sin() and cos()?)的性能相关的问题之后,我用他的代码进行了一些测试并发现了一个奇怪的事情:如果我用浮点值调用sin / cos,那就太多了使用优化编译时比使用double慢。

#include <cmath>
#include <cstdio>

const int N = 4000;

float cosine[N][N];
float sine[N][N];

int main() {
    for (int i = 0; i < N; i++) {
        for (int j = 0; j < N; j++) {
            float ang = i*j*2*M_PI/N;
            cosine[i][j] = cos(ang);
            sine[i][j] = sin(ang);
        }
    }
}

使用上面的代码我得到:

使用-O0:2.402s

使用-O1:9.004s

使用-O2:9.013s

使用-O3:9.001s

现在如果我改变

float ang = i*j*2*M_PI/N;

double ang = i*j*2*M_PI/N;

我明白了:

使用-O0:2.362s

使用-O1:1.188s

使用-O2:1.197s

使用-O3:1.197s

如果没有优化,第一次测试怎么能更快?

我正在使用g ++(Ubuntu / Linaro 4.5.2-8ubuntu4)4.5.2,64位。

编辑:更改了标题以更好地描述问题。

编辑:添加了汇编代码

用O0组装第一次测试:

    .file   "main.cpp"
.globl cosine
    .bss
    .align 32
    .type   cosine, @object
    .size   cosine, 64000000
cosine:
    .zero   64000000
.globl sine
    .align 32
    .type   sine, @object
    .size   sine, 64000000
sine:
    .zero   64000000
    .text
.globl main
    .type   main, @function
main:
.LFB87:
    .cfi_startproc
    pushq   %rbp
    .cfi_def_cfa_offset 16
    movq    %rsp, %rbp
    .cfi_offset 6, -16
    .cfi_def_cfa_register 6
    subq    $16, %rsp
    movl    $0, -4(%rbp)
    jmp .L2
.L5:
    movl    $0, -8(%rbp)
    jmp .L3
.L4:
    movl    -4(%rbp), %eax
    imull   -8(%rbp), %eax
    addl    %eax, %eax
    cvtsi2sd    %eax, %xmm0
    movsd   .LC0(%rip), %xmm1
    mulsd   %xmm1, %xmm0
    movsd   .LC1(%rip), %xmm1
    divsd   %xmm1, %xmm0
    unpcklpd    %xmm0, %xmm0
    cvtpd2ps    %xmm0, %xmm0
    movss   %xmm0, -12(%rbp)
    movss   -12(%rbp), %xmm0
    cvtps2pd    %xmm0, %xmm0
    call    cos
    unpcklpd    %xmm0, %xmm0
    cvtpd2ps    %xmm0, %xmm0
    movl    -8(%rbp), %eax
    cltq
    movl    -4(%rbp), %edx
    movslq  %edx, %rdx
    imulq   $4000, %rdx, %rdx
    leaq    (%rdx,%rax), %rax
    movss   %xmm0, cosine(,%rax,4)
    movss   -12(%rbp), %xmm0
    cvtps2pd    %xmm0, %xmm0
    call    sin
    unpcklpd    %xmm0, %xmm0
    cvtpd2ps    %xmm0, %xmm0
    movl    -8(%rbp), %eax
    cltq
    movl    -4(%rbp), %edx
    movslq  %edx, %rdx
    imulq   $4000, %rdx, %rdx
    leaq    (%rdx,%rax), %rax
    movss   %xmm0, sine(,%rax,4)
    addl    $1, -8(%rbp)
.L3:
    cmpl    $3999, -8(%rbp)
    setle   %al
    testb   %al, %al
    jne .L4
    addl    $1, -4(%rbp)
.L2:
    cmpl    $3999, -4(%rbp)
    setle   %al
    testb   %al, %al
    jne .L5
    movl    $0, %eax
    leave
    .cfi_def_cfa 7, 8
    ret
    .cfi_endproc
.LFE87:
    .size   main, .-main
    .section    .rodata
    .align 4
    .type   _ZL1N, @object
    .size   _ZL1N, 4
_ZL1N:
    .long   4000
    .align 8
.LC0:
    .long   1413754136
    .long   1074340347
    .align 8
.LC1:
    .long   0
    .long   1085227008
    .ident  "GCC: (Ubuntu/Linaro 4.5.2-8ubuntu4) 4.5.2"
    .section    .note.GNU-stack,"",@progbits

使用O3进行首次测试的组装:

    .file   "main.cpp"
    .text
    .p2align 4,,15
.globl main
    .type   main, @function
main:
.LFB121:
    .cfi_startproc
    pushq   %r15
    .cfi_def_cfa_offset 16
    xorl    %r15d, %r15d
    .cfi_offset 15, -16
    pushq   %r14
    .cfi_def_cfa_offset 24
    movl    $cosine+16000, %r14d
    .cfi_offset 14, -24
    pushq   %r13
    .cfi_def_cfa_offset 32
    xorl    %r13d, %r13d
    .cfi_offset 13, -32
    pushq   %r12
    .cfi_def_cfa_offset 40
    pushq   %rbp
    .cfi_def_cfa_offset 48
    pushq   %rbx
    .cfi_def_cfa_offset 56
    subq    $24, %rsp
    .cfi_def_cfa_offset 80
    .p2align 4,,10
    .p2align 3
.L2:
    movslq  %r15d, %rbp
    .cfi_offset 3, -56
    .cfi_offset 6, -48
    .cfi_offset 12, -40
    movl    %r13d, %r12d
    movl    $0x3f800000, %edx
    imulq   $16000, %rbp, %rbp
    xorl    %eax, %eax
    leaq    cosine(%rbp), %rbx
    addq    $sine, %rbp
    jmp .L5
    .p2align 4,,10
    .p2align 3
.L3:
    movl    %r12d, %eax
    leaq    8(%rsp), %rsi
    leaq    12(%rsp), %rdi
    subl    %r13d, %eax
    cvtsi2sd    %eax, %xmm0
    mulsd   .LC2(%rip), %xmm0
    divsd   .LC3(%rip), %xmm0
    unpcklpd    %xmm0, %xmm0
    cvtpd2ps    %xmm0, %xmm0
    call    sincosf
    movl    8(%rsp), %edx
    movl    12(%rsp), %eax
.L5:
    movl    %edx, (%rbx)
    addq    $4, %rbx
    movl    %eax, 0(%rbp)
    addl    %r13d, %r12d
    addq    $4, %rbp
    cmpq    %r14, %rbx
    jne .L3
    addl    $1, %r15d
    addl    $2, %r13d
    leaq    16000(%rbx), %r14
    cmpl    $4000, %r15d
    jne .L2
    addq    $24, %rsp
    .cfi_def_cfa_offset 56
    xorl    %eax, %eax
    popq    %rbx
    .cfi_def_cfa_offset 48
    popq    %rbp
    .cfi_def_cfa_offset 40
    popq    %r12
    .cfi_def_cfa_offset 32
    popq    %r13
    .cfi_def_cfa_offset 24
    popq    %r14
    .cfi_def_cfa_offset 16
    popq    %r15
    .cfi_def_cfa_offset 8
    ret
    .cfi_endproc
.LFE121:
    .size   main, .-main
.globl cosine
    .bss
    .align 32
    .type   cosine, @object
    .size   cosine, 64000000
cosine:
    .zero   64000000
.globl sine
    .align 32
    .type   sine, @object
    .size   sine, 64000000
sine:
    .zero   64000000
    .section    .rodata.cst8,"aM",@progbits,8
    .align 8
.LC2:
    .long   1413754136
    .long   1074340347
    .align 8
.LC3:
    .long   0
    .long   1085227008
    .ident  "GCC: (Ubuntu/Linaro 4.5.2-8ubuntu4) 4.5.2"
    .section    .note.GNU-stack,"",@progbits

2 个答案:

答案 0 :(得分:5)

这是一种可能性:

在C中,cos是双精度,cosf是单精度。在C ++中,std::cos对double和single都有重载。

您没有致电std::cos。如果<cmath>也没有重载::cos(据我所知,它不是必需的),那么你只是调用C双精度函数。如果是这种情况,那么您将承担在浮动,双重和返回之间转换的成本。

现在,一些标准库将cos(float x)实现为(float)cos((double)x),因此即使您正在调用float函数,它仍可能在幕后进行转换。

但这并不能解释9倍的性能差异。

答案 1 :(得分:2)

AFAIK它是因为计算机本身以双精度工作。使用float需要转换。&#39;