numa架构的cpu多线程读写内存差异问题

2020-01-11  本文已影响0人  大海无垠_af22

在做一个基于内存的系统,测试多性能的时候发现一个问题,多线程的读取内存比写入内存更快,不同的机器上甚至要快很多。

1 测试环境

测试在笔记本上进行,硬件配置如下:

CPU
    Intel(R) Core(TM) i5-8265U CPU @ 1.60GHz
    基准速度:   1.80 GHz
    插槽: 1
    内核: 4
    逻辑处理器:  8
    虚拟化:    已启用
    L1 缓存:  256 KB
    L2 缓存:  1.0 MB
    L3 缓存:  6.0 MB

内存
    24.0 GB
    速度: 2667 MHz
    已使用的插槽: 2/2
    外形规格:   SODIMM
    为硬件保留的内存:   129 MB

经查,CPU是numa架构。内存有两根,8GB+16GB。
操作系统:win10 家庭版 1809 17763.914
编译器:VS2019.

2 测试代码

针对这个问题,简化了系统代码,提取能复现问题的思路。主要是分配两块内存,一个1M大小,另外一个是多个1M大小的内存集合,总共1GB。有两种操作,读操作是将1GB的内存复制到1M的块中,写操作则相反。创建多个线程同时执行这两个操作,分别记录时间,计算总的速度。
主要代码如下所示。

#include <iostream>
#include <list>
#include <vector>
#include <thread>
#include<stdlib.h>
#include<time.h>

const static int64_t runnum = 100;
const static int mb = 1 << 20;
const static int sizemb = 2048;
// 从多个数据块复制到同一数据块
void test_read_list() {
   std::list<char*> bufs;
   char* mbuf = new char[mb];
   memset(mbuf, 0, mb);
   for (int k = 0; k < sizemb; ++k) {
       char*t = new char[mb];
       memset(t, 0, mb);
       bufs.push_back(t);
   }

   for (int n = 0; n < runnum; ++n) {
       for (auto t: bufs) {
           memcpy(mbuf, t, mb);
       }
   }

   delete[] mbuf;
   for (auto t : bufs)
       delete[] t;
}

void test_read_list_mt() {
   std::thread ts[2];
   time_t st, ed;
  time(&st);
   ts[0] = std::thread(test_read_list);
   ts[1] = std::thread(test_read_list);
   ts[0].join();
   ts[1].join();
   time(&ed);
   auto vel_mb = (2 * runnum * sizemb) / (ed - st);
   std::cout << "read-mt result: t=" << (ed - st) << " " << vel_mb << "MB/s" << std::endl;
}

// 从同一数据块复制到多个数据块
void test_write_list() {
   std::list<char*> bufs;
   char* mbuf = new char[mb];
   memset(mbuf, 0, mb);
   for (int k = 0; k < sizemb; ++k) {
       char* t = new char[mb];
       memset(t, 0, mb);
       bufs.push_back(t);
   }

   for (int n = 0; n < runnum; ++n) {
       for (auto t : bufs) {
           //memcpy(t, mbuf, mb);
           memmove(t, mbuf, mb);
       }
   }

   delete[] mbuf;
   for (auto t : bufs)
       delete[] t;
}

void test_write_list_mt() {
   std::thread ts[2];
   time_t st, ed;
   time(&st);
   ts[0] = std::thread(test_write_list);
   ts[1] = std::thread(test_write_list);
   ts[0].join();
   ts[1].join();
   time(&ed);
   auto vel_mb = (2 * runnum * sizemb) / (ed - st);
   std::cout << "write-mt result: t=" << (ed - st) << " " << vel_mb << "MB/s" << std::endl;
}

int main()
{
   test_read_list_mt();
   test_write_list_mt();
   return 0;
}

main()里先进行读测试,后面进行写测试,线程个数、数据块大小都完全一致。读写前都使用memset()初始化内存,使操作系统能分配到物理内存。

3 测试结果

1线程测试输出如下。

read result: t=16 12800MB/s
write result: t=14 14628MB/s

过程中cpu使用率如下所示。


读写中cpu使用率曲线

2线程测试输出如下。

read-mt result: t=22 18618MB/s
write-mt result: t=28 14628MB/s

3线程测试输出如下。

read result: t=34 18070MB/s
write result: t=53 11592MB/s

cpu使用率曲线如下所示。


读测试中cpu利用率变化曲线

根据测试结果可以发现几个问题:
1、仅看读速度,2线程比1线程速度快45%,与3线程速度近似;
2、仅看写速度,2线程和1线程速度一样,3线程速度就下降了20%;
3、单线程时写内存更快,而多线程写速度就远不如读内存速度;
4、多线程读的过程中,cpu使用率从100%降到了80%左右,相比之下,单线程的cpu使用率则很平稳;

上述问题应该都是和numa架构有关系,涉及到多核心CPU访问内存的模式,但是这个架构具体怎么导致读写速度差异,这个就留着后面再解决吧。

上一篇 下一篇

猜你喜欢

热点阅读