▶ 第四章,逐步优化了一个三维卷积计算的过程

● 基准代码

 #include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <time.h>
#include <sys/time.h>
#include <omp.h>
#include <assert.h>
#include <sys/mman.h> #define REAL float
#define NX (64) #ifndef M_PI
#define M_PI (3.1415926535897932384626)
#endif // 初始化格点矩阵
void init(REAL *buff, const int nx, const int ny, const int nz, const REAL kx, const REAL ky, const REAL kz,
const REAL dx, const REAL dy, const REAL dz, const REAL kappa, const REAL time)
{
REAL ax = exp(-kappa * time*(kx*kx)), ay = exp(-kappa * time*(ky*ky)), az = exp(-kappa * time*(kz*kz));
for (int jz = ; jz < nz; jz++)
{
for (int jy = ; jy < ny; jy++)
{
for (int jx = ; jx < nx; jx++)
{
int j = (jz * ny + jy) * NX + jx;
REAL x = dx * ((REAL)(jx + 0.5)), y = dy * ((REAL)(jy + 0.5)), z = dz * ((REAL)(jz + 0.5));
buff[j] = (REAL)0.125*(1.0 - ax * cos(kx * x))*(1.0 - ay * cos(ky * y))*(1.0 - az * cos(kz * z));;
}
}
}
} // 计算卷积
void diffusion(REAL *f1, REAL *f2, int nx, int ny, int nz,
REAL ce, REAL cw, REAL cn, REAL cs, REAL ct, REAL cb, REAL cc, REAL dt, int count)
{
for (int i = ; i < count; ++i)
{
for (int z = ; z < nz; z++)
{
for (int y = ; y < ny; y++)
{
for (int x = ; x < nx; x++)
{
int c = (z * ny + y) * NX + x;
int w = (x == ) ? c : c - ;
int e = (x == NX - ) ? c : c + ;
int n = (y == ) ? c : c - NX;
int s = (y == ny - ) ? c : c + NX;
int b = (z == ) ? c : c - NX * ny;
int t = (z == nz - ) ? c : c + NX * ny;
f2[c] = cc * f1[c] + cw * f1[w] + ce * f1[e] + cs * f1[s] + cn * f1[n] + cb * f1[b] + ct * f1[t];
}
}
}
REAL *t = f1;
f1 = f2;
f2 = t;
}
return;
} static double cur_second(void) // 计时器,返回一个秒数
{
struct timeval tv;
gettimeofday(&tv, NULL);
return (double)tv.tv_sec + (double)tv.tv_usec / 1000000.0;
} REAL accuracy(const REAL *b1, REAL *b2, const int len) //计算两个数组的差距
{
REAL err = 0.0;
for (int i = ; i < len; i++)
err += (b1[i] - b2[i]) * (b1[i] - b2[i]);
return (REAL)sqrt(err / len);
} void dump_result(REAL *f, int nx, int ny, int nz, char *out_path) // 将结果写到文件中
{
FILE *out = fopen(out_path, "w");
assert(out);
fwrite(f, sizeof(REAL), nx * ny * nz, out);
fclose(out);
} int main(int argc, char *argv[])
{
int nx = NX, ny = NX, nz = NX;
REAL *f1 = (REAL *)malloc(sizeof(REAL) * NX * NX * NX);
REAL *f2 = (REAL *)malloc(sizeof(REAL) * NX * NX * NX);
REAL *f3 = (REAL *)malloc(sizeof(REAL) * NX * ny * nz);
assert(f1 != MAP_FAILED);
assert(f2 != MAP_FAILED);
assert(f3 != MAP_FAILED); REAL dx, dy, dz, kx, ky, kz;
dx = dy = dz = 1.0 / nx; // 边长 1.0
kx = ky = kz = 2.0 * M_PI;
REAL kappa = 0.1;
REAL dt = 0.1 * dx * dx / kappa;
int count = 0.1 / dt; init(f1, nx, ny, nz, kx, ky, kz, dx, dy, dz, kappa, 0.0); REAL ce, cw, cn, cs, ct, cb, cc;
ce = cw = kappa * dt / (dx * dx);
cn = cs = kappa * dt / (dy * dy);
ct = cb = kappa * dt / (dz * dz);
cc = 1.0 - (ce + cw + cn + cs + ct + cb); printf("Running diffusion kernel %d times\n", count);
fflush(stdout);
struct timeval time_b, time_e;
gettimeofday(&time_b, NULL);
diffusion(f1, f2, nx, ny, nz, ce, cw, cn, cs, ct, cb, cc, dt, count);
gettimeofday(&time_e, NULL);
//dump_result((count % 2) ? f2 : f1, nx, ny, nz, "diffusion_result.dat"); init(f3, nx, ny, nz, kx, ky, kz, dx, dy, dz, kappa, count * dt); // 对比基准结果
REAL err = accuracy((count % ) ? f2 : f1, f3, nx*ny*nz);
double elapsed_time = (time_e.tv_sec - time_b.tv_sec) + (time_e.tv_usec - time_b.tv_usec) * 1.0e-6;
REAL mflops = (nx*ny*nz)*13.0*count / elapsed_time * 1.0e-06;
double thput = (nx * ny * nz) * sizeof(REAL) * 3.0 * count / elapsed_time * 1.0e-09; printf("Elapsed time : %.3f (s)\nFLOPS : %.3f (MFlops)\n", elapsed_time, mflops);
printf("Throughput : %.3f (GB/s)\nAccuracy : %e\n", thput, err); free(f1);
free(f2);
return ;
}

