我在MIC(intel Xeon Phi协处理器)上进行手动矢量化,我在做一个简单的计算基准(实际上是对CPU和MIC进行基准测试,并分析矢量化效果auto和manual)。我想试试内在的效果。这里是我在CPU上的问题,我可以观察到m256内部函数的性能提高了30%(与没有内部函数的CPU相比),但是在m512的MIC上,性能与没有内部函数的MIC(OpenMP+内部函数)相同,这正常吗?
MIC+INTR~3.18秒
麦克风~3.19秒
CPU+INTR~4.31秒
CPU~6.47秒
我使用的选项:(英特尔编译器)
要编译为MIC+内在的:-O3-openmp-DWITH_INTR-restrict
编译MIC:-O3-openmp-restrict
为CPU+内部编译:-O3-openmp-DWITH_INTR-no offload-restrict
为CPU编译:-O3-openmp-no offload-restrict
我的硬件配置:
CPU:Intel(R)Xeon(R)CPU E5-2680 [email protected] | SandyBridge(2x8核| 32线程)
MIC:Intel(R)Xeon Phi(TM)协处理器x100系列(61核244线程)
代码看起来很长,但这仅仅是因为没有使用内部函数的计算,以及使用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公司

最佳答案

在循环的每一次迭代中,你最大的一个慢下来的地方就是你在“malloc ing”然后“free”了。堆分配非常缓慢。你最好做一个简单的堆栈分配,即。

__declspec( align( 64 ) ) double A[8];

当动态堆管理被完全删除时,这可能会显著提高性能。
也就是说这不是你的主要问题。英特尔编译器很可能很好地对循环进行了矢量化。您应该看看编译器和内部函数生成的程序集,看看其中一个是否比另一个好。如果intrinsics程序集看起来更好,那么很可能您看到的大部分时间都被内存访问占用了。。。

关于c - 性能AVX-512与MIC上的自动矢量化(英特尔至强融核协处理器),我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/27945345/

10-09 02:45