Warning: file_get_contents(/data/phpspider/zhask/data//catemap/0/performance/5.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ 在C++;要花点钱吗?_C++_Performance_Declaration - Fatal编程技术网

C++ 在C++;要花点钱吗?

C++ 在C++;要花点钱吗?,c++,performance,declaration,C++,Performance,Declaration,为了可读性,我认为下面的第一个代码块更好。但是第二个代码块更快吗 第一个街区: for (int i = 0; i < 5000; i++){ int number = rand() % 10000 + 1; string fizzBuzz = GetStringFromFizzBuzzLogic(number); } for(int i=0;i 强>如果程序将被认为太慢而强>如果代码的哪些部分会导致减速和 >如果< /强>指向这个循环的测量,那么他们会考虑改变它。 然而

为了可读性,我认为下面的第一个代码块更好。但是第二个代码块更快吗

第一个街区:

for (int i = 0; i < 5000; i++){
    int number = rand() % 10000 + 1;
    string fizzBuzz = GetStringFromFizzBuzzLogic(number);
}
for(int i=0;i<5000;i++){
整数=兰德()%10000+1;
字符串fizzBuzz=GetStringFromFizzBuzzLogic(数字);
}
第二个街区:

int number;
string fizzBuzz;
for (int i = 0; i < 5000; i++){
    number = rand() % 10000 + 1;
    fizzBuzz = GetStringFromFizzBuzzLogic(number);
}
整数;
弦乐嘶嘶作响;
对于(int i=0;i<5000;i++){
数字=兰德()%10000+1;
fizzBuzz=GetStringFromFizzBuzzLogic(编号);
}

<>在C++中重新声明变量是否有代价?

< p>任何现代编译器都会注意到这一点并进行优化工作。
如果有疑问,一定要确保可读性。尽可能在最内部的范围内声明变量。

第一个代码块应该被认为更快,因为一次调用
std::string
默认构造函数没有任何开销

实际上,在第二个代码块中没有变量的重新声明。这些只是简单的赋值操作

重新声明实际上意味着你有这样的东西

int number;
string fizzBuzz;
for (int i = 0; i < 5000; i++){
    int number = rand() % 10000 + 1;
 // ^^^
    string fizzBuzz = GetStringFromFizzBuzzLogic(number);
 // ^^^^^^
}
整数;
弦乐嘶嘶作响;
对于(int i=0;i<5000;i++){
整数=兰德()%10000+1;
// ^^^
字符串fizzBuzz=GetStringFromFizzBuzzLogic(数字);
// ^^^^^^
}
在这种情况下,编译器将优化开销,因为根本不使用外部作用域变量。

所有声明(值)变量都是通过该函数/方法中所有局部变量的组合大小来增加堆栈

调用构造函数/析构函数的代价可能超过对象类型(字符串)的最佳次数

在这种情况下没有区别。如果使用合适的编译器,优化器将为您提供最好的解决方案


您可能希望以最佳方式阅读代码,这样您的同事就不会认为您编写了糟糕的代码

> C++中没有再声明的东西。在第二个代码段中,
number
fizzBuzz
仅声明和初始化一次。后面的
=
作业

与所有优化问题一样,您只能猜测或最好测量。当然,这完全取决于你的编译器和你调用它的设置。当然,在速度优化和空间优化之间可以进行权衡

我知道没有一个严肃的C++程序员不喜欢第一种形式,因为它更容易阅读,而且更简洁。 <> > <>强>如果程序将被认为太慢而<>强>如果代码的哪些部分会导致减速和<强> >如果< /强>指向这个循环的测量,那么他们会考虑改变它。

然而,正如其他人所说,这是一种不现实的情况。在优化方面,现代编译器极不可能以不同的方式处理这两个代码段,也不可能遇到任何可测量的速度差异


(编辑:很抱歉输入错误,我把“第一”和“第二”混淆了)

我对这段代码进行了基准测试,即使没有优化,这两种变体的运行时间也几乎相同。一旦最低级别的优化被打开,结果就非常接近于相同的结果(时间测量中有一点噪音)

编辑:下面对生成的汇编程序代码的分析表明,很难猜测哪种形式更快,因为大多数人可能会给出的答案是
func2
,但事实证明该函数稍微慢一点,至少在使用clang++和-O2编译时是这样。这很好地证明了“写代码、基准测试、更改代码、基准测试”是处理性能的正确方法,而不是基于阅读代码的猜测。还记得有人告诉我的,优化有点像把洋葱分层——一旦你优化了一部分,你最终会看到非常相似的东西,只是稍微小一点而已……)

然而,我最初的分析使
func1
的速度明显变慢,这是因为编译器出于某种原因,没有在
func1
中优化
rand()%10000+1
,而是在
func2
中优化。这意味着
func1
。但是,一旦启用优化,两个函数都会得到一个“快速”模

使用linux性能工具
perf
表明,使用clang++和-O2,我们可以为func1获得以下结果

  15.76%  a.out    libc-2.20.so         free
  12.31%  a.out    libstdc++.so.6.0.20  std::string::_S_construct<char cons
  12.29%  a.out    libc-2.20.so         _int_malloc
  10.05%  a.out    a.out                func1
   7.26%  a.out    libc-2.20.so         __random
   6.36%  a.out    libc-2.20.so         malloc
   5.46%  a.out    libc-2.20.so         __random_r
   5.01%  a.out    libstdc++.so.6.0.20  std::basic_string<char, std::char_t
   4.83%  a.out    libstdc++.so.6.0.20  std::string::_Rep::_S_create
   4.01%  a.out    libc-2.20.so         strlen
使用func2运行:

./a.out
time=905721532
time=895393507
time=886537634
time=879836476
time=883887384
这是在N中再加上一个0——因此运行时间延长了10倍——看起来它一直都有点慢,但它只有几个百分点,而且可能在噪音范围内,真的——在时间上,整个基准测试大约需要1.30-1.39秒

编辑:查看实际循环的汇编代码[这只是循环的一部分,但其余部分在代码的实际作用方面是相同的]

职能1:

.LBB0_1:                                # %for.body
    callq   rand
    movslq  %eax, %rcx
    imulq   $1759218605, %rcx, %rcx # imm = 0x68DB8BAD
    movq    %rcx, %rdx
    shrq    $63, %rdx
    sarq    $44, %rcx
    addl    %edx, %ecx
    imull   $10000, %ecx, %ecx      # imm = 0x2710
    negl    %ecx
    leal    1(%rax,%rcx), %esi
    movq    %r15, %rdi
    callq   _Z26GetStringFromFizzBuzzLogici
    movq    (%rsp), %rax
    leaq    -24(%rax), %rdi
    cmpq    %rbx, %rdi
    jne .LBB0_2
.LBB0_7:                                # %_ZNSsD2Ev.exit
    decl    %ebp
    jne .LBB0_1
职能2:

.LBB1_1:
    callq   rand
    movslq  %eax, %rcx
    imulq   $1759218605, %rcx, %rcx # imm = 0x68DB8BAD
    movq    %rcx, %rdx
    shrq    $63, %rdx
    sarq    $44, %rcx
    addl    %edx, %ecx
    imull   $10000, %ecx, %ecx      # imm = 0x2710
    negl    %ecx
    leal    1(%rax,%rcx), %esi
    movq    %rbx, %rdi
    callq   _Z26GetStringFromFizzBuzzLogici
    movq    %r14, %rdi
    movq    %rbx, %rsi
    callq   _ZNSs4swapERSs
    movq    (%rsp), %rax
    leaq    -24(%rax), %rdi
    cmpq    %r12, %rdi
    jne .LBB1_4
.LBB1_9:                                # %_ZNSsD2Ev.exit19
    incl    %ebp
    cmpl    $5000000, %ebp          # imm = 0x4C4B40
因此,可以看出,
func2
版本包含一个额外的函数调用:

    callq   _ZNSs4swapERSs
转换为
std::basic_string::swap(std::basic_string&)
std::string::swap(std::string&)
——这可能是调用
std::string::operator=(std::string&s)
的结果。这可以解释为什么
func2
func1
稍慢

我确信在循环中构建/销毁一个对象需要花费大量时间的情况是可能的,但一般来说,这不会有什么区别,或者根本不会有什么区别,而更清晰的代码实际上会帮助读者。它还经常帮助编译器进行“生命周期分析”,因为要“遍历”变量以确定变量是否在以后使用,所需的代码更少(在本例中,代码很短,但在实际示例中显然并不总是如此)

“确实如此
./a.out
time=905721532
time=895393507
time=886537634
time=879836476
time=883887384
.LBB0_1:                                # %for.body
    callq   rand
    movslq  %eax, %rcx
    imulq   $1759218605, %rcx, %rcx # imm = 0x68DB8BAD
    movq    %rcx, %rdx
    shrq    $63, %rdx
    sarq    $44, %rcx
    addl    %edx, %ecx
    imull   $10000, %ecx, %ecx      # imm = 0x2710
    negl    %ecx
    leal    1(%rax,%rcx), %esi
    movq    %r15, %rdi
    callq   _Z26GetStringFromFizzBuzzLogici
    movq    (%rsp), %rax
    leaq    -24(%rax), %rdi
    cmpq    %rbx, %rdi
    jne .LBB0_2
.LBB0_7:                                # %_ZNSsD2Ev.exit
    decl    %ebp
    jne .LBB0_1
.LBB1_1:
    callq   rand
    movslq  %eax, %rcx
    imulq   $1759218605, %rcx, %rcx # imm = 0x68DB8BAD
    movq    %rcx, %rdx
    shrq    $63, %rdx
    sarq    $44, %rcx
    addl    %edx, %ecx
    imull   $10000, %ecx, %ecx      # imm = 0x2710
    negl    %ecx
    leal    1(%rax,%rcx), %esi
    movq    %rbx, %rdi
    callq   _Z26GetStringFromFizzBuzzLogici
    movq    %r14, %rdi
    movq    %rbx, %rsi
    callq   _ZNSs4swapERSs
    movq    (%rsp), %rax
    leaq    -24(%rax), %rdi
    cmpq    %r12, %rdi
    jne .LBB1_4
.LBB1_9:                                # %_ZNSsD2Ev.exit19
    incl    %ebp
    cmpl    $5000000, %ebp          # imm = 0x4C4B40
    callq   _ZNSs4swapERSs