提问人:Vern 提问时间:11/1/2023 最后编辑:Peter CordesVern 更新时间:11/1/2023 访问量:130
从多个插槽 (Xeon Scalable) 上的所有内核到共享缓存行的存储争用导致系统变得迟钝
Contention for stores to a shared cache line from all cores on multiple sockets (Xeon Scalable) is causing system to become sluggish
问:
#include <glibmm/thread.h>
#include <sys/sysinfo.h>
#include <stdio.h>
void threadLoop(int *PtrCounter)
{
struct timespec sleep = {0};
while (1)
{
*PtrCounter += 1; // #1: commenting this fixes the slow responsiveness
//clock_nanosleep(CLOCK_MONOTONIC, 0, &sleep, NULL); // #2 uncommenting this fixes the slow responsiveness
// sched_yield(); // #3 uncommenting this does nothing (still get slow responsiveness)
}
}
int counter = 0;
void ExitHandler(int signum)
{
printf("counter=%d\n", counter);
exit(0);
}
int main(int argc, char *argv[])
{
int numThreads = get_nprocs();
int i;
printf("using %d threads... (Ctrl-C to stop)\n", numThreads);
signal(SIGINT, ExitHandler);
Glib::Thread *threadArray[numThreads];
for (i=0; i<numThreads; i++)
{
threadArray[i] = Glib::Thread::create(std::bind(&threadLoop, &counter));
}
// never returns
for (i=0; i<numThreads; i++)
{
threadArray[i]->join();
}
}
在没有优化的情况下编译,因此 ASM 会按照源代码所说的去做(从每个线程加载和存储):counter
g++ `pkg-config --libs --cflags glibmm-2.4` threads.cpp
我在 Dell PowerEdge R760 上的 Fedora 38、内核 6.5.8-200 上运行此代码(Xeon Platinum 8480+,256G 内存,get_nprocs() 返回 224)
当我运行此代码时,系统变得非常无响应(对鼠标点击和键盘按下的响应速度很慢)。在 firefox 中导航变得难以忍受。为什么会这样?
您的第一反应可能是这是可怕的代码,因为 *PtrCounter 不受任何形式的同步保护,而您是对的。我最初使用 __atomic_add_fetch() 进行增量,我看到了同样的缓慢响应。为了得到最小的可重复的例子,我删除了原子的东西。所以是的,它会错误地计算,但我认为这有助于证明这个问题。
你的下一个反应可能是,“你当然会变得迟钝,你正在占用CPU”。是的,我正在占用 cpu,但这本身并不会导致这种迟钝。正如评论中的“#1”所说,如果我注释掉“*PtrCounter +=1”,迟钝就会消失,在这种情况下,我仍然会占用 cpu(从顶部看)。
另一件事是,如果我在其他系统(更旧的 8 cpu intel,或更新的 128 cpu amd)上运行此代码,我不会感到迟钝。我邀请您在系统上尝试代码。除非,它是一个较新的高 cpu 数英特尔,我敢打赌它工作得很好,你不会遇到迟钝。
您可以在代码中看到 #2 和 #3 注释。不知道该怎么做,但我认为它足够有趣。
那么,这种迟钝的根源是什么呢?我知道有很多争用是不好的,会导致大量的缓存抖动,并且会降低算法的性能,但我不认为它应该影响操作系统的响应能力。 操作系统不应该强制非自愿的上下文切换和处理中断,无论这些算法是否遇到争用?
答: 暂无答案
下一个:Colmap 中的多线程
评论
int counter = 0;
正在多个线程上修改,没有同步。没有解释迟钝,而是把代码放进了UB的土地上。*PtrCounter +=1