NUMA の非対称メモリ アクセスの影響を測定しようとしましたが、失敗しました。
実験
Intel Xeon X5570 @ 2.93GHz、2 CPU、8 コアで実行。
コア 0 に固定されたスレッドで、numa_alloc_local を使用してコア 0 の NUMA ノードにサイズ 10,000,000 バイトの配列xを割り当てます。次に、配列xを 50 回繰り返し、配列内の各バイトを読み書きします。50 回の反復を実行するのにかかった時間を測定します。
次に、サーバーの他の各コアで新しいスレッドを固定し、配列x内のすべてのバイトの読み取りと書き込みを 50 回繰り返して経過時間を再度測定します。
キャッシュの影響を最小限に抑えるために、配列xは大きくなっています。キャッシュが役に立っている場合ではなく、ロードとストアのために CPU が RAM まで行かなければならない場合の速度を測定したいと考えています。
サーバーには 2 つの NUMA ノードがあるため、配列xが割り当てられている同じノードにアフィニティを持つコアは、読み取り/書き込み速度が速いと予想されます。私はそれを見ていません。
なんで?
おそらくNUMAは、他の場所で提案されているように、8〜12コアを超えるシステムにのみ関連していますか?
http://lse.sourceforge.net/numa/faq/
numest.cpp
#include <numa.h>
#include <iostream>
#include <boost/thread/thread.hpp>
#include <boost/date_time/posix_time/posix_time.hpp>
#include <pthread.h>
void pin_to_core(size_t core)
{
cpu_set_t cpuset;
CPU_ZERO(&cpuset);
CPU_SET(core, &cpuset);
pthread_setaffinity_np(pthread_self(), sizeof(cpu_set_t), &cpuset);
}
std::ostream& operator<<(std::ostream& os, const bitmask& bm)
{
for(size_t i=0;i<bm.size;++i)
{
os << numa_bitmask_isbitset(&bm, i);
}
return os;
}
void* thread1(void** x, size_t core, size_t N, size_t M)
{
pin_to_core(core);
void* y = numa_alloc_local(N);
boost::posix_time::ptime t1 = boost::posix_time::microsec_clock::universal_time();
char c;
for (size_t i(0);i<M;++i)
for(size_t 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 << "Elapsed read/write by same thread that allocated on core " << core << ": " << (t2 - t1) << std::endl;
*x = y;
}
void thread2(void* x, size_t core, size_t N, size_t M)
{
pin_to_core(core);
boost::posix_time::ptime t1 = boost::posix_time::microsec_clock::universal_time();
char c;
for (size_t i(0);i<M;++i)
for(size_t 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 << "Elapsed read/write by thread on core " << core << ": " << (t2 - t1) << std::endl;
}
int main(int argc, const char **argv)
{
int numcpus = numa_num_task_cpus();
std::cout << "numa_available() " << numa_available() << std::endl;
numa_set_localalloc();
bitmask* bm = numa_bitmask_alloc(numcpus);
for (int i=0;i<=numa_max_node();++i)
{
numa_node_to_cpus(i, bm);
std::cout << "numa node " << i << " " << *bm << " " << numa_node_size(i, 0) << std::endl;
}
numa_bitmask_free(bm);
void* x;
size_t N(10000000);
size_t M(50);
boost::thread t1(boost::bind(&thread1, &x, 0, N, M));
t1.join();
for (size_t i(0);i<numcpus;++i)
{
boost::thread t2(boost::bind(&thread2, x, i, N, M));
t2.join();
}
numa_free(x, N);
return 0;
}
出力
g++ -o numatest -pthread -lboost_thread -lnuma -O0 numatest.cpp
./numatest
numa_available() 0 <-- NUMA is available on this system
numa node 0 10101010 12884901888 <-- cores 0,2,4,6 are on NUMA node 0, which is about 12 Gb
numa node 1 01010101 12874584064 <-- cores 1,3,5,7 are on NUMA node 1, which is slightly smaller than node 0
Elapsed read/write by same thread that allocated on core 0: 00:00:01.767428
Elapsed read/write by thread on core 0: 00:00:01.760554
Elapsed read/write by thread on core 1: 00:00:01.719686
Elapsed read/write by thread on core 2: 00:00:01.708830
Elapsed read/write by thread on core 3: 00:00:01.691560
Elapsed read/write by thread on core 4: 00:00:01.686912
Elapsed read/write by thread on core 5: 00:00:01.691917
Elapsed read/write by thread on core 6: 00:00:01.686509
Elapsed read/write by thread on core 7: 00:00:01.689928
どのコアが読み取りと書き込みを行っているかに関係なく、配列x に対して読み取りと書き込みを 50 回繰り返すには、約 1.7 秒かかります。
アップデート:
私の CPU のキャッシュ サイズは 8Mb なので、おそらく 10Mb の配列xではキャッシュ効果を排除するには十分な大きさではありません。100Mb 配列xを試し、最も内側のループ内で __sync_synchronize() を使用して完全なメモリ フェンスを発行しようとしました。それでも、NUMA ノード間の非対称性は明らかになりません。
更新 2:
__sync_fetch_and_add() を使用して、配列xの読み取りと書き込みを試みました。まだ何もありません。