■ 输出结果

Running diffusion kernel  times
Elapsed time : 177.015 (s)
FLOPS : 252.276 (MFlops)
Throughput : 0.233 (GB/s)
Accuracy : 5.068947e-06

● 计算内核加入 OpenMP

 void diffusion(REAL *restrict f1, REAL *restrict f2, int nx, int ny, int nz,
REAL ce, REAL cw, REAL cn, REAL cs, REAL ct, REAL cb, REAL cc, REAL dt, int count)// 加了 restrict
{
#pragma omp parallel // openMP 并行域
{
REAL *f1_t = f1, *f2_t = f2; // 使用局部的指针
for (int i = ; i < count; ++i)
{
#pragma omp for collapse(2) // 展开外两层循环
for (int z = ; z < nz; z++)
{
for (int y = ; y < ny; y++)
{
for (int x = ; x < nx; x++)
{
int c = (z * ny + y) * NX + x;
int w = (x == ) ? c : c - ;
int e = (x == NX - ) ? c : c + ;
int n = (y == ) ? c : c - NX;
int s = (y == ny - ) ? c : c + NX;
int b = (z == ) ? c : c - NX * ny;
int t = (z == nz - ) ? c : c + NX * ny;
f2_t[c] = cc * f1_t[c] + cw * f1_t[w] + ce * f1_t[e] + cs * f1_t[s] + cn * f1_t[n] + cb * f1_t[b] + ct * f1_t[t];
}
}
}
REAL *t = f1_t;
f1_t = f2_t;
f2_t = t;
}
}
return;
}

■ 输出结果

Running diffusion kernel  times
Elapsed time : 2.936 (s)
FLOPS : 15209.439 (MFlops)
Throughput : 14.039 (GB/s)
Accuracy : 4.789139e-06

● 保证向量化

 void diffusion(REAL *restrict f1, REAL *restrict f2, int nx, int ny, int nz,
REAL ce, REAL cw, REAL cn, REAL cs, REAL ct, REAL cb, REAL cc, REAL dt, int count)
{
#pragma omp parallel
{
REAL *f1_t = f1, *f2_t = f2;
for (int i = ; i < count; ++i)
{
#pragma omp for collapse(2)
for (int z = ; z < nz; z++)
{
for (int y = ; y < ny; y++)
{
#pragma simd // 保证向量化,不考虑 f1_t 和 f2_t 之间的独立子性
for (int x = ; x < nx; x++)
{
int c = (z * ny + y) * NX + x;
int w = (x == ) ? c : c - ;
int e = (x == NX - ) ? c : c + ;
int n = (y == ) ? c : c - NX;
int s = (y == ny - ) ? c : c + NX;
int b = (z == ) ? c : c - NX * ny;
int t = (z == nz - ) ? c : c + NX * ny;
f2_t[c] = cc * f1_t[c] + cw * f1_t[w] + ce * f1_t[e] + cs * f1_t[s] + cn * f1_t[n] + cb * f1_t[b] + ct * f1_t[t];
}
}
}
REAL *t = f1_t;
f1_t = f2_t;
f2_t = t;
}
}
return;
}

■ 输出结果

Running diffusion kernel  times
Elapsed time : 0.865 (s)
FLOPS : 51651.863 (MFlops)
Throughput : 47.679 (GB/s)
Accuracy : 4.427611e-06

