インテル® Xeon Phi™ プロセッサー/コプロセッサーの実行性能の比較

同カテゴリーの次の記事

インテル Parallel Universe 25 号日本語版の公開

インテル® Xeon Phi™ プロセッサーと既存のインテル® Xeon® プロセッサーおよびインテル® Xeon Phi™ コロセッサーとの実行性能の比較

概要

この記事は、インテル® Xeon Phi™ コプロセッサー (Knights Corner – KNC) とインテル® Xeon® プロセッサーを搭載したシステムとインテル® Xeon Phi™ プロセッサー (Knights Landing – KNL) を搭載したシステムを使用して KNL の性能を比較した際の検証内容を紹介しています。

いままでのインテル® Xeon Phi™ コプロセッサーでは、PCI-Express 転送によるボトルネックや、搭載メモリー (最大 16GB) などのハードウェアの制約により、大規模な配列を効果的に処理させるためには工夫が必要とされてきました。

KNL は KNC と異なり、汎用 OS をブート可能なホストプロセッサーとして動作します。これにより、インテル® Xeon® プロセッサーやインテル® Core™ プロセッサー上で動作しているアプリケーションをインテル® MIC アーキテクチャー向けに再コンパイルする必要なく、インテル® Xeon Phi™ プロセッサー上で動作させることができます。

ここでは、既存のインテル® Xeon® プロセッサー上で動作する最適化されたプログラムをインテル® Xeon Phi™ プロセッサー上で動作させた場合の性能をテストした結果をまとめました。

  • 検証に用いたインテル® Distribution for Python* はベータ版です。
    製品版とは結果が異なる可能性があります。
  • 本記事の内容は iSUS 編集部で検証を行ったものですが、性能を保証するものではありません。
    ご了承ください。

実行環境

実行環境
システム 1 システム 1-1 システム 2
プロセッサー詳細 インテル® Xeon® プロセッサー E5 2740 インテル® Xeon Phi™ コプロセッサー 5110P インテル® Xeon Phi™ プロセッサー 7210
プロセッサー数 2 1 1
物理コア数/論理コア数 12/24 (HT 無効) 60/240 64/256
搭載メモリ DDR3 96GB GDDR5 8GB DDR4 96GB
MCDRAM 16GB
OS RHEL 6.4 mpss 3.4 CentOS 7.2

比較内容

ここでは下記の処理を実行して、性能比較を行いました。

  1. DGEMM (インテル® MKL)
  2. numpy.dot (インテル® Distribution for Python*)
  3. DGEMV (インテル® MKL)

1. DGEMM (インテル® MKL)

#include <stdio.h>
#include <stdlib.h>
#include <sys/time.h>
#include "mkl.h"

void do_work(double C[], double A[], double B[], int N)
{
    int i, j, k;
    double alpha = 1.0, beta = 1.0;

#pragma omp parallel for
    for( i=0; i<N; i++ ){
      for( j=0; j<N; j++ ){
        C[i*N + j] = 0.0;
        A[i*N + j] = j&(1<<10) - 512;
        B[i*N + j] = (j%2) ? 1.0 : -1.0;
      }
    }
    cblas_dgemm(CblasRowMajor, CblasNoTrans, CblasNoTrans,
        N, N, N, alpha, A, N, B, N, beta, C, N);
}

