高性能计算-openmp-多线程缓存一致性(9)
1. 背景介绍
L1 L2 cache是单核独享,L3是多核共享。如果多线程访问共享一维数组的连续元素,先读入第一个线程的L1 缓存中,其他线程访问缓存不命中需要加载,并且数据的更改后,标记为脏数据,其他线程访问cacheline中相邻地址需要先写回内存,再读入目标L1 cache,效率低。使用三份代码,测试多线程计算效率。
2. 测试:定积分划分矩形求π,迭代次数越高精度越高。定积分如下:
∫ 0 1 4 1 + x 2 d x
2.1一维数组测试代码:
// 目标:openmp 定积分划分矩形求π,多线程交替计算,使用一维数组sumH,
// 缺点:多线程会读写数组,缓存不一致,性能下降
#include <stdio.h>
#include <omp.h>
#include <string.h>
#include <sys/time.h>
#define NT 16 //线程数量
#define N 50000000 //划分区间个数
void main()
{
struct timeval start,end;
float time;
double pi=0;
double sumH[NT]; //存储各个线程矩形的高之和
memset(sumH,0,sizeof(double)*NT);
double step = 1.0/(double)(N); //划分区间的宽度
// 设置线程数量,根据线程id交替计算矩形长度之和
omp_set_num_threads(NT);
gettimeofday(&start,NULL); //开始时间
#pragma omp parallel
{
double x=0; //x 坐标
int tid = omp_get_thread_num(); //线程id
int nts = omp_get_num_threads();//获取线程总数
for(int i=tid;i<N;i+=nts)
{
x = (i+0.5)*step; //取划分矩形的中点函数值
sumH[tid] += 4.0/(1+x*x);
}
}
for(int i=0;i<NT;i++)
pi += sumH[i];
pi = step * pi;
gettimeofday(&end,NULL);
time = end.tv_sec-start.tv_sec+(end.tv_usec-start.tv_usec)/1e6;
printf("ths %d useSec %f pi %18.15f\n",NT,time,pi);
return;
}
2.2二维数组测试代码:查询 L1cache line 大小。将数组变为二维数组,每个cache line对应一个元素并0填充,每个线程访问一个 cache line。
// 目标:openmp 定积分划分矩形求π,多线程交替计算,使用一维数组sumH,
// 使用二维数组填充cacheline
#include <stdio.h>
#include <omp.h>
#include <string.h>
#include <sys/time.h>
#define NT 16 //线程数量
#define N 50000000 //划分区间个数
void main()
{
struct timeval start,end;
float time;
double pi=0;
double sumH[NT][8]; //存储各个线程矩形的高之和,使用二维数组每行存储到一个cacheline
memset(sumH,0,sizeof(double)*NT*8);
double step = 1.0/(double)(N); //划分区间的宽度
// 设置线程数量,根据线程id交替计算矩形长度之和
omp_set_num_threads(NT);
gettimeofday(&start,NULL); //开始时间
#pragma omp parallel
{
double x=0; //x 坐标
int tid = omp_get_thread_num(); //线程id
int nts = omp_get_num_threads();//获取线程总数
for(int i=tid;i<N;i+=nts)
{
x = (i+0.5)*step; //取划分矩形的中点函数值
sumH[tid][0] += 4.0/(1+x*x);
}
}
for(int i=0;i<NT;i++)
pi += sumH[i][0];
pi = step * pi;
gettimeofday(&end,NULL);
time = end.tv_sec-start.tv_sec+(end.tv_usec-start.tv_usec)/1e6;
printf("ths %d useSec %f pi %18.15f\n",NT,time,pi);
return;
}
2.3使用私有变量:多线程使用局部变量,最后再赋值给共享数组的元素,减少每个线程对共享变量数组的访问次数。
// 目标:openmp 定积分划分矩形求π,多线程交替计算,使用一维数组sumH,
// 使用局部变量
#include <stdio.h>
#include <omp.h>
#include <string.h>
#include <sys/time.h>
#define NT 16 //线程数量
#define N 50000000 //划分区间个数
void main()
{
struct timeval start,end;
float time;
double pi=0;
double sumH[NT]; //存储各个线程矩形的高之和
memset(sumH,0,sizeof(double)*NT);
double step = 1.0/(double)(N); //划分区间的宽度
// 设置线程数量,根据线程id交替计算矩形长度之和
omp_set_num_threads(NT);
gettimeofday(&start,NULL); //开始时间
#pragma omp parallel
{
double sum=0; //使用局部变量
double x=0; //x 坐标
int tid = omp_get_thread_num(); //线程id
int nts = omp_get_num_threads();//获取线程总数
for(int i=tid;i<N;i+=nts)
{
x = (i+0.5)*step; //取划分矩形的中点函数值
sum += 4.0/(1+x*x);
}
sumH[tid] = sum;
}
for(int i=0;i<NT;i++)
pi += sumH[i];
pi = step * pi;
gettimeofday(&end,NULL);
time = end.tv_sec-start.tv_sec+(end.tv_usec-start.tv_usec)/1e6;
printf("ths %d useSec %f pi %18.15f\n",NT,time,pi);
return;
}
3. 测试数据
3.1 耗时:单位为秒,每组参数测试四次取均值
| 线程数 | 1 | 2 | 4 | 8 | 16 |
|---|---|---|---|---|---|
| 访问共享一维数组元素 | 0.59455275 | 0.83000775 | 0.55785925 | 0.08166325 | 0.11506225 |
| 数组元素填充为二维,每个元素放在一个cacheline大小的一维数组中 | 0.61837 | 0.54942225 | 0.37650275 | 0.19830225 | 0.09455275 |
| 使用线程私有局部变量 | 0.4499215 | 0.22541475 | 0.113015 | 0.0571045 | 0.037793 |
3.2 加速比
| 线程数 | 1 | 2 | 4 | 8 | 16 |
|---|---|---|---|---|---|
| 访问共享一维数组元素 | 0.716321926 | 1.065775552 | 7.280542349 | 5.167226871 | |
| 数组元素填充为二维,每个元素放在一个cacheline大小的一维数组中 | 1.125491368 | 1.642405002 | 3.118320644 | 6.539947278 | |
| 使用线程私有局部变量 | 1.99597187 | 3.981077733 | 7.878914972 | 11.90488979 |
3.3 并行效率
| 线程数 | 1 | 2 | 4 | 8 | 16 |
|---|---|---|---|---|---|
| 访问共享一维数组元素 | 0.358160963 | 0.266443888 | 0.910067794 | 0.322951679 | |
| 数组元素填充为二维,每个元素放在一个cacheline大小的一维数组中 | 0.562745684 | 0.410601251 | 0.389790081 | 0.408746705 | |
| 使用线程私有局部变量 | 0.997985935 | 0.995269433 | 0.984864371 | 0.744055612 |
4. 数据分析
a. 随着线程数的增加除了原始代码效率会降低,优化代码效率均有明显提升,使用线程私有变量的效率最高。
b. 加速比并不会随着线程数的增加而线性增加。
c. 随着线程数量的增加,并行效率的变化是剧烈波动的。
高性能计算-openmp-多线程缓存一致性(9)的更多相关文章
- Java的多线程机制系列:(二)缓存一致性和CAS
一.总线锁定和缓存一致性 这是两个操作系统层面的概念.随着多核时代的到来,并发操作已经成了很正常的现象,操作系统必须要有一些机制和原语,以保证某些基本操作的原子性.首先处理器需要保证读一个字节或写一个 ...
- redis,缓存雪崩,粗粒度锁,缓存一致性
1, redis单线程为什么快 io多路复用技术 单线程避免多线程的频繁切换问题 memcache缺点 kv形式数据 没有持久化mongodb 海量数据的访问效率 mr的计算模型文档就是类似json的 ...
- 缓存一致性协议 mesi
m : modified e : exlusive s : shared i : invalid 四种状态的转换略过,现在讨论为什么有了这个协议,i++在多线程上还不是安全的. 两个cpu A B同时 ...
- C和C++中的volatile、内存屏障和CPU缓存一致性协议MESI
目录 1. 前言2 2. 结论2 3. volatile应用场景3 4. 内存屏障(Memory Barrier)4 5. setjmp和longjmp4 1) 结果1(非优化编译:g++ -g -o ...
- java并发编程(三)cpu cache & 缓存一致性
一 cpu cache 1. cache的意义 为什么需要CPU cache?因为CPU的频率太快了,快到主存跟不上,这样在处理器时钟周期内,CPU常常需要等待主存,浪费资源.所以cache的出 ...
- 缓存一致性协议(MESI)
在目前主流的计算机中,cpu执行计算的主要流程如图所示: 数据加载的流程如下: 1.将程序和数据从硬盘加载到内存中 2.将程序和数据从内存加载到缓存中(目前多三级缓存,数据加载顺序:L3->L2 ...
- Volatile如何保证线程可见性之总线锁、缓存一致性协议
基础知识回顾 下图给出了假想机的基本设计.中央处理单元(CPU)是进行算术和逻辑操作的部件,包含了有限数量的存储位置--寄存器(register),一个高频时钟.一个控制单元和一个算术逻辑单元. 时钟 ...
- 【Java虚拟机4】Java内存模型(硬件层面的并发优化基础知识--缓存一致性问题)
前言 今天学习了Java内存模型第一课的视频,讲了硬件层面的知识,还是和大学时一样,醍醐灌顶.老师讲得太好了. Java内存模型,感觉以前学得比较抽象.很繁杂,抽象. 这次试着系统一点跟着2个老师学习 ...
- 简述伪共享和缓存一致性MESI
什么是伪共享 计算机系统中为了解决主内存与CPU运行速度的差距,在CPU与主内存之间添加了一级或者多级高速缓冲存储器(Cache),这个Cache一般是集成到CPU内部的,所以也叫 CPU Cache ...
- 缓存一致性性协议MESI笔记
概述 今天的笔记只是讲解一下MESI的概念和使用场景的介绍,MESI(Modified Exclusive Shared Or Invalid)也称为伊利诺斯协议,是一种广泛使用的支持协会策略的缓存一 ...
随机推荐
- 二. Spring Boot 中的 “依赖管理和自动配置” 详解透彻到底(附+详细代码流程)
二. Spring Boot 中的 "依赖管理和自动配置" 详解透彻到底(附+详细代码流程) @ 目录 二. Spring Boot 中的 "依赖管理和自动配置" ...
- 浅谈下javascript的proxy和reflect
近日喜欢上了uniapp和vue,但看到相关程序代码中频繁出现了proxy和reflect的使用,于是进行了一番学习,现总结如下. Proxy和Reflect是ES6(ECMAScript 2015) ...
- 【YashanDB知识库】数据库使用shutdown immediate无响应导致coredump
[标题]数据库使用shutdown immediate无响应导致coredump [问题分类]数据库维护 [关键词]YashanDB, shutdown immediate, coredump [问题 ...
- 示例python 批量操作excel统计销售榜品牌及销售额
示例统计销售榜品牌及销售额 import pandas as pd import numpy as np import os os.chdir('F:\\50mat\源数据1000张表格') name ...
- Facebook Ads – 笔记
前言 记入一些小东西 参考 YouTube – 这是第一次广告投放回报做到11倍!Facebook广告高广告投资回报2023年终极策略密码分享 价值阶梯 先卖便宜 value 低的东西给客户,甚至免费 ...
- CSS – PostCSS
前言 我第一次接触 PostCSS 是在学 Tailwind CSS 的时候. 它类似 JavaScript 的 Babel. 我没有用过 Babel, 因为 TypeScript 用的早. Post ...
- 课时05:Linux必备系统命令
- LeetCode 1316. Distinct Echo Substrings (RK哈希)
题意: 给一个字符串 寻找字符串为(a+a)格式的子串有多少.a+a 格式字符串比如 abcabc, ee 等. 首先O(N^2)枚举子串,然后通过哈希在O(1)复杂度判断子串是否符合要求. RK哈希 ...
- 3个步骤轻松集成Push Kit,实现App消息推送
推送通知作为App重要的消息传递工具,广泛应用于电子商务.社交媒体.旅游交通等领域的通知场景.比如当应用有新功能或安全补丁时,系统将推送消息提醒用户及时更新:如果是航班出行类的应用,会发送最新的班次时 ...
- 墨天轮沙龙 | SphereEx代野:Apache ShardingSphere-从中间件到分布式生态演进之路
在9月22日举办的[墨天轮数据库沙龙第十期-国产中间件专场]中,SphereEx 解决方案专家 代野分享了Apache ShardingSphere:从中间件到分布式生态演进之路>主题演讲,本文 ...