● 手动剥离边界

 void diffusion(REAL *restrict f1, REAL *restrict f2, int nx, int ny, int nz,
REAL ce, REAL cw, REAL cn, REAL cs, REAL ct, REAL cb, REAL cc, REAL dt, int count)
{
#pragma omp parallel
{
REAL *f1_t = f1, *f2_t = f2;
for (int i = ; i < count; ++i)
{
#pragma omp for collapse(2)
for (int z = ; z < nz; z++)
{
for (int y = ; y < ny; y++)
{
int x = ; // 每行首次
int c = (z * ny + y) * NX + x; // 注意 w 方向的下标是 c
int n = (y == ) ? c : c - NX;
int s = (y == ny - ) ? c : c + NX;
int b = (z == ) ? c : c - NX * ny;
int t = (z == nz - ) ? c : c + NX * ny;
f2_t[c] = cc * f1_t[c] + cw * f1_t[c] + ce * f1_t[c + ] + cs * f1_t[s] + cn * f1_t[n] + cb * f1_t[b] + ct * f1_t[t];
#pragma simd
for (x = ; x < nx - ; x++) // 中间部分,注意循环要按照 OpenMP 格式书写
{
c++;
n++;
s++;
b++;
t++;
f2_t[c] = cc * f1_t[c] + cw * f1_t[c - ] + ce * f1_t[c + ] + cs * f1_t[s] + cn * f1_t[n] + cb * f1_t[b] + ct * f1_t[t];
}
c++; // 每行末次
n++; // 注意 e 方向的下标是 c
s++;
b++;
t++;
f2_t[c] = cc * f1_t[c] + cw * f1_t[c - ] + ce * f1_t[c] + cs * f1_t[s] + cn * f1_t[n] + cb * f1_t[b] + ct * f1_t[t];
}
}
REAL *t = f1_t;
f1_t = f2_t;
f2_t = t;
}
}
return;
}

■ 输出结果

Running diffusion kernel  times
Elapsed time : 0.565 (s)
FLOPS : 79071.250 (MFlops)
Throughput : 72.989 (GB/s)
Accuracy : 4.577150e-06

● 数据切片

 void diffusion(REAL *restrict f1, REAL *restrict f2, int nx, int ny, int nz,
REAL ce, REAL cw, REAL cn, REAL cs, REAL ct, REAL cb, REAL cc, REAL dt, int count)
{
#pragma omp parallel
{
REAL *f1_t = f1, *f2_t = f2;
for (int i = ; i < count; ++i)
{
#define YBF 16 // 分块大小
#pragma omp for collapse(2)
for (int yy = ; yy < ny; yy += YBF) // 在循环之外放入分块
{
for (int z = ; z < nz; z++)
{
int yyy = (yy + YBF) >= ny ? ny : (yy + YBF); // 该分块的末端
for (int y = yy; y < yyy; y++) // y 限定在分块内循环
{
int x = ;
int c = (z * ny + y) * NX + x;
int n = (y == ) ? c : c - NX;
int s = (y == ny - ) ? c : c + NX;
int b = (z == ) ? c : c - NX * ny;
int t = (z == nz - ) ? c : c + NX * ny;
f2_t[c] = cc * f1_t[c] + cw * f1_t[c] + ce * f1_t[c + ] + cs * f1_t[s] + cn * f1_t[n] + cb * f1_t[b] + ct * f1_t[t];
#pragma simd
for (x = ; x < nx - ; x++)
{
c++;
n++;
s++;
b++;
t++;
f2_t[c] = cc * f1_t[c] + cw * f1_t[c - ] + ce * f1_t[c + ] + cs * f1_t[s] + cn * f1_t[n] + cb * f1_t[b] + ct * f1_t[t];
}
c++;
n++;
s++;
b++;
t++;
f2_t[c] = cc * f1_t[c] + cw * f1_t[c - ] + ce * f1_t[c] + cs * f1_t[s] + cn * f1_t[n] + cb * f1_t[b] + ct * f1_t[t];
}
}
}
REAL *t = f1_t;
f1_t = f2_t;
f2_t = t;
}
}
return;
}

■ 输出结果,没有明显优化

Running diffusion kernel  times
Elapsed time : 0.594 (s)
FLOPS : 75224.680 (MFlops)
Throughput : 69.438 (GB/s)
Accuracy : 4.577150e-06

