我想使用增强的REP MOVSB(ERMSB)为自定义获得高带宽memcpy.
ERMSB引入了Ivy Bridge微体系结构.如果您不知道ERMSB是什么,请参阅英特尔优化手册中的"增强型REP MOVSB和STOSB操作(ERMSB)" 部分.
我知道直接执行此操作的唯一方法是使用内联汇编.我从https://groups.google.com/forum/#!topic/gnu.gcc.help/-Bmlm_EG_fE获得了以下功能
static inline void *__movsb(void *d, const void *s, size_t n) {
asm volatile ("rep movsb"
: "=D" (d),
"=S" (s),
"=c" (n)
: "0" (d),
"1" (s),
"2" (n)
: "memory");
return d;
}
Run Code Online (Sandbox Code Playgroud)
然而,当我使用它时,带宽远小于memcpy.
使用我的i7-6700HQ(Skylake)系统,Ubuntu 16.10,DDR4 @ 2400 MHz双通道32 GB,GCC 6.2,__movsb获得15 GB/s并memcpy获得26 GB/s.
为什么带宽如此低REP MOVSB?我该怎么做才能改善它?
这是我用来测试它的代码.
//gcc -O3 -march=native -fopenmp foo.c
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include …Run Code Online (Sandbox Code Playgroud) 我正在调查矢量化对程序性能的影响.在这方面,我写了以下代码:
#include <stdio.h>
#include <sys/time.h>
#include <stdlib.h>
#define LEN 10000000
int main(){
struct timeval stTime, endTime;
double* a = (double*)malloc(LEN*sizeof(*a));
double* b = (double*)malloc(LEN*sizeof(*b));
double* c = (double*)malloc(LEN*sizeof(*c));
int k;
for(k = 0; k < LEN; k++){
a[k] = rand();
b[k] = rand();
}
gettimeofday(&stTime, NULL);
for(k = 0; k < LEN; k++)
c[k] = a[k] * b[k];
gettimeofday(&endTime, NULL);
FILE* fh = fopen("dump", "w");
for(k = 0; k < LEN; k++)
fprintf(fh, "c[%d] = %f\t", k, c[k]);
fclose(fh);
double …Run Code Online (Sandbox Code Playgroud) 两个数组的点积
for(int i=0; i<n; i++) {
sum += x[i]*y[i];
}
Run Code Online (Sandbox Code Playgroud)
不重用数据,因此它应该是一个内存绑定操作.因此,我应该能够从点积测量内存带宽.
使用代码 为什么 - 矢量化 - 循环 - 没有性能改进 我的系统带宽为9.3 GB/s.但是,当我尝试使用点积计算带宽时,我获得单个线程的速率的两倍以及使用多个线程的速率超过三倍(我的系统有四个核心/八个超线程).这对我没有意义,因为内存绑定操作不应该受益于多个线程.以下代码的输出如下:
Xeon E5-1620, GCC 4.9.0, Linux kernel 3.13
dot 1 thread: 1.0 GB, sum 191054.81, time 4.98 s, 21.56 GB/s, 5.39 GFLOPS
dot_avx 1 thread 1.0 GB, sum 191043.33, time 5.16 s, 20.79 GB/s, 5.20 GFLOPS
dot_avx 2 threads: 1.0 GB, sum 191045.34, time 3.44 s, 31.24 GB/s, 7.81 GFLOPS
dot_avx 8 threads: 1.0 GB, sum 191043.34, time 3.26 …Run Code Online (Sandbox Code Playgroud) 在R中给出这样的数据帧:
+---+---+
| X | Y |
+---+---+
| 1 | 2 |
| 2 | 4 |
| 4 | 5 |
+---+---+
Run Code Online (Sandbox Code Playgroud)
如果对此数据帧执行矢量化操作,如下所示:
data$Z <- data$X * data$Y
Run Code Online (Sandbox Code Playgroud)
这会利用处理器的单指令多数据(SIMD)功能来优化性能吗?这似乎是一个完美的案例,但我找不到任何证实我的预感的东西.
我有许多紧凑的循环,我正在尝试使用GCC和内在函数进行优化.考虑例如以下功能.
void triad(float *x, float *y, float *z, const int n) {
float k = 3.14159f;
int i;
__m256 k4 = _mm256_set1_ps(k);
for(i=0; i<n; i+=8) {
_mm256_store_ps(&z[i], _mm256_add_ps(_mm256_load_ps(&x[i]), _mm256_mul_ps(k4, _mm256_load_ps(&y[i]))));
}
}
Run Code Online (Sandbox Code Playgroud)
这会产生这样的主循环
20: vmulps ymm0,ymm1,[rsi+rax*1]
25: vaddps ymm0,ymm0,[rdi+rax*1]
2a: vmovaps [rdx+rax*1],ymm0
2f: add rax,0x20
33: cmp rax,rcx
36: jne 20
Run Code Online (Sandbox Code Playgroud)
但是cmp指令是不必要的.而不是具有rax在零开始和结束在sizeof(float)*n我们可以设置基指针(rsi,rdi,和rdx),以阵列的端部,并设置rax到-sizeof(float)*n然后测试为零.我可以用我自己的汇编代码这样做
.L2 vmulps ymm1, ymm2, [rdi+rax]
vaddps ymm0, ymm1, [rsi+rax]
vmovaps [rdx+rax], ymm0 …Run Code Online (Sandbox Code Playgroud) 我正在使用以下处理器 i7 在 MacOS 上进行矢量化基准测试:
$ sysctl -n machdep.cpu.brand_string
Intel(R) Core(TM) i7-4960HQ CPU @ 2.60GHz
Run Code Online (Sandbox Code Playgroud)
我的 MacBook Pro 是 2014 年中期的。
我尝试使用不同的标志选项进行矢量化:我感兴趣的 3 个是 SSE、AVX 和 AVX2。
对于我的基准测试,我将 2 个数组的每个元素相加,并将总和存储在第三个数组中。
我必须让您注意到我正在使用double这些数组的类型。
以下是我的基准代码中使用的函数:
1*) 首先使用 SSE 矢量化:
#ifdef SSE
#include <x86intrin.h>
#define ALIGN 16
void addition_tab(int size, double *a, double *b, double *c)
{
int i;
// Main loop
for (i=size-1; i>=0; i-=2)
{
// Intrinsic SSE syntax
const __m128d x = _mm_load_pd(a); // Load two x elements
const …Run Code Online (Sandbox Code Playgroud) 我测试了以下简单的功能
void mul(double *a, double *b) {
for (int i = 0; i<N; i++) a[i] *= b[i];
}
Run Code Online (Sandbox Code Playgroud)
具有非常大的数组,因此它受内存带宽限制.我使用的测试代码如下.当我用-O2它编译它需要1.7秒.当我用-O2 -mavx它编译它只需要1.0秒.非vex编码的标量操作慢了70%!为什么是这样?
系统:i7-6700HQ@2.60GHz(Skylake)32 GB内存,Ubuntu 16.10,GCC 6.3
测试代码
//gcc -O2 -fopenmp test.c
//or
//gcc -O2 -mavx -fopenmp test.c
#include <string.h>
#include <stdio.h>
#include <x86intrin.h>
#include <omp.h>
#define N 1000000
#define R 1000
void mul(double *a, double *b) {
for (int i = 0; i<N; i++) a[i] …Run Code Online (Sandbox Code Playgroud)