【问题标题】:Measuring NUMA (Non-Uniform Memory Access). No observable asymmetry. Why?测量 NUMA(非统一内存访问)。没有可观察到的不对称性。为什么?
【发布时间】:2011-11-07 17:32:39
【问题描述】:

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

实验

在 Intel Xeon X5570 @ 2.93GHz、2 个 CPU、8 个内核上执行。

在固定到核心 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。还是什么都没有。

【问题讨论】:

    标签: c++ linux performance linux-kernel numa


    【解决方案1】:

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

    另一件事:

    socket 1366 Xeon 机器只是稍微 NUMA。所以很难看出区别。 NUMA 效应在 4P 皓龙上更为明显。

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

    最后一件事:

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

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

    这样的东西可以保证它不会被编译器淘汰:

    ((char*)x)[j] += 1;
    

    【讨论】:

    • 是的,我已禁用超线程。对 numa_node_to_cpus() 的调用显示了这种交错架构......所以我认为它是正确的。禁用优化是我指定 g++ -O0 开关的原因。
    • +1 这是关于内存带宽顺序访问与随机访问的一个非常有趣的评论——这是因为缓存局部性,还是因为其他原因?那么您是否认为同样的实验应该测量 NUMA 不对称性,但我的 Xeon X5570 没有足够的不对称性来测量?
    • 我记得读过一些关于双 1366 架构的文章,远程节点的延迟仅比本地节点高 50%。在 4P Opterons 上,它可以高 4 倍。如果您使用顺序访问,硬件预取器将能够在您实际需要数据之前将其拾取并开始获取数据。因此,延迟是隐藏的。但是如果你随机访问内存,你可以打败这个预取器,并在每次访问时获得完整的延迟。
    • 所以要回答你的问题,是的,它与地点有关。如果随机访问没有显示任何内容,那么您很可能需要更复杂的测试来显示它。我实际上没有双插槽 1366 机器,所以我不确定。
    • 在这种情况下,分支无关紧要。有两个原因: 1. 预取器只将数据移动到缓存中。它是否被使用是另一回事。 2. 您的分支足够简单,可以在 100% 的时间正确预测它,除非它退出循环。
    【解决方案2】:

    啊哈!玄学是对的!不知何故,硬件预取优化了我的读/写。

    如果是缓存优化,那么强制使用内存屏障会破坏优化:

    c = __sync_fetch_and_add(((char*)x) + j, 1);
    

    但这并没有什么不同。不同之处在于将我的迭代器索引乘以素数 1009 以击败预取优化:

    *(((char*)x) + ((j * 1009) % N)) += 1;
    

    随着这种变化,NUMA 不对称性被清楚地揭示:

    numa_available() 0
    numa node 0 10101010 12884901888
    numa node 1 01010101 12874584064
    Elapsed read/write by same thread that allocated on core 0: 00:00:00.961725
    Elapsed read/write by thread on core 0: 00:00:00.942300
    Elapsed read/write by thread on core 1: 00:00:01.216286
    Elapsed read/write by thread on core 2: 00:00:00.909353
    Elapsed read/write by thread on core 3: 00:00:01.218935
    Elapsed read/write by thread on core 4: 00:00:00.898107
    Elapsed read/write by thread on core 5: 00:00:01.211413
    Elapsed read/write by thread on core 6: 00:00:00.898021
    Elapsed read/write by thread on core 7: 00:00:01.207114
    

    至少我认为是这样的。

    感谢神秘主义者!

    编辑:结论 ~133%

    对于任何只是浏览这篇文章以大致了解 NUMA 性能特征的人,根据我的测试,这是底线:

    对非本地 NUMA 节点的内存访问大约是对本地节点的内存访问延迟的 1.33 倍。

    【讨论】:

    • 我很好奇 1009 因子。我用不同的数字替换它并得到不同的延迟——例如,1001 有更大的差异。为什么会发生这种情况?预取是否只有几次失败?
    • 我选择了 1009,因为它是一个比缓存行大得多的质数。在*(((char*)x) + ((j * 1009) % N)) += 1; 公式中使用素数可确保每个字节只增加一次。对于像 1001 这样的非质数,数组中的某些字节会增加几次,而某些字节根本不会增加。话虽如此,我还没有尝试任何其他因素。我本来希望看到与非素因数 1001 相同或更小的差异,所以这里显然还有更多我不明白的地方。
    • 如果您每 64 个位置访问数组的元素(以确保您错过 LLC)而不是使用质数会发生什么? (使用 -O0 编译程序)。如果你使用一些素数,位置将在同一个缓存块中。
    • 不是质数,不能像英特尔内存延迟检查器那样禁用硬件预取器吗? software.intel.com/content/www/us/en/develop/articles/…
    【解决方案3】:

    感谢您提供此基准代码。我采用了您的“固定”版本并将其更改为纯 C + OpenMP,并添加了一些测试内存系统在争用下的行为方式。您可以找到新代码here

    以下是 Quad Opteron 的一些示例结果:

    num cpus: 32
    numa available: 0
    numa node 0 10001000100010000000000000000000 - 15.9904 GiB
    numa node 1 00000000000000001000100010001000 - 16 GiB
    numa node 2 00010001000100010000000000000000 - 16 GiB
    numa node 3 00000000000000000001000100010001 - 16 GiB
    numa node 4 00100010001000100000000000000000 - 16 GiB
    numa node 5 00000000000000000010001000100010 - 16 GiB
    numa node 6 01000100010001000000000000000000 - 16 GiB
    numa node 7 00000000000000000100010001000100 - 16 GiB
    
    sequential core 0 -> core 0 : BW 4189.87 MB/s
    sequential core 1 -> core 0 : BW 2409.1 MB/s
    sequential core 2 -> core 0 : BW 2495.61 MB/s
    sequential core 3 -> core 0 : BW 2474.62 MB/s
    sequential core 4 -> core 0 : BW 4244.45 MB/s
    sequential core 5 -> core 0 : BW 2378.34 MB/s
    sequential core 6 -> core 0 : BW 2442.93 MB/s
    sequential core 7 -> core 0 : BW 2468.61 MB/s
    sequential core 8 -> core 0 : BW 4220.48 MB/s
    sequential core 9 -> core 0 : BW 2442.88 MB/s
    sequential core 10 -> core 0 : BW 2388.11 MB/s
    sequential core 11 -> core 0 : BW 2481.87 MB/s
    sequential core 12 -> core 0 : BW 4273.42 MB/s
    sequential core 13 -> core 0 : BW 2381.28 MB/s
    sequential core 14 -> core 0 : BW 2449.87 MB/s
    sequential core 15 -> core 0 : BW 2485.48 MB/s
    sequential core 16 -> core 0 : BW 2938.08 MB/s
    sequential core 17 -> core 0 : BW 2082.12 MB/s
    sequential core 18 -> core 0 : BW 2041.84 MB/s
    sequential core 19 -> core 0 : BW 2060.47 MB/s
    sequential core 20 -> core 0 : BW 2944.13 MB/s
    sequential core 21 -> core 0 : BW 2111.06 MB/s
    sequential core 22 -> core 0 : BW 2063.37 MB/s
    sequential core 23 -> core 0 : BW 2082.75 MB/s
    sequential core 24 -> core 0 : BW 2958.05 MB/s
    sequential core 25 -> core 0 : BW 2091.85 MB/s
    sequential core 26 -> core 0 : BW 2098.73 MB/s
    sequential core 27 -> core 0 : BW 2083.7 MB/s
    sequential core 28 -> core 0 : BW 2934.43 MB/s
    sequential core 29 -> core 0 : BW 2048.68 MB/s
    sequential core 30 -> core 0 : BW 2087.6 MB/s
    sequential core 31 -> core 0 : BW 2014.68 MB/s
    
    all-contention core 0 -> core 0 : BW 1081.85 MB/s
    all-contention core 1 -> core 0 : BW 299.177 MB/s
    all-contention core 2 -> core 0 : BW 298.853 MB/s
    all-contention core 3 -> core 0 : BW 263.735 MB/s
    all-contention core 4 -> core 0 : BW 1081.93 MB/s
    all-contention core 5 -> core 0 : BW 299.177 MB/s
    all-contention core 6 -> core 0 : BW 299.63 MB/s
    all-contention core 7 -> core 0 : BW 263.795 MB/s
    all-contention core 8 -> core 0 : BW 1081.98 MB/s
    all-contention core 9 -> core 0 : BW 299.177 MB/s
    all-contention core 10 -> core 0 : BW 300.149 MB/s
    all-contention core 11 -> core 0 : BW 262.905 MB/s
    all-contention core 12 -> core 0 : BW 1081.89 MB/s
    all-contention core 13 -> core 0 : BW 299.173 MB/s
    all-contention core 14 -> core 0 : BW 299.025 MB/s
    all-contention core 15 -> core 0 : BW 263.865 MB/s
    all-contention core 16 -> core 0 : BW 432.156 MB/s
    all-contention core 17 -> core 0 : BW 233.12 MB/s
    all-contention core 18 -> core 0 : BW 232.889 MB/s
    all-contention core 19 -> core 0 : BW 202.48 MB/s
    all-contention core 20 -> core 0 : BW 434.299 MB/s
    all-contention core 21 -> core 0 : BW 233.274 MB/s
    all-contention core 22 -> core 0 : BW 233.144 MB/s
    all-contention core 23 -> core 0 : BW 202.505 MB/s
    all-contention core 24 -> core 0 : BW 434.295 MB/s
    all-contention core 25 -> core 0 : BW 233.274 MB/s
    all-contention core 26 -> core 0 : BW 233.169 MB/s
    all-contention core 27 -> core 0 : BW 202.49 MB/s
    all-contention core 28 -> core 0 : BW 434.295 MB/s
    all-contention core 29 -> core 0 : BW 233.309 MB/s
    all-contention core 30 -> core 0 : BW 233.169 MB/s
    all-contention core 31 -> core 0 : BW 202.526 MB/s
    
    two-contention core 0 -> core 0 : BW 3306.11 MB/s
    two-contention core 1 -> core 0 : BW 2199.7 MB/s
    
    two-contention core 0 -> core 0 : BW 3286.21 MB/s
    two-contention core 2 -> core 0 : BW 2220.73 MB/s
    
    two-contention core 0 -> core 0 : BW 3302.24 MB/s
    two-contention core 3 -> core 0 : BW 2182.81 MB/s
    
    two-contention core 0 -> core 0 : BW 3605.88 MB/s
    two-contention core 4 -> core 0 : BW 3605.88 MB/s
    
    two-contention core 0 -> core 0 : BW 3297.08 MB/s
    two-contention core 5 -> core 0 : BW 2217.82 MB/s
    
    two-contention core 0 -> core 0 : BW 3312.69 MB/s
    two-contention core 6 -> core 0 : BW 2227.04 MB/s
    
    two-contention core 0 -> core 0 : BW 3287.93 MB/s
    two-contention core 7 -> core 0 : BW 2209.48 MB/s
    
    two-contention core 0 -> core 0 : BW 3660.05 MB/s
    two-contention core 8 -> core 0 : BW 3660.05 MB/s
    
    two-contention core 0 -> core 0 : BW 3339.63 MB/s
    two-contention core 9 -> core 0 : BW 2223.84 MB/s
    
    two-contention core 0 -> core 0 : BW 3303.77 MB/s
    two-contention core 10 -> core 0 : BW 2197.99 MB/s
    
    two-contention core 0 -> core 0 : BW 3323.19 MB/s
    two-contention core 11 -> core 0 : BW 2196.08 MB/s
    
    two-contention core 0 -> core 0 : BW 3582.23 MB/s
    two-contention core 12 -> core 0 : BW 3582.22 MB/s
    
    two-contention core 0 -> core 0 : BW 3324.9 MB/s
    two-contention core 13 -> core 0 : BW 2250.74 MB/s
    
    two-contention core 0 -> core 0 : BW 3305.66 MB/s
    two-contention core 14 -> core 0 : BW 2209.5 MB/s
    
    two-contention core 0 -> core 0 : BW 3303.52 MB/s
    two-contention core 15 -> core 0 : BW 2182.43 MB/s
    
    two-contention core 0 -> core 0 : BW 3352.74 MB/s
    two-contention core 16 -> core 0 : BW 2607.73 MB/s
    
    two-contention core 0 -> core 0 : BW 3092.65 MB/s
    two-contention core 17 -> core 0 : BW 1911.98 MB/s
    
    two-contention core 0 -> core 0 : BW 3025.91 MB/s
    two-contention core 18 -> core 0 : BW 1918.06 MB/s
    
    two-contention core 0 -> core 0 : BW 3257.56 MB/s
    two-contention core 19 -> core 0 : BW 1885.03 MB/s
    
    two-contention core 0 -> core 0 : BW 3339.64 MB/s
    two-contention core 20 -> core 0 : BW 2603.06 MB/s
    
    two-contention core 0 -> core 0 : BW 3119.29 MB/s
    two-contention core 21 -> core 0 : BW 1918.6 MB/s
    
    two-contention core 0 -> core 0 : BW 3054.14 MB/s
    two-contention core 22 -> core 0 : BW 1910.61 MB/s
    
    two-contention core 0 -> core 0 : BW 3214.44 MB/s
    two-contention core 23 -> core 0 : BW 1881.69 MB/s
    
    two-contention core 0 -> core 0 : BW 3332.3 MB/s
    two-contention core 24 -> core 0 : BW 2611.8 MB/s
    
    two-contention core 0 -> core 0 : BW 3111.94 MB/s
    two-contention core 25 -> core 0 : BW 1922.11 MB/s
    
    two-contention core 0 -> core 0 : BW 3049.02 MB/s
    two-contention core 26 -> core 0 : BW 1912.85 MB/s
    
    two-contention core 0 -> core 0 : BW 3251.88 MB/s
    two-contention core 27 -> core 0 : BW 1881.82 MB/s
    
    two-contention core 0 -> core 0 : BW 3345.6 MB/s
    two-contention core 28 -> core 0 : BW 2598.82 MB/s
    
    two-contention core 0 -> core 0 : BW 3109.04 MB/s
    two-contention core 29 -> core 0 : BW 1923.81 MB/s
    
    two-contention core 0 -> core 0 : BW 3062.94 MB/s
    two-contention core 30 -> core 0 : BW 1921.3 MB/s
    
    two-contention core 0 -> core 0 : BW 3220.8 MB/s
    two-contention core 31 -> core 0 : BW 1901.76 MB/s
    

    如果有人有进一步的改进,我很乐意听到他们的消息。例如,这些显然不是真实世界单位中的完美带宽测量值(​​可能会偏离 - 希望是常数 - 整数因子)。

    【讨论】:

    • 嗨。我很好奇:为什么你的 Quad Operton 机器有 8 个 NUMA 节点?
    • 四个套接字,每个节点两个通道? (猜测。)
    • 还有一个建议:当您发生争用时,您的测量结果可能会关闭。当“较快”线程完成时,“较慢”线程的吞吐量会上升。所以对于“较慢”的线程,这个数字可能过于乐观了。
    【解决方案4】:

    几个cmets:

    • 要查看系统的 NUMA 结构(在 linux 上),您可以使用 hwloc 库中的 lstopo 实用程序获得图形概览。特别是,您将看到哪些核心编号是哪个 NUMA 节点(处理器插槽)的成员
    • char 可能不是测量最大 RAM 吞吐量的理想数据类型。我怀疑使用 32 位或 64 位数据类型,您可以通过相同数量的 cpu 周期获得更多数据。
    • 更一般地,您还应该检查您的测量不受 CPU 速度的限制,而是受到 RAM 速度的限制。例如,ramspeed 实用程序在源代码中在某种程度上显式展开内部循环:

      for(i = 0; i < blk/sizeof(UTL); i += 32) {
          b[i] = a[i];        b[i+1] = a[i+1];
          ...
          b[i+30] = a[i+30];  b[i+31] = a[i+31];
      }
      

      编辑:在受支持的架构上 ramsmp 实际上甚至对这些循环使用“手写”汇编代码

    • L1/L2/L3 缓存效果:以 GByte/s 为单位测量带宽作为块大小的函数是有益的。当增加与读取数据的位置(缓存或主内存)相对应的块大小时,您应该会看到大约四种不同的速度。您的处理器似乎有8 MByte 的 Level3 (?) 缓存,因此您的 1000 万字节可能只是大部分留在 L3 缓存中(在一个处理器的所有内核之间共享)。

    • Memory channels:你的处理器有3 memory channels。如果您的内存条已安装,您可以利用它们(例如参见主板手册),您可能希望同时运行多个线程。我看到的效果是,当只用一个线程读取时,渐近带宽接近单个内存模块的带宽(例如 DDR-1600 为 12.8 GByte/s),而当运行多个线程时,渐近带宽接近数字内存通道数乘以单个内存模块的带宽。

    【讨论】:

      【解决方案5】:

      您也可以使用 numactl 来选择在哪个节点上运行进程以及从哪里分配内存:

      numactl --cpubind=0 --membind=1 <process>
      

      我将它与LMbench 结合使用来获取内存延迟数:

      numactl --cpubind=0 --membind=0  ./lat_mem_rd -t 512
      numactl --cpubind=0 --membind=1  ./lat_mem_rd -t 512
      

      【讨论】:

      • 硬件预取器是否会影响 lmbench 报告的延迟?
      【解决方案6】:

      如果其他人想尝试这个测试,这里是修改后的工作程序。我很想看到其他硬件的结果。这可以在我的机器上使用 Linux 2.6.34-12-desktop、GCC 4.5.0、Boost 1.47。

      g++ -o numatest -pthread -lboost_thread -lnuma -O0 numatest.cpp
      

      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)
              {
                  *(((char*)y) + ((j * 1009) % N)) += 1;
              }
      
          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)
              {
                  *(((char*)x) + ((j * 1009) % N)) += 1;
              }
      
          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(5);
      
          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;
      }
      

      【讨论】:

      • 我还没有尝试查看这段代码,但是不做任何更改就可以在任何系统上运行好吗?当我有时间的时候,我可以在我的 4 x Opteron 8356(4 x 4 个物理内核)上试试这个。虽然我可能需要一些时间来设置它,因为我很少将它引导到 Linux - numactl 也部分损坏,我以前从未使用过 Boost...lol
      • 我认为它应该适用于任何具有最新内核、GCC 和 Boost 版本的 Linux 系统。这段代码也可以清理和缩小很多,但无论如何,这是一次性的。
      猜你喜欢
      • 2023-04-03
      • 2011-11-06
      • 1970-01-01
      • 1970-01-01
      • 2017-06-16
      • 1970-01-01
      • 2021-03-30
      • 1970-01-01
      • 2019-01-30
      相关资源
      最近更新 更多