我尝试测量NUMA的非对称内存访问效果,但失败了。

本实验

在2.93GHz,2个CPU,8核的Intel Xeon X5570上执行。

在固定到核心0的线程上,我在具有numa_alloc_local的核心0的NUMA节点上分配了大小为10,000,000字节的 x 数组。
然后,我遍历数组 x 50次,并读取和写入数组中的每个字节。测量经过的时间以执行50次迭代。

然后,在服务器中的每个其他内核上,我固定一个新线程,然后再次测量经过50次读取和写入迭代的时间
x 数组中的每个字节。

数组 x 很大,可以最大程度地减少缓存影响。我们要测量CPU必须一直到RAM进行加载和存储的速度,而不是在缓存有帮助的情况下测量速度。

我的服务器中有两个NUMA节点,所以我希望在分配了 x 数组的同一节点上具有亲和力的内核
更快的读写速度。我没看到

为什么?

就像我在其他地方看到的那样,也许NUMA仅与具有8-12个内核的系统有关?

http://lse.sourceforge.net/numa/faq/

numatest.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 数组。依然没有。

最佳答案

我要指出的第一件事是,您可能需要仔细检查每个节点上的核心。我不记得那样交错的核心和节点。
另外,由于HT,您应该有16个线程。 (除非您禁用了它)

另一件事:

socket 1366 Xeon机器只有NUMA。因此,很难看到区别。 NUMA效应在4P皓龙上更为明显。

在像您这样的系统上,节点到节点的带宽实际上比CPU到内存的带宽快。由于访问模式是完全顺序的,因此无论数据是否是本地的,您都将获得全部带宽。更好的衡量标准是延迟。尝试随机访问1 GB的块,而不是顺序流式传输。

最后一件事:

根据您的编译器优化的积极程度,您的循环可能不会被优化,因为它什么都不做:

c = ((char*)x)[j];
((char*)x)[j] = c;

这样的事情将保证编译器不会消除它:
((char*)x)[j] += 1;

关于c++ - 测量NUMA(非统一内存访问)。没有明显的不对称性。为什么?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/7259363/

10-13 05:08