代码在发布中比在调试中运行慢

问题描述 投票:0回答:1

我正在尝试测试缓存行一致性规则对性能的影响。测试代码如下:

constexpr uint64_t loop = 1000000000;

struct no_padding_struct {
    no_padding_struct() :x(0), y(0) {}
    uint64_t x;
    uint64_t y;
};

struct padding_struct {
    padding_struct() :x(0), y(0) {}
    uint64_t x;
    char padding[64];
    uint64_t y;
};

alignas(64) volatile no_padding_struct n;
alignas(64) volatile padding_struct p;

constexpr core_a = 0;
constexpr core_b = 1;

void func(volatile uint64_t* addr, uint64_t b, uint64_t mask) {
    SetThreadAffinityMask(GetCurrentThread(), mask);
    for (uint64_t i = 0; i < loop; ++i) {
        *addr += b;
    }
}

void test1(uint64_t a, uint64_t b) {
    thread t1{ func, &n.x, a, 1<<core_a };
    thread t2{ func, &n.y, b, 1<<core_b };

    t1.join();
    t2.join();
}

void test2(uint64_t a, uint64_t b) {
    thread t1{ func, &p.x, a, 1<<core_a  };
    thread t2{ func, &p.y, b, 1<<core_b  };

    t1.join();
    t2.join();
}

int main() {
    uint64_t a, b;
    cin >> a >> b;


    auto start = std::chrono::system_clock::now();
    //test1(a, b);
    //test2(a, b);
    auto end = std::chrono::system_clock::now();
    cout << (end - start).count();
}

结果主要如下:

x86                                         x64             
cores    test1           test2              cores       test1        test2  
         debug  release  debug  release               debug release  debug  release
0-0      4.0s   2.8s     4.0s   2.8s        0-0       2.8s  2.8s     2.8s   2.8s
0-1      5.6s   6.1s     3.0s   1.5s        0-1       4.2s  7.8s     2.1s   1.5s
0-2      6.2s   1.8s     2.0s   1.4s        0-2       3.5s  2.0s     1.4s   1.4s
0-3      6.2s   1.8s     2.0s   1.4s        0-3       3.5s  2.0s     1.4s   1.4s
0-5      6.5s   1.8s     2.0s   1.4s        0-5       3.5s  2.0s     1.4s   1.4s

test result in image

我的CPU是intel core i7-9750h。 “ core0”和“ core1”属于物理核心,“ core2”和“ core3”等也是如此。 MSVC 14.24被用作编译器。

记录的时间是几次运行中最佳成绩的近似值,因为有大量的后台任务。我认为这很公平,因为可以将结果清楚地分为几组,而0.1s〜0.3s的误差不会影响这种划分。

Test2很容易解释。由于xy位于不同的缓存行中,因此,在2个物理内核上运行可以提高2倍的性能提升(此处可忽略在单个内核上运行2个线程时上下文切换的成本),并且可以在一个内核上运行SMT的效率比2个物理内核低,受制于咖啡杯的吞吐量限制(相信Ryzen可以做得更好),并且比临时多线程效率更高。看来这里的64位模式更有效。

但是test1的结果令我感到困惑。首先,在调试模式下,0-2、0-3和0-5比0-0慢,这是有道理的。我之所以这样解释,是因为某些数据是从L1到L3以及从L3到L1反复移动的,因为缓存必须保持2个内核之间的一致性,而当在单个内核上运行时,它始终保持在L1中。但是,该理论与0-1对始终是最慢的事实相矛盾。从技术上讲,两个线程应该共享相同的L1缓存。 0-1的运行速度应是0-0的2倍。

其次,在释放模式下,0-2、0-3和0-5比0-0快,这证明了上述理论。

Last,0-1在64位和32位模式下的运行速度都比release中的运行慢。那是我最不明白的。我阅读了生成的汇编代码,没有发现任何帮助。

c++ performance x86 cpu-cache micro-architecture
1个回答
-1
投票

这可以通过超线程来解释。共享为2个超线程内核的内核不会像2个完全独立的内核那样获得两倍的吞吐量。相反,您可能会得到大约1.7倍的性能。

确实,您的处理器具有6个核心和12个线程,如果正确读取所有这些信息,那么core0 / core1是同一基础核心上的2个线程。

实际上,如果您在脑海中想象到超线程是如何工作的,并且将两个独立的内核的工作交织在一起,也就不足为奇了。

© www.soinside.com 2019 - 2024. All rights reserved.