Я думаю, что он должен работать на любой системе Linux с недавним ядром, GCC и Boost-версией. Этот код может быть также очищен и сильно уменьшен, но как бы то ни было, он одноразовый.

ытался измерить асимметричные эффекты доступа к памяти NUMA, но потерпел неудачу.

Эксперимент

Выполняется на Intel Xeon X5570 с частотой 2,93 ГГц, 2 процессора, 8 ядер.

В потоке, прикрепленном к ядру 0, я выделяю массивx размером 10 000 000 байтов на узле NUMA ядра 0 с numa_alloc_local. Затем я перебираю массивx 50 раз и прочитайте и запишите каждый байт в массиве. Измерьте прошедшее время, чтобы выполнить 50 итераций.

Затем на каждом из остальных ядер моего сервера я закрепляю новый поток и снова измеряю затраченное время, чтобы выполнить 50 итераций чтения и записи для каждого байта в массиве.x.

массивx большой, чтобы минимизировать эффекты кэша. Мы хотим измерить скорость, когда процессору приходится загружать и хранить весь объем ОЗУ, а не когда кеш помогает.

На моем сервере есть два узла 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(s,ize_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

Выполнение 50 итераций при чтении и записи в массивx занимает около 1,7 секунды, независимо от того, какое ядро ​​выполняет чтение и запись.

Обновить:

Размер кэша на моих процессорах составляет 8 МБ, так что, возможно, массив 10 МБx недостаточно велик для устранения кеш-эффектов. Я пробовал 100Мб массивxи я попытался выдать забор полной памяти с помощью __sync_synchronize () внутри моих внутренних циклов. Это все еще не показывает никакой асимметрии между узлами NUMA.

Обновление 2:

Я пытался читать и писать в массивx с __sync_fetch_and_add (). Еще ничего.

Ответы на вопрос(6)

Ваш ответ на вопрос