- iOS/Objective-C 元类和类别
- objective-c - -1001 错误,当 NSURLSession 通过 httpproxy 和/etc/hosts
- java - 使用网络类获取 url 地址
- ios - 推送通知中不播放声音
我正在创建一个简单的矩阵乘法程序,在英特尔至强融核架构上运行。
经过多次自动矢量化尝试后,为了获得更好的性能,我不得不使用 Intel Intrinsics。
到目前为止,矩阵大小是由源代码中的#define 给出的,但是当我尝试在运行时给出它时,性能会大幅下降。
源代码如下:
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <math.h>
#include <stddef.h>
#include <chrono>
#include <ctime>
#include <mmintrin.h>
#include <xmmintrin.h> // SSE
#include <pmmintrin.h> // SSE2
#include <emmintrin.h> // SSE3
#include <immintrin.h>
#include <zmmintrin.h>
#define ALIGNMENT 64
#ifndef SIZE
#define SIZE 960
#endif
#define vZero(c) {(c) = _mm512_setzero_pd();}
#define start_time() \
auto start = std::chrono::high_resolution_clock::now();
/** Shows the elapsed time. See start_time for usage*/
#define elapsed_time(STRING) \
auto elapsed = std::chrono::high_resolution_clock::now() - start; \
long long microseconds = std::chrono::duration_cast<std::chrono::microseconds>(elapsed).count(); \
printf(#STRING":%lld\n", microseconds);
void recTranspose(double *__restrict__ a, double *__restrict__ aT, const int n, const int k, const int lda, const int ldat){
if (n*k <= 128) {
for(int i = 0; i < n; i++) {
for(int j = 0; j < k; j++) {
aT[j*ldat+i] = a[i*lda+j];
}
}
//printf("Reached _|_");
return;
}
if(k > n) {
recTranspose(a, aT, n, (k+1)/2, lda, ldat);
recTranspose(&a[(k+1)/2], &aT[(k+1)/2*ldat], n, k-((k+1)/2), lda, ldat);
} else {
recTranspose(a, aT, (n+1)/2, k, lda, ldat);
recTranspose(&a[(n+1)/2*lda], &aT[(n+1)/2], n- (n+1)/2, k, lda, ldat);
}
}
/** Calculates 8 cols and 30 rows of c.*/
inline void eightbythirty(double *__restrict__ a, double *__restrict__ b, double * __restrict__ c, const int size) {
__m512d c0, c1, c2, c3, c4, c5, c6, c7, c8, c9;
__m512d c10, c11, c12, c13, c14, c15, c16, c17, c18, c19;
__m512d c20, c21, c22, c23, c24, c25, c26, c27, c28, c29;
vZero(c0); vZero(c1); vZero(c2); vZero(c3); vZero(c4); vZero(c5);
vZero(c6); vZero(c7); vZero(c8); vZero(c9); vZero(c10); vZero(c11);
vZero(c12); vZero(c13); vZero(c14); vZero(c15); vZero(c16); vZero(c17);
vZero(c18); vZero(c19); vZero(c20); vZero(c21); vZero(c22); vZero(c23);
vZero(c24); vZero(c25); vZero(c26); vZero(c27); vZero(c28); vZero(c29);
__assume_aligned(a, ALIGNMENT);
__assume_aligned(b, ALIGNMENT);
__assume_aligned(c, ALIGNMENT);
__assume(size%16==0);
for(int i = 0; i < size; i++) {
const __m512d bv = _mm512_load_pd(b+i*size);
c0 = _mm512_fmadd_pd(_mm512_extload_pd(a+i*size+0, _MM_UPCONV_PD_NONE, _MM_BROADCAST_1X8, 0), bv, c0);
c1 = _mm512_fmadd_pd(_mm512_extload_pd(a+i*size+1, _MM_UPCONV_PD_NONE, _MM_BROADCAST_1X8, 0), bv, c1);
c2 = _mm512_fmadd_pd(_mm512_extload_pd(a+i*size+2, _MM_UPCONV_PD_NONE, _MM_BROADCAST_1X8, 0), bv, c2);
c3 = _mm512_fmadd_pd(_mm512_extload_pd(a+i*size+3, _MM_UPCONV_PD_NONE, _MM_BROADCAST_1X8, 0), bv, c3);
c4 = _mm512_fmadd_pd(_mm512_extload_pd(a+i*size+4, _MM_UPCONV_PD_NONE, _MM_BROADCAST_1X8, 0), bv, c4);
c5 = _mm512_fmadd_pd(_mm512_extload_pd(a+i*size+5, _MM_UPCONV_PD_NONE, _MM_BROADCAST_1X8, 0), bv, c5);
c6 = _mm512_fmadd_pd(_mm512_extload_pd(a+i*size+6, _MM_UPCONV_PD_NONE, _MM_BROADCAST_1X8, 0), bv, c6);
c7 = _mm512_fmadd_pd(_mm512_extload_pd(a+i*size+7, _MM_UPCONV_PD_NONE, _MM_BROADCAST_1X8, 0), bv, c7);
c8 = _mm512_fmadd_pd(_mm512_extload_pd(a+i*size+8, _MM_UPCONV_PD_NONE, _MM_BROADCAST_1X8, 0), bv, c8);
c9 = _mm512_fmadd_pd(_mm512_extload_pd(a+i*size+9, _MM_UPCONV_PD_NONE, _MM_BROADCAST_1X8, 0), bv, c9);
c10 = _mm512_fmadd_pd(_mm512_extload_pd(a+i*size+10, _MM_UPCONV_PD_NONE, _MM_BROADCAST_1X8, 0),bv, c10);
c11 = _mm512_fmadd_pd(_mm512_extload_pd(a+i*size+11, _MM_UPCONV_PD_NONE, _MM_BROADCAST_1X8, 0),bv, c11);
c12 = _mm512_fmadd_pd(_mm512_extload_pd(a+i*size+12, _MM_UPCONV_PD_NONE, _MM_BROADCAST_1X8, 0), bv, c12);
c13 = _mm512_fmadd_pd(_mm512_extload_pd(a+i*size+13, _MM_UPCONV_PD_NONE, _MM_BROADCAST_1X8, 0), bv, c13);
c14 = _mm512_fmadd_pd(_mm512_extload_pd(a+i*size+14, _MM_UPCONV_PD_NONE, _MM_BROADCAST_1X8, 0), bv, c14);
c15 = _mm512_fmadd_pd(_mm512_extload_pd(a+i*size+15, _MM_UPCONV_PD_NONE, _MM_BROADCAST_1X8, 0), bv, c15);
c16 = _mm512_fmadd_pd(_mm512_extload_pd(a+i*size+16, _MM_UPCONV_PD_NONE, _MM_BROADCAST_1X8, 0), bv, c16);
c17 = _mm512_fmadd_pd(_mm512_extload_pd(a+i*size+17, _MM_UPCONV_PD_NONE, _MM_BROADCAST_1X8, 0), bv, c17);
c18 = _mm512_fmadd_pd(_mm512_extload_pd(a+i*size+18, _MM_UPCONV_PD_NONE, _MM_BROADCAST_1X8, 0), bv, c18);
c19 = _mm512_fmadd_pd(_mm512_extload_pd(a+i*size+19, _MM_UPCONV_PD_NONE, _MM_BROADCAST_1X8, 0), bv, c19);
c20 = _mm512_fmadd_pd(_mm512_extload_pd(a+i*size+20, _MM_UPCONV_PD_NONE, _MM_BROADCAST_1X8, 0), bv, c20);
c21 = _mm512_fmadd_pd(_mm512_extload_pd(a+i*size+21, _MM_UPCONV_PD_NONE, _MM_BROADCAST_1X8, 0), bv, c21);
c22 = _mm512_fmadd_pd(_mm512_extload_pd(a+i*size+22, _MM_UPCONV_PD_NONE, _MM_BROADCAST_1X8, 0), bv, c22);
c23 = _mm512_fmadd_pd(_mm512_extload_pd(a+i*size+23, _MM_UPCONV_PD_NONE, _MM_BROADCAST_1X8, 0), bv, c23);
c24 = _mm512_fmadd_pd(_mm512_extload_pd(a+i*size+24, _MM_UPCONV_PD_NONE, _MM_BROADCAST_1X8, 0), bv, c24);
c25 = _mm512_fmadd_pd(_mm512_extload_pd(a+i*size+25, _MM_UPCONV_PD_NONE, _MM_BROADCAST_1X8, 0), bv, c25);
c26 = _mm512_fmadd_pd(_mm512_extload_pd(a+i*size+26, _MM_UPCONV_PD_NONE, _MM_BROADCAST_1X8, 0), bv, c26);
c27 = _mm512_fmadd_pd(_mm512_extload_pd(a+i*size+27, _MM_UPCONV_PD_NONE, _MM_BROADCAST_1X8, 0), bv, c27);
c28 = _mm512_fmadd_pd(_mm512_extload_pd(a+i*size+28, _MM_UPCONV_PD_NONE, _MM_BROADCAST_1X8, 0), bv, c28);
c29 = _mm512_fmadd_pd(_mm512_extload_pd(a+i*size+29, _MM_UPCONV_PD_NONE, _MM_BROADCAST_1X8, 0), bv, c29);
}
_mm512_storenr_pd(c+0*size, c0);
_mm512_storenr_pd(c+1*size, c1);
_mm512_storenr_pd(c+2*size, c2);
_mm512_storenr_pd(c+3*size, c3);
_mm512_storenr_pd(c+4*size, c4);
_mm512_storenr_pd(c+5*size, c5);
_mm512_storenr_pd(c+6*size, c6);
_mm512_storenr_pd(c+7*size, c7);
_mm512_storenr_pd(c+8*size, c8);
_mm512_storenr_pd(c+9*size, c9);
_mm512_storenr_pd(c+10*size, c10);
_mm512_storenr_pd(c+11*size, c11);
_mm512_storenr_pd(c+12*size, c12);
_mm512_storenr_pd(c+13*size, c13);
_mm512_storenr_pd(c+14*size, c14);
_mm512_storenr_pd(c+15*size, c15);
_mm512_storenr_pd(c+16*size, c16);
_mm512_storenr_pd(c+17*size, c17);
_mm512_storenr_pd(c+18*size, c18);
_mm512_storenr_pd(c+19*size, c19);
_mm512_storenr_pd(c+20*size, c20);
_mm512_storenr_pd(c+21*size, c21);
_mm512_storenr_pd(c+22*size, c22);
_mm512_storenr_pd(c+23*size, c23);
_mm512_storenr_pd(c+24*size, c24);
_mm512_storenr_pd(c+25*size, c25);
_mm512_storenr_pd(c+26*size, c26);
_mm512_storenr_pd(c+27*size, c27);
_mm512_storenr_pd(c+28*size, c28);
_mm512_storenr_pd(c+29*size, c29);
}
int main(int argc, const char ** argv) {
#ifdef SIZES
const int size = SIZE;
#else
const int size = atoi(argv[1]);
#endif
void* p = malloc((sizeof(double)*5*size*size) + ALIGNMENT-1);
double *__restrict__ a = (double*)(((size_t)p + ALIGNMENT-1) / ALIGNMENT * ALIGNMENT);
double *__restrict__ aT = (double*) a+size*size;
double *__restrict__ b = aT+size*size;
double *__restrict__ c = b+size*size;
double *__restrict__ d = c+size*size;
srand(time(NULL));
for(int i = 0; i < size; i++) {
for(int j = 0; j < size; j++) {
a[i*size+j] = (double) (rand()%20);
}
for(int j2=0; j2<size; j2++){
c[i*size+j2] = 0.0;
}
}
for(int i = 0; i < size; i++) {
for(int j = 0; j < size; j++) {
b[i*size+j] = (double) (rand()%20);
}
}
start_time();
recTranspose(a, aT, size, size, size, size);
for(int i = 0; i < size; i+=30) {
for(int j = 0; j < size; j+=8) {
eightbythirty(&aT[i], &b[j], &c[i*size+j], size);
}
}
elapsed_time();
double gflops = 2.0*size*size*size*1.0e-03/(microseconds);
printf("Gflops: %f\n", gflops);
for(int i = 0; i < size; i++) {
for(int j = 0; j < size; j++) {
double s = 0;
for(int u = 0; u < size; u++) {
s += a[i*size+u] * b[u*size+j];
}
d[i*size+j] = s;
}
}
int error = 0;
for(int i = 0; i < size; i++) {
for(int j = 0; j < size; j++) {
if(abs(c[i*size+j] - d[i*size+j]) > 1) {
printf("Error at %d %d , %f instead of %f\n", i, j, c[i*size+j], d[i*size+j]);
error++;
if(error > 16) return 0;
}
}
}
printf("OK\n");
}
例如,尺寸为 960(目前它只适用于尺寸为 30*8 的倍数):
如果我使用给定大小的编译时间进行编译:icc -mmic -O3 -restrict -std=c++11 -DSIZES -DSIZE=960 mmul.cpp -o mmul.o
耗时:0.460745 秒Gflops:3.840458
如果我使用给定大小的运行时编译:icc -mmic -O3 -restrict -std=c++11 mmul.cpp -o mmul.o
耗时:2.204564sGflops:0.802640
我认为这可能是 icc 无法识别内存访问模式的预取问题。查看生成的 asm 源代码,“编译时”版本中 vprefetch 指令的数量要多得多。
有趣的事实:检查正确的乘法结果(代码末尾的两个 for 循环,第 178-197 行)在编译时版本中要慢得多!
有什么想法吗?我尝试了 #pragma loop_count 但它似乎没用,而且手动内部预取似乎也不是很有效。
提前感谢您的回答。
问候,卢卡
最佳答案
计算机科学的基本定理指出,任何问题都可以通过另一层间接来解决。
想法是将代码保留为固定大小的循环,并编写代码以分派(dispatch)到正确的固定大小循环。
第一次更改 eightbythirty
像这样阅读:
template<int size>
inline void eightbythirty(double *__restrict__ a, double *__restrict__ b, double * __restrict__ c) {
内部有相同的实现。你可以把它放在 namespace details
中因为它通常不面向用户。
接下来,包装它:
inline void eightbythirty(double *__restrict__ a, double *__restrict__ b, double * __restrict__ c, const int size_divided_by_240) {
int size=size_divided_by_240;
switch( size&7 ) {
case 0: break;
case 01: eightbythirty<01>(a,b,c); break;
case 02: eightbythirty<02>(a,b,c); break;
case 03: eightbythirty<03>(a,b,c); break;
case 04: eightbythirty<04>(a,b,c); break;
case 05: eightbythirty<05>(a,b,c); break;
case 06: eightbythirty<06>(a,b,c); break;
case 07: eightbythirty<07>(a,b,c); break;
}
a+=(size&7)*8*30;
b+=(size&7)*8*30;
c+=(size&7)*8*30;
switch( (size>>3)&7 ) {
case 0: break;
case 01: eightbythirty<1*8>(a,b,c); break;
case 02: eightbythirty<2*8>(a,b,c); break;
case 03: eightbythirty<3*8>(a,b,c); break;
case 04: eightbythirty<4*8>(a,b,c); break;
case 05: eightbythirty<5*8>(a,b,c); break;
case 06: eightbythirty<6*8>(a,b,c); break;
case 07: eightbythirty<7*8>(a,b,c); break;
}
a += (size&(7<<3))*8*30;
b += (size&(7<<3))*8*30;
c += (size&(7<<3))*8*30;
switch( (size>>6)&7 ) {
case 0: break;
case 01: eightbythirty<1*8*8>(a,b,c); break;
case 02: eightbythirty<2*8*8>(a,b,c); break;
case 03: eightbythirty<3*8*8>(a,b,c); break;
case 04: eightbythirty<4*8*8>(a,b,c); break;
case 05: eightbythirty<5*8*8>(a,b,c); break;
case 06: eightbythirty<6*8*8>(a,b,c); break;
case 07: eightbythirty<7*8*8>(a,b,c); break;
default:
}
a += (size&(7<<6))*8*30;
b += (size&(7<<6))*8*30;
c += (size&(7<<6))*8*30;
int steps = size/8/8/8;
for( int i = 0; i < steps; ++i ) {
eightbythirty<512>(a+512*i, b+512*i, c+512*i);
}
}
这会将您的输入大小分成 3 位 block 。然后它调用固定大小的实现。上面代码中出现了4个分支,其中大部分是简单的跳表,针对小于512*8*30的值。对于大于该值的值,主要以 512*8*30 的 block 来完成。
7*3+1 = 原始函数的 22 个实现被实现,每个都有一个常量 size
, 因此编译器可以充分优化它们。
这通常可以通过元编程来完成,但不值得一次性使用。
我可能遗漏了一些 *(8*30)
在上面的代码中,当我调用 <int size>
时eightbythirty
的版本.
关于c++ - 如果在 Xeon Phi 上编译时不知道循环计数,性能会下降,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/26740384/
我正在开发一个系统,用 C++ 编写,在 Linux 上的 Xeon 上运行,它需要尽可能快地运行。 RAM 中有一个超过 10 GB 的大型数据结构(基本上是一个结构数组),其中的元素需要定期访问。
我想获取 xeon phi 上字符串的所有子字符串 首先,我从 args 读取一个 txt 文件并将其存储到这样的指针数组中 char *temp_string[N_ELEMENT]; 其次,我想使用
我正在通过 Stampede 使用 Xeon Phi 解决 Collatz 猜想问题。我已经测试过我的代码,对于高达 100,000 的值可以正常工作,但是测试高达 100 万的值时,我几乎立即收
我正在使用带卸载功能的 cilk plus 在 Xeon phi 上进行一些性能测试。 在一个简单的 vector 加法程序中,我有两种方法: 使用 cilk_for 将任务拆分到 Xeon phi
我有两台服务器,一台运行一个 E3-1220 处理器,另一台运行两个 E5-2640 处理器。 http://ark.intel.com/compare/52269,64591 问题是两个处理器服务器
考虑购买一对 Xeon Phi 5110P,但试图估计我需要更改多少代码或需要其他软件。 目前我善用R在多核 Windows 机器(24 核)上使用 foreach包,传递给其他包forecast ,
在 Nvidia GPU 上,我们可以通过使用 Streams 让多个内核同时运行。至强 Phi 怎么样?如果我通过不同的线程卸载两部分计算代码,它们会在至强融核上并发运行吗? 最佳答案 是的,您可以
我试图在两种算法之间做出决定。一个将 8 个字节(两个对齐的 4 字节字)写入 2 个缓存线,另一个写入 3 个完整的缓存线。 如果 CPU 仅将更改后的 8 字节写回内存,则第一种算法使用的内存带宽
我们一直在使用两个完全相同的软件(Centos 7 OS和BIOS设置)使用相同的Skylake服务器。除延迟性能外,其他所有内容都相同。我们的软件正在使用AVX512。 在测试中,我注意到AVX51
从这里https://software.intel.com/en-us/videos/purpose-of-the-mic-architecture我了解具有复杂或大量随机内存访问的应用程序不太适合
我正在创建一个简单的矩阵乘法程序,在 Intel Xeon Phi 架构上运行。该程序如下所示(参数为 A、B、C),时序不包括初始化: //start timing for(int i = 0; i
假设我有一个在主机上运行的 c/c++ 应用程序。主机 CPU 上运行的线程很少,Xeon Phi 内核上运行的线程有 50 个。 我如何确保这 50 个中的每一个都在其自己的 Xeon Phi 核心
我正在尝试使用 perf 工具来测量某些程序的性能。由于某种原因 perf stat 不支持硬件缓存事件。我使用的是英特尔至强 e5-2620 (haswell) 处理器。我在一些论坛上读到,该 cp
我想在用户空间中使用 shell 脚本读取英特尔至强的性能计数器。 Oprofile 无法工作,因为它太死板,无法满足我的要求。我正在使用 FC13。谢谢 最佳答案 Perf 将允许您选择所需的计数器
我正在 Intel Xeon® Phi® 上实现超快的 popcount,因为它是各种生物信息学软件的性能热点。 我已经实现了五段代码, #if defined(__MIC__) #include
我有一个 struct A,其中包含一些 int 和一个 int * 成员。我如何在卸载中使用它? 我可能无法执行#pragma offload target(mic: 0) inout(A){}..
如果我在 512 宽 SIMD 向量中有以下 double ,如在 Xeon Phi 寄存器中: m0 = |b4|a4|b3|a3|b2|a2|b1|a1| 有没有可能变成: m0_d = |a4|
我知道 Intel Xeon phi 协处理器 SE10X 有 61 个内核建议仅使用 60 个核心,因为 1 个核心用于卸载守护程序。另外,由于intel xeon phi协处理器5110P有60个
如何编译虚拟机并在 Intel Xeon Phi 上运行 Erlang 程序协处理器? 最佳答案 Intel Xeon Phi 不是典型的 x86_64 架构,因此不可能在其上运行官方的 Erlang
有人告诉我可以在 MIC 上运行一个用 gcc 构建的程序。 这是真的吗? 如果是,如何进行? 我使用的是 gcc 4.4.7 版。 最佳答案 英特尔至强融核确实可以运行使用 gcc 交叉编译器编译的
我是一名优秀的程序员,十分优秀!