Xeon Phi 《协处理器高性能编程指南》随书代码整理 part 2的更多相关文章

  1. Xeon Phi 《协处理器高性能编程指南》随书代码整理 part 1

    ▶ 第三章,逐步优化了一个二维卷积计算的过程 ● 基准代码 #include <stdio.h> #include <stdlib.h> #include <string ...

  2. Xeon Phi 《协处理器高性能编程指南》随书代码整理 part 4

    ▶ 第五章,几个优化 ● 代码 #include <stdio.h> #include <stdlib.h> #include <math.h> #define S ...

  3. Xeon Phi 《协处理器高性能编程指南》随书代码整理 part 3

    ▶ 第二章,几个简单的程序 ● 代码,单线程 #include <stdio.h> #include <stdlib.h> #include <string.h> ...

  4. Xeon Phi 编程备忘

    ▶ 闲鱼的 Xeon Phi 3120A 配办公室的新 Xeon 服务器,记录一下环境安装过程. ● 原本尝试搭 Ubuntu 服务器,参考[https://software.intel.com/en ...

  5. Python猫荐书系列之五:Python高性能编程

    稍微关心编程语言的使用趋势的人都知道,最近几年,国内最火的两种语言非 Python 与 Go 莫属,于是,隔三差五就会有人问:这两种语言谁更厉害/好找工作/高工资…… 对于编程语言的争论,就是猿界的生 ...

  6. 《高性能javascript》一书要点和延伸(上)

    前些天收到了HTML5中国送来的<高性能javascript>一书,便打算将其做为假期消遣,顺便也写篇文章记录下书中一些要点. 个人觉得本书很值得中低级别的前端朋友阅读,会有很多意想不到的 ...

  7. 高质量C++/C编程指南(林锐)

    推荐-高质量C++/C编程指南(林锐) 版本/状态 作者 参与者 起止日期 备注 V 0.9 草稿文件 林锐   2001-7-1至 2001-7-18 林锐起草 V 1.0 正式文件 林锐   20 ...

  8. 物联网操作系统HelloX应用编程指南

    HelloX操作系统应用编程指南 HelloX应用开发概述 可以通过三种方式,在HelloX操作系统基础上开发应用: 1.        以内部命令方式实现应用,直接编译链接到HelloX的内核she ...

  9. JDK 高性能编程之容器

    高性能编程在对不同场景下对于容器的选择有着非常苛刻的条件,这里记录下前人总结的经验,并对源码进行调试 JDK高性能编程之容器 读书笔记内容部分来源书籍深入理解JVM.互联网等 先放一个类图util,点 ...

随机推荐

  1. JAVA概率实现--一篇最常见、最通用解决方案

    日常场景:某活动抽奖,控制各等奖的出现概率 比如控制A(中奖率):20%:B(获得优惠券率):30%:C(谢谢参与率):50% 下面以封装好在main()函数里,上代码(记得导入相应的包): publ ...

  2. error: checker javascript/jshint: can’t parse version string (abnormal termination?)”

    vim 安装插件(k-vim方法 )好后 编辑js文件提示错误 可能是nodejs环境没搭建好 或者版本有误 用nvm安装node 后 需要 source ~/.bashrc 或者重新开一个终端 再运 ...

  3. python学习 day13 迭代器,生成器,枚举对象

    一.复习 1.闭包:定义在函数内部的函数(被函数嵌套的函数) 2.装饰器:闭包的一个应用场景 -- 为一个函数添加新功能的工具 3.开放封闭原则:不能修改源代码,不能修改调用方式,但可以对外提供增加新 ...

  4. 二、fread与fwrite

    fread 原型:size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream); 参数: ptr:数据存放地址 size:一个对象的 ...

  5. [LeetCode&Python] Problem 744. Find Smallest Letter Greater Than Target

    Given a list of sorted characters letters containing only lowercase letters, and given a target lett ...

  6. linux自启动tomcat

    第一种方式 1.修改脚本文件rc.local:vim /etc/rc.d/rc.local 这个脚本是使用者自定的开机启动程序,可以在里面添加想在系统启动之后执行的脚本或者脚本执行命令 2.添加如下内 ...

  7. 五分钟带你走入MP

    一.MyBatis-Plus简介 1.1MyBatis-Plus是什么? MyBatis-Plus(简称 MP)是一个 MyBatis 的增强工具,在 MyBatis 的基础上只做增强不做改变,为简化 ...

  8. 谈一谈Crsf和XSS攻击

    crsf 和 XSS CRFS攻击全称是一种利用cookie的漏洞进行的一种跨域请求伪造: 攻击者盗用了你的身份,以你的名义发送恶意请求,对服务器来说这个请求是完全合法的,但是却完成了攻击者所期望的一 ...

  9. 【mysql】工具使用

    mysql之workbench如何只导出(insert语句)数据 MySQL 编码:utf8 与 utf8mb4,utf8mb4_unicode_ci 与 utf8mb4_general_ci htt ...

  10. 1.1.19 Word中表格自动断开

    1.修改前效果如下图所示: 2.先右键点击表格的左上角的“被正方形包着的四方箭头”, 如下图中的序号1,在出现的快捷菜单上点击[表格属性],出现[表格属性]对话框. 3.将参数设置成“允许跨页断行”, ...