在NUMA架构中移动每线程的内存页面

时间:2013-08-02 05:14:29

标签: c memory-management pthreads system-calls numa

我有两个问题:

(i)假设线程X在CPU Y上运行。是否可以使用syscalls migrate_pages - 甚至更好的move_pages(或它们的libnuma包装器) - 将与X相关联的页面移动到Y连接的节点?

这个问题是因为两个系统调用的第一个参数是PID(我需要一个针对某些研究的每线程方法)

(ii)在(i)的肯定答案的情况下,我如何获得某些线程使用的所有页面?我的目的是,移动包含数组M []的页面例如...为了使用上面的系统调用,如何将数据结构与其内存页面“链接”?

额外信息:我正在使用C和pthreads。提前谢谢!

2 个答案:

答案 0 :(得分:1)

您希望使用更高级libnuma接口而不是低级系统调用。

  

libnuma库为Linux内核支持的NUMA(非统一内存访问)策略提供了一个简单的编程接口。在NUMA架构上,某些内存区域的延迟或带宽与其他区域不同。

     

可用策略是页面交织(即,以循环方式从系统上的所有节点或子集中分配),首选节点分配(即,优选地在特定节点上分配),本地分配(即,在任务当前正在执行的节点上分配),或仅在特定节点上分配(即,在可用节点的某个子集上分配)。也可以将任务绑定到特定节点。

man pages for the low level numa_* system calls警告你不要使用它们:

  

-lnuma链接以获取系统调用定义。 libnuma包中提供了<numaif.h>和所需的numactl标题。

     

但是,应用程序不应直接使用这些系统调用。建议使用numa(3)包中numactl函数提供的更高级别接口。 numactl包可在<ftp://oss.sgi.com/www/projects/libnuma/download/>处获得。该软件包也包含在某些Linux发行版中。一些发行版包括单独的numactl-devel包中的开发库和标题。

答案 1 :(得分:1)

这里是我用来将线程固定到单个CPU并将堆栈移动到相应的NUMA节点的代码(稍微适合删除其他地方定义的一些常量)。请注意,我首先正常创建线程,然后从线程内调用下面的SetAffinityAndRelocateStack()。我认为这比尝试创建自己的堆栈要好得多,因为堆栈特别支持增长,以防达到底部。

代码也可以适用于从外部操作新创建的线程,但这可能会导致竞争条件(例如,如果线程在其堆栈中执行I / O),所以我不推荐它

void* PreFaultStack()
{
    const size_t NUM_PAGES_TO_PRE_FAULT = 50;
    const size_t size = NUM_PAGES_TO_PRE_FAULT * numa_pagesize();
    void *allocaBase = alloca(size);
    memset(allocaBase, 0, size);
    return allocaBase;
}

void SetAffinityAndRelocateStack(int cpuNum)
{
    assert(-1 != cpuNum);
    cpu_set_t cpuset;
    CPU_ZERO(&cpuset);
    CPU_SET(cpuNum, &cpuset);
    const int rc = pthread_setaffinity_np(pthread_self(), sizeof(cpu_set_t), &cpuset);
    assert(0 == rc);

    pthread_attr_t attr;
    void *stackAddr = nullptr;
    size_t stackSize = 0;
    if ((0 != pthread_getattr_np(pthread_self(), &attr)) || (0 != pthread_attr_getstack(&attr, &stackAddr, &stackSize))) {
        assert(false);
    }

    const unsigned long nodeMask = 1UL << numa_node_of_cpu(cpuNum);
    const auto bindRc = mbind(stackAddr, stackSize, MPOL_BIND, &nodeMask, sizeof(nodeMask), MPOL_MF_MOVE | MPOL_MF_STRICT);
    assert(0 == bindRc);

    PreFaultStack();
    // TODO: Also lock the stack with mlock() to guarantee it stays resident in RAM
    return;
}