int main(void)
{
    long i;
    int N = 6000; //Matrix Size
    double *A, *B, *C;
    struct timeval start, end;
    long mtime, seconds, useconds;

    A = (double *)mkl_malloc(sizeof(double)*N*N, 64);
    B = (double *)mkl_malloc(sizeof(double)*N*N, 64);
    C = (double *)mkl_malloc(sizeof(double)*N*N, 64);

    printf("\nMatrix size: [%d,%d]\n\n",N,N);
     
    for( i=0; i<6; i++)  {
      gettimeofday(&start, NULL);
      do_work(C, A, B, N);
      gettimeofday(&end, NULL);

      seconds  = end.tv_sec  - start.tv_sec;
      useconds = end.tv_usec - start.tv_usec;
      mtime = ((seconds) * 1000 + useconds/1000.0) + 0.5;
      printf("Time taken to run Matmul: %ld 
        milliseconds (%lf)\n", mtime, C[N*N-1]);

      fflush(stdout);
    }
    mkl_free(A);
    mkl_free(B);
    mkl_free(C);
    return 0;
}

図 1: DGEMM を使用したサンプルコード

図 1 はインテル® MKL の DGEMM 関数を各行列サイズ毎に複数回実行するプログラムです。Matrix Size ( N )を変えながら各システム上で計算を行い、システム 1 の結果を基準として、Matrix Size 毎に一番性能の高い値を比較した結果が図 2 です。

図 2: DGEMM の実行性能

2. numpy.dot (インテル® Distribution for Python*)

import numpy as np 
import time  
N = 6000

print ('Initializing...')

A = np.matrix(np.random.random((N, N)), dtype=np.double) 
B = np.matrix(np.random.random((N, N)), dtype=np.double)

print ('\nMatrix size: [{0}, {1}]\n'.format(N, N))
   
for i in range(1,6): 
    start = time.time()
    C = np.dot(A, B)
    end = time.time()

    tm = int((end - start) * 1000)
    print ('Run numpy.dot {0} milliseconds'.format(tm))

図 3: Numpy.dot を使用したサンプルコード

図 3 は numpy ライブラリーに含まれている dot ルーチンを DGEMM と同様に複数回実行するスクリプトです。図 4 は各システム上での実行性能を比較しています。Python* は インテル® Distribution for Python* を使用してインテル® プロセッサー向けに高速に動作するように最適化しています。(インテル® Distribution for Python の詳細はこちらをご確認ください。)

図 4: numpy.dot の性能比較

3. DGEMV (インテル® MKL)

#include<stdio.h>
#include<stdlib.h>
#include <sys/time.h>
#include <malloc.h>
#include <hbwmalloc.h>

#include "mkl.h"

#define SIZE 15000

main()
{
    long i, j;
    double *A;
    double *x, *y;

    long m = SIZE, n = SIZE;
    int ONE = 1;
    double alpha = 1, beta = 1;

    struct timeval start, end;
    long mtime, seconds, useconds;

    int retA = posix_memalign((void*) &A, 64, sizeof(double)*(n * m));
    int retB = posix_memalign((void*) &x, 64, sizeof(double)*n);
    int retC = posix_memalign((void*) &y, 64, sizeof(double)*n);

    printf("\nMatrix size\n");
    printf("A : [%d,%d]\n",n,m);
    printf("x : [%d]\n",n); 
    printf("y : [%d]\n",n);
    printf("\n");

    gettimeofday(&start, NULL);

#pragma omp parallel for
    for (i = 0; i < m; i++){
        x[i] = 1.0;
        y[i] = 1.0;
        for (j = 0; j < n; j++) {
            A[i*n + j] = j%4 ;
        }
    }

    cblas_dgemv(CblasRowMajor,CblasNoTrans, 
        m, n, alpha, A, m, x, ONE, beta, y, ONE);

    gettimeofday(&end, NULL);

    seconds = end.tv_sec - start.tv_sec;
    useconds = end.tv_usec - start.tv_usec;
    mtime = ((seconds) * 1000 + useconds / 1000.0) + 0.5;

    printf("Time taken to run dgemv: %ld milliseconds\n", mtime);
    for (j = 0; j<5; j++) {
        printf("%f ", y[j]);
    }
    printf("\n");

    free(A);
    free(x);
    free(y);

    printf("\n");
}

                

図 5: DGEMV のサンプルコード

図 5 はインテル® MKL の DGEMV 関数を実行するプログラムです。図 6 ではインテル® Xeon Phi™ プロセッサーのみに搭載されている広帯域メモリ (MCDRAM) に行列を確保した実行時間と一般的なメモリ (DDR3) 上で実行した場合の実行時間を比較しています。なお、40000 サイズの計算は 8GB 以上になるため、KNC 上では計算を行っていません。

図 6: DGEMV 性能比較

まとめ

今回は、インテル® Xeon® プロセッサーで動作する既存のプログラムをコードレベルの変更を行わず、インテル® Xeon Phi™ プロセッサーでそのまま動作させて実行性能の比較を行いました。またインテル® Xeon Phi™ コプロセッサー上ではインテル® MIC アーキテクチャー向けのバイナリーを生成して比較しました。これらは既に並列化処理を実装しているため、インテル® Xeon Phi™ プロセッサーではより実行性能が向上していることが確認できます。

検証では少ししか触れていませんが、インテル® Xeon Phi™ プロセッサーには DDR4 メモリに加え、独自に 16 GB の MCDRAMを搭載しているため、頻繁に使用されるデータを MCDRAM に配置することで、より性能が向上する可能性があります。インテル® MKL の関数は可能であれば MCDRAM に配列を確保するため、より顕著な性能の向上が見られました。また、ベクトル命令として AVX-512 を使用することもできます。より高度なベクトル命令を使用することで、より高速に動作する可能性があります。

なお、KNLでは、KNC 同様に並列化されていない処理は、一般的なインテル® Xeon® プロセッサーよりも遅くなってしまいます。複数の並列レベルを意識したプログラムを開発することで、インテル® Xeon Phi™ プロセッサーのリソースを効果的に利用することができます。
ソフトウェアの最適化については、現代化の記事もご参照ください。

関連記事