首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >为什么这个+=循环比等效的=循环快?

为什么这个+=循环比等效的=循环快?
EN

Stack Overflow用户
提问于 2022-07-03 22:15:03
回答 1查看 122关注 0票数 3

我用C++编写了这个简单的循环(Visual 2019在发布模式下),它经历了100万个32位整数并分配给前一个:

代码语言:javascript
复制
    for ( int i = 1; i < Iters; i++ )
        ints32[i - 1] = ints32[i];

这里有一个循环,除了使用+=之外,这个循环是相同的

代码语言:javascript
复制
    for ( int i = 1; i < Iters; i++ )
        ints32[i - 1] += ints32[i];

我正在测量这两个功能,运行了20次,放弃了5次最慢和5次最快的运行,平均其余的。(在每次测量之前,我都会用随机整数填充数组。)我发现只有赋值的循环大约需要460微秒,但是加法的循环需要320微秒。这些度量在多个运行中是一致的(它们略有不同,但添加速度总是更快),即使在更改测量这两个函数的顺序时也是如此。

为什么带加法的循环比没有加法的循环要快?如果有什么变化的话,我认为这会让它花费更长的时间。

下面是反汇编,您可以看到这些函数是等价的,只不过加法循环做了更多的工作(并将eax设置为ints[i - 1]而不是ints[i])。

代码语言:javascript
复制
    for ( int i = 1; i < Iters; i++ )
00541670 B8 1C 87 54 00       mov         eax,54871Ch  
        ints32[i - 1] = ints32[i];
00541675 8B 08                mov         ecx,dword ptr [eax]  
00541677 89 48 FC             mov         dword ptr [eax-4],ecx  
0054167A 83 C0 04             add         eax,4  
0054167D 3D 18 90 91 00       cmp         eax,offset floats32 (0919018h)  
00541682 7C F1                jl          IntAssignment32+5h (0541675h)  
}
00541684 C3                   ret  

    for ( int i = 1; i < Iters; i++ )
00541700 B8 18 87 54 00       mov         eax,offset ints32 (0548718h)  
        ints32[i - 1] += ints32[i];
00541705 8B 08                mov         ecx,dword ptr [eax]  
00541707 03 48 04             add         ecx,dword ptr [eax+4]  
0054170A 89 08                mov         dword ptr [eax],ecx  
0054170C 83 C0 04             add         eax,4  
0054170F 3D 14 90 91 00       cmp         eax,919014h  
00541714 7C EF                jl          IntAddition32+5h (0541705h)  
}
00541716 C3                   ret  

( int数组是volatile,因为我不希望编译器对其进行优化或矢量化,而且它产生的反汇编确实是我想要测量的。)

编辑:我注意到,当我改变程序看似无关的东西时,分配版本变得更快,然后又变慢了。我怀疑这可能与函数代码的对齐有关,也许吧?

我正在使用默认的VisualStudio2019 Win32发行版编译器选项(从项目属性复制此选项):

代码语言:javascript
复制
/permissive- /ifcOutput "Release\" /GS /GL /analyze- /W3 /Gy /Zc:wchar_t /Zi /Gm- /O2 /sdl /Fd"Release\vc142.pdb" /Zc:inline /fp:precise /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_UNICODE" /D "UNICODE" /errorReport:prompt /WX- /Zc:forScope /Gd /Oy- /Oi /MD /FC /Fa"Release\" /EHsc /nologo /Fo"Release\" /Fp"Release\profile.pch" /diagnostics:column 

编译器版本: 2019版本16.11.15

以下是完整的代码:

代码语言:javascript
复制
#include <iostream>
#include <chrono>
#include <random>
#include <algorithm>

const int ValueRange = 100000000;
std::default_random_engine generator;
std::uniform_int_distribution< int > distribution( 1, ValueRange - 1 );

const int Iters = 1000000; // nanoseconds -> milliseconds

volatile int ints32[Iters];

void InitArrayInt32()
{
    for ( int i = 0; i < Iters; i++ )
        ints32[i] = distribution( generator );
}

const int SampleCount = 20;
const int KeepSampleCount = SampleCount - 2 * (SampleCount / 4);

float ProfileFunction( void(*setup)(), void(*func)() )
{
    uint64_t times[SampleCount];

    for ( int i = 0; i < SampleCount; i++ )
    {
        setup();

        auto startTime = std::chrono::steady_clock::now();

        func();

        auto endTime = std::chrono::steady_clock::now();
        times[i] = std::chrono::duration_cast<std::chrono::microseconds>( endTime - startTime ).count();
    }

    std::sort( times, times + SampleCount );
    uint64_t total = 0;
    for ( int i = SampleCount / 4; i < SampleCount - SampleCount / 4; i++ )
        total += times[i];
    return total * (1.0f / KeepSampleCount);
}

void IntAssignment32()
{
    for ( int i = 1; i < Iters; i++ )
        ints32[i - 1] = ints32[i];
}
void IntAddition32()
{
    for ( int i = 1; i < Iters; i++ )
        ints32[i - 1] += ints32[i];
}

int main()
{
    float assignment = ProfileFunction( InitArrayInt32, IntAssignment32 );
    float addition = ProfileFunction( InitArrayInt32, IntAddition32 );
    printf( "assignment: %g\n", assignment );
    printf( "addition: %g\n", addition );
    return 0;
}
EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2022-07-03 23:47:06

编辑:我注意到当我改变程序中看似无关的东西时,分配版本变得更快,然后又变慢了。我怀疑这可能与函数代码的对齐有关,也许吧?

其中一个比另一个更快的原因是运气。由代码和数据的对齐引起的速度差异比生成的代码中的细微差异有更大的影响。

即使您测量了多次,您测量的总是相同的对齐(在某种程度上,如果您有地址空间随机化)。这消除了来自调度程序和中断的噪声,但不消除来自对齐更改的噪声。

但是,当您更改代码中的某些内容时,您将更改对齐方式,例如,将数组移动4字节。将代码移动1字节。这对速度的影响实际上比gcc的-O0和-O2之间的差异更大。关于这种效果的论文很多,还有更多的。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/72850251

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档