美文网首页
关于page cache的性能实验数据

关于page cache的性能实验数据

作者: 谭英智 | 来源:发表于2022-11-25 19:19 被阅读0次

page cache局部性

行优先VS列优先

代码

int array[n][n];

//行优先
for (int i = 0; i < n; ++i)
  for (int j = 0; j < n; ++j)
    array[i][j] += j;

//列优先
for (int i = 0; i < n; ++i)
  for (int j = 0; j < n; ++j)
    array[j][i] += j;

//乱序访问
for (int i = 0; i < n; ++i)
  for (int j = 0; j < n; ++j)
    array[rand()%n][rand()%n] += j;

行优先

row_tra

列优先

col_tra

乱序访问(链表)

list_tra

性能差异

performance

分析结论

L1 cache的cache line大小是64 bytes = 64 * 8 bits。每次cpu加载一个变量时,会一次性的加载一个cache line大小的内存到L1 cache。

行优先遍历数组,由于每次加载的cache line数据都能命中缓存,因此效率高

而列优先遍历,由于每次读完L1 cache变量后,读下一个变量有可能发现cache miss,需要重新从内存中加载,导致消息低

乱序访问是每次读去都会cache miss

访问数据的顺序

当我们有三个变量,cpu访问的顺序有可能有下面两种

var_cross
var_order

第一种是不断的轮训访问三个变量,但是当再次访问变量时,有可能cache就会被踢走,需要重新加载变量到cache中。

比较高效的方法是,有序的访问三个变量,这样就能一直保持变量在cache中,直到处理完为止

伪共享与对其cache line

多线程共享一个原子变量

代码

void work(std::atomic<int>& a) {
  for(int i = 0; i < 100000; ++i) {
    a++;
  }
}
void directSharing() {
  // Create a single atomic integer
  std::atomic<int> a;
  a = 0;

  // Four threads all sharing one atomic<int>
  std::thread t1([&]() {work(a)});
  std::thread t2([&]() {work(a)});
  std::thread t3([&]() {work(a)});
  std::thread t4([&]() {work(a)});

  // Join the 4 threads
  t1.join();
  t2.join();
  t3.join();
  t4.join();
}

性能

share_one_perf

可以看到随着线程数的增加,性能会出现下降

原子变量在不同core之间的流转过程

初始状态

cache_1

Core1 core2读取原子变量a,并加载到cache

cache_2

Core1 修改a变量,并另core2的变量失效

cache_3

core2重新加载a变量

cache_4

分析与结论

原子变量在不同core之间,由于需要不断的令cache失效,又重新加载修改,造成随着分享的core越多,开销也越大

多线程发生伪共享

代码

void work(std::atomic<int>& a) {
  for(int i = 0; i < 100000; ++i) {
    a++;
  }
}
void falseSharing() {
  // Create a single atomic integer
  std::atomic<int> a;
  a = 0;
  std::atomic<int> b;
  b = 0;
  std::atomic<int> c;
  c = 0;
  std::atomic<int> d;
  d = 0;

  // Four threads each with their own atomic<int>
  std::thread t1([&]() {work(a)});
  std::thread t2([&]() {work(b)});
  std::thread t3([&]() {work(c)});
  std::thread t4([&]() {work(d)});

  // Join the 4 threads
  t1.join();
  t2.join();
  t3.join();
  t4.join();
} 

性能

false_share

可以看到效果跟共享一个原子变量没有什么差异

分析与结论

这是因为变量的失效是以cache line为单位的,

变量a b c d,他们都位于同一个cache linezhong,所以只要有一个失效,其他变量也跟着失效

解决

对齐cache line

struct alignas(64) AlignedType {
  AlignedType() { val = 0; }
  std::atomic<int> val;
};
void noSharing() {
  AlignedType a{};
  AlignedType b{};
  AlignedType c{};
  AlignedType d{};
 
  // Launch the four threads now using our aligned data
  std::thread t1([&]() { work(a.val); });
  std::thread t2([&]() { work(b.val); });
  std::thread t3([&]() { work(c.val); });
  std::thread t4([&]() { work(d.val); });
 
  // Join the threads
  t1.join();
  t2.join();
  t3.join();
  t4.join();
}

性能对比如下

no_share

stl库性能分析

Vector , List, deque性能对比

尾插入

fill_back

线性搜索

search

析够

destrory

分析与结论

可以看到list由于元素内存的不连续性,导致小对象不能很好的利用page cache,导致性能比不上vecotr之类的容器

list对大对象性能是可观的,因为元素足够大,完全占满了cache line;

在我们使用的过程中,考虑到一般我们都是使用小对象为主,为了更好的利用page cache,应该避免使用链表,rb-tree, unordered_map的容器

寻求对缓存更友好的容器来做高性能开发

相关文章

  • Page Cache

    Page Cache是通过将磁盘中的数据缓存到内存中,减少磁盘I/O操作,从而提高性能。此外,还要确保Page C...

  • kafka读写高效的总结

    写入: 顺序写,性能要远远优于散列写 使用了操作系统的page cache 读取: 顺序读,kafka的数据本身就...

  • Rocket MQ

    参考万亿级数据洪峰下的分布式消息引擎 - 简书 Page Cache Page Cache是文件的缓存,用于加速对...

  • The Page Cache and Page Writebac

    page cache是kernel实现的disk cache, 这是为了减少磁盘I/O。page writebac...

  • write()函数

    IO写入的基本过程a. 定位用户数据b. 将用户数据拷贝至内核中(page cache) 先解释一下page ca...

  • Kafka专题:1.kafka高性能的原因

    高性能包含两个方向写的高性能1)顺序写+page cache2)生产者批量发送消息集,压缩:生产者并不直接将消息发...

  • Page Cache

    1、简洁Page Cache 存在于内核,目的是为了减少 I/O,提升应用的 I/O 速度。 2、速度对比生成一个...

  • Page Cache

    容器里面乾坤大 我想说的是 cgroup 可以限制cpu, memory, network 等等, 但是 page...

  • Page Claim

    Linux page包括分配给进程的Page及 cache Page, 记得很早之前看过vm.swappiness...

  • 【CentOS】关于CentOS7.x free内存查看

    # free -h free里的cache/buffer就是page cache, 早期Linux文件相关的cac...

网友评论

      本文标题:关于page cache的性能实验数据

      本文链接:https://www.haomeiwen.com/subject/ncfqfdtx.html