我在MIC(intel Xeon Phi Coprocessor)上进行手动矢量化,我正在进行简单的计算基准测试(实际上是对CPU与MIC进行基准测试并分析自动与手动的矢量化效果)。我想尝试内在函数的效果。这是我在CPU上的问题,我可以观察到m256内在函数(相对于没有内在函数的CPU)的性能提升30%,但是对于m512的MIC,性能与没有内在函数的MIC相同(OpenMP +内在的),这是正常的吗?
MIC~3.19秒
CPU + INTR~4.31秒
我使用的选项:(英特尔编译器)
编译MIC:-O3 -openmp -restrict
编译CPU +内在函数:-O3 -openmp -DWITH_INTR -no-offload -restrict
我的硬件配置:
代码似乎很长,但这只是因为没有使用内在函数和256位向量和512位向量的计算进行计算。
以及想要重现结果的代码:
#include <stdio.h>
#include <omp.h>
#include <offload.h>
#include <math.h>
#include <immintrin.h>
#define N 2<<17
#define P 2<<14
__declspec(target(mic:0)) void testVctr( double * restrict a, double * restrict b, double * restrict c )
{
double t1( omp_get_wtime() );
omp_set_num_threads(omp_get_max_threads());
__assume_aligned( a, 64 );
__assume_aligned( b, 64 );
__assume_aligned( c, 64 );
int i;
int j;
int k;
#ifdef WITH_INTR
#ifdef __MIC__
__m512d n1 = _mm512_set1_pd( 1. );
__m512d n1024 = _mm512_set1_pd( 1024. );
__m512d n230 = _mm512_set1_pd( 230. );
#else
__m256d n1 = _mm256_set1_pd( 1. );
__m256d n1024 = _mm256_set1_pd( 1024. );
__m256d n230 = _mm256_set1_pd( 230. );
#endif
#endif
#pragma omp parallel for private( i, j, k ) schedule( dynamic )
for( i=0; i<N; ++i )
{
#ifdef WITH_INTR
#ifdef __MIC__
double * restrict A = (double *restrict) _mm_malloc( (size_t)( (8) * sizeof(double) ), 64 );
__m512d res = _mm512_setzero_pd(), r0, r1;
for( j=0; j<P; j+=8 )
{
r0 = _mm512_load_pd( &b[j] );
r0 = _mm512_add_pd( r0, n1 );
r0 = _mm512_div_pd( n1, r0 );
r0 = _mm512_exp_pd( r0 );
r1 = _mm512_load_pd( &c[j] );
r1 = _mm512_mul_pd( r1, n1024 );
r1 = _mm512_add_pd( r1, n230 );
r1 = _mm512_log_pd( r1 );
r0 = _mm512_div_pd( r0, r1 );
res = _mm512_add_pd( res, r0 );
}
_mm512_store_pd( A, res );
double tmp(0.);
for( k=0; k<8; ++k )
tmp += A[k];
a[i] = tmp;
_mm_free( (double * restrict) A );
#else
double * restrict A = (double * restrict) _mm_malloc( (size_t)( (4) * sizeof(double) ), 64 );
__m256d res = _mm256_setzero_pd(), r0, r1;
for( j=0; j<P; j+=4 )
{
r0 = _mm256_load_pd( &b[j] );
r0 = _mm256_add_pd( r0, n1 );
r0 = _mm256_div_pd( n1, r0 );
r0 = _mm256_exp_pd( r0 );
r1 = _mm256_load_pd( &c[j] );
r1 = _mm256_mul_pd( r1, n1024 );
r1 = _mm256_add_pd( r1, n230 );
r1 = _mm256_log_pd( r1 );
r0 = _mm256_div_pd( r0, r1 );
res = _mm256_add_pd( res, r0 );
}
_mm256_store_pd( A, res );
double tmp(0.);
for( k=0; k<4; ++k )
tmp += A[k];
a[i] = tmp;
_mm_free( (double * restrict) A );
#endif
#else
double res = 0.;
#pragma simd
for( j=0; j<P; ++j )
{
double tmp0 = 1./(b[j]+1.);
double tmp1 = exp( tmp0 );
double tmp2 = c[j] * 1024;
double tmp3 = tmp2 + 230;
double tmp4 = log( tmp3 );
double tmp5 = tmp1 / tmp4;
res += tmp5;
}
a[i] = res;
#endif
}
printf("\nElapsed time: %f sec\n", omp_get_wtime() - t1 );
}
int main( void )
{
int i;
printf("\nOuter loop (N) %d iterations \nInner loop (P) %d iterations\n", N, P );
double * restrict a = (double * restrict) _mm_malloc( (size_t)( (N) * sizeof(double) ), 64 );
double * restrict b = (double * restrict) _mm_malloc( (size_t)( (P) * sizeof(double) ), 64 );
double * restrict c = (double * restrict) _mm_malloc( (size_t)( (P) * sizeof(double) ), 64 );
for( i=0; i<P; ++i )
{
b[i] = rand()/RAND_MAX;
c[i] = rand()/RAND_MAX;
}
#pragma offload target( mic : 0 ) \
out( a : length( N ) align(512) ) \
in ( b : length( P ) align(512) ) \
in ( c : length( P ) align(512) )
testVctr( a, b, c );
printf( "\nCheck last result: %f (~ 1.)\n", a[N-1]*2./(P) );
_mm_free( (double * restrict) a );
_mm_free( (double * restrict) b );
_mm_free( (double * restrict) c );
return 0;
}
也许,我错过了代码中的某些内容或编译命令中的一些选项。
我会尝试任何建议。
谢谢。
GS
答案 0 :(得分:2)
你遇到的最大的减速之一就是你在循环的每次迭代中都是_mm_malloc'ing然后_mm_free'ing。堆分配非常慢。你做一个简单的堆栈分配会好得多,即
__declspec( align( 64 ) ) double A[8];
这可能会显着提高您的性能,因为完全删除了动态堆管理。
那说这不是你的主要问题。很可能intel编译器很好地矢量化循环。您应该看一下编译器和内在函数生成的程序集,看看是否“内在地”(抱歉)比另一个更好。如果内在组件看起来更好,那么很可能你看到的大部分时间都被内存访问所占用......