我试图测量NUMA的非对称内存访问效应,但失败了
实验
在2.93GHz、2个CPU、8核的英特尔至强X5570上执行
在固定到core 0的线程上,我在core 0的NUMA节点上使用NUMA_alloc_local分配一个大小为10000000字节的数组。
然后我迭代数组x50次,读取和写入数组中的每个字节。测量执行50次迭代所用的时间
然后,在服务器中的每个其他内核上,我固定一个新线程,并再次测量执行50次读写迭代所用的时间
到数组中的每个字节x
数组x很大,可以最大限度地减少缓存影响。我们想要测量的是CPU必须一直到RAM才能加载和存储时的速度,而不是缓存有帮助时的速度
我的服务器中有两个NUMA节点,因此我希望在分配数组x的同一节点上具有关联性的核心具有
更快的读/写速度。我没看到
为什么?
也许NUMA只适用于8-12核以上的系统,正如我在其他地方看到的那样
http://lse.sourceforge.net/numa/faq/
numatest.cpp
#包括<;numa.h>;
#包括<;iostream>;
#包括<;增压/螺纹/螺纹。hpp>;
#包括<;boost/date\u time/posix\u time/posix\u time.hpp>;
#包括<;pthread.h>;
空心销与芯(芯尺寸)
{
cpu\u设置\u t cpuset;
CPU_ZERO(&;cpuset);
中央处理器组(核心和中央处理器组);
pthread_setaffinity_np(pthread_self()、sizeof(cpu_set_t)和cpuset);
}
标准::奥斯特雷姆公司;操作员<&书信电报;(标准:ostream&;os、常量位掩码&;bm)
{
对于(大小i=0;i<;bm.size;++i)
{
操作系统<;<;numa\U位掩码\U isbitset(&;bm,i);
}
返回操作系统;
}
空心*螺纹1(空心**x,尺寸\u t芯,尺寸\u t N,尺寸\u t M)
{
引脚至铁芯(铁芯);
void*y=numa\u alloc\u local(N);
boost::posix_time::ptime t1=boost::posix_time::microsec_clock::universal_time();
字符c;
用于(尺寸i(0);i<;M;++i)
对于(尺寸j(0);j<;N;++j)
{
c=((char*)y)[j];
((char*)y)[j]=c;
}
boost::posix_time::ptime t2=boost::posix_time::microsec_clock::universal_time();
std::cout<;<;<;<;<;core<;<;<;<;<;<;<;<;<;<;<;<;<;<;<;core<;<;<;<;<;<;<;t2-t1)<;<;std::endl;
*x=y;
}
空心螺纹2(空心*x,尺寸\u t芯,尺寸\u t N,尺寸\u t M)
{
引脚至铁芯(铁芯);
boost::posix_time::ptime t1=boost::posix_time::microsec_clock::universal_time();
字符c;
用于(尺寸i(0);i<;M;++i)
对于(尺寸j(0);j<;N;++j)
{
c=((char*)x)[j];
((char*)x)[j]=c;
}
boost::posix_time::ptime t2=boost::posix_time::microsec_clock::universal_time();
std::cout<;<;<;<;<;core<;<;<;<;<;<;<;<;<;<;<;<;<;<;<;t2-t1)<;<;std::endl;
}
int main(int argc,常量字符**argv)
{
int numpus=numa_num_task_cpu();
标准::cout<;<;<;<;numa_available()<;<;numa_available()<;<;标准::endl;
numa_set_localalloc();
位掩码*bm=numa\u位掩码\u alloc(numpus);
对于(int i=0;i<;=numa_max_node();++i)
{
numa_节点到cpu(i,bm);
标准::cout<;<;<;<;numa节点<;<;<;<;<;<;<;bm<;<;<;numa节点尺寸(i,0)<;<;标准::endl;
}
无位掩码(bm);
void*x;
规模(10000000);
尺寸M(50);
boost::线程t1(boost::绑定(&;thread1,&;x,0,N,M));
t1.join();
用于(大小i(0);i<;numpus;++i)
{
boost::线程t2(boost::绑定(&;thread2,x,i,N,M));
t2.连接();
}
无努马(x,N);
返回0;
}
输出
g++-o numest-pthread-lboost\u thread-lnuma-O0 numest.cpp
/最新
numa_可用()0<;--NUMA在此系统上可用
numa节点0 10101010 12884901888<;-核心0,2,4,6位于NUMA节点0上,约为12GB
numa节点1 01010101 12874584064<;-核1、3、5、7位于NUMA节点1上,该节点略小于节点0
在内核0:00:00:01.767428上分配的同一线程已用读/写时间
内核0:00:00:01.760554上线程经过的读/写操作
内核1:00:00:01.719686上线程经过的读/写操作
内核2:00:00:01.708830上线程已用读/写时间
内核3:00:00:01.691560上线程已用读/写时间
内核4:00:00:01.686912上线程经过的读/写操作
内核5:00:00:01.691917上线程经过的读/写操作
内核6:00:00:01.686509上线程经过的读/写操作
内核7:00:00:01.689928上线程经过的读/写操作
无论哪个内核正在读写,通过数组执行50次迭代读写大约需要1.7秒
更新:
我的CPU上的缓存大小是8Mb,因此10Mb的阵列可能不足以消除缓存效应。我尝试了100Mb阵列x,并且
我尝试过在最里面的循环中使用_sync_synchronize()发出一个完整的内存限制。它仍然没有揭示NUMA节点之间的任何不对称性
更新2:
我已尝试使用uu sync\u fetch\u和_add()读取和写入数组x。还是没什么
我想指出的第一件事是,您可能需要仔细检查每个节点上有哪些内核。我不记得核心和节点是那样交错的。
此外,由于HT,您应该有16个线程。(除非您禁用了它)
还有一件事:
插座1366 Xeon机器仅略为NUMA。因此,很难看出两者之间的区别。NUMA效应在4P Opteron上更为明显
在像您这样的系统上,节点到节点的带宽实际上比CPU到内存的带宽快。由于您的访问模式是完全顺序的,因此无论数据是否为本地数据,您都将获得全部带宽。更好的测量方法是延迟。尝试随机访问1 GB的块,而不是按顺序流式传输
最后一件事:
根据编译器优化的力度,循环可能会被优化,因为它不会做任何事情:
c=((char*)x)[j];
((char*)x)[j]=c;
这样做可以保证编译器不会消除它:
((char*)x)[j]+=1;