3

そのようなコードが gcc 4.4.6 でベクトル化されない理由がわかりません

int MyFunc(const float *pfTab, float *pfResult, int iSize, int iIndex)
{
  for (int i = 0; i < iSize; i++)
     pfResult[i] = pfResult[i] + pfTab[iIndex];
}

 note: not vectorized: unhandled data-ref

ただし、次のコードを書くと

   int MyFunc(const float *pfTab, float *pfResult, int iSize, int iIndex)
{
  float fTab =  pfTab[iIndex];
  for (int i = 0; i < iSize; i++)
     pfResult[i] = pfResult[i] + fTab;
}

gcc はこのループの自動ベクトル化に成功します

omp ディレクティブを追加すると

   int MyFunc(const float *pfTab, float *pfResult, int iSize, int iIndex)
{
  float fTab =  pfTab[iIndex];
  #pragma omp parallel for
  for (int i = 0; i < iSize; i++)
     pfResult[i] = pfResult[i] + fTab;
}

ベクトル化されていない次のエラーがあります: unhandled data-ref

最初のコードと 3 番目のコードが自動ベクトル化されない理由を教えてください。

2 番目の質問: 数学オペランドがベクトル化されていないようです (exp、log など)。このコードは、たとえば

for (int i = 0; i < iSize; i++)
         pfResult[i] = exp(pfResult[i]);

ベクトル化されていません。私のバージョンの gcc が原因ですか?

編集: gcc 4.8.1 および openMP 2011 の新しいバージョン (echo |cpp -fopenmp -dM |grep -i open) では、基本的にすべての種類のループに対して次のエラーが発生します。

   for (iGID = 0; iGID < iSize; iGID++)
        {
             pfResult[iGID] = fValue;
        }


note: not consecutive access *_144 = 5.0e-1;
note: Failed to SLP the basic block.
note: not vectorized: failed to find SLP opportunities in basic block.

編集2:

#include<stdio.h>
#include<sys/time.h>
#include <string.h>
#include <math.h>
#include <stdlib.h>
#include <omp.h>

int main()
{
        int szGlobalWorkSize = 131072;
        int iGID = 0;
        int j = 0;
        omp_set_dynamic(0);
        // warmup
        #if WARMUP
        #pragma omp parallel
        {
        #pragma omp master
        {
        printf("%d threads\n", omp_get_num_threads());
        }
        }
        #endif
        printf("Pagesize=%d\n", getpagesize());
        float *pfResult = (float *)malloc(szGlobalWorkSize * 100* sizeof(float));
        float fValue = 0.5f;
        struct timeval tim;
        gettimeofday(&tim, NULL);
        double tLaunch1=tim.tv_sec+(tim.tv_usec/1000000.0);
        double time = omp_get_wtime();
        int iChunk = getpagesize();
        int iSize = ((int)szGlobalWorkSize * 100) / iChunk;
        //#pragma omp parallel for
        for (iGID = 0; iGID < iSize; iGID++)
        {
             pfResult[iGID] = fValue;
        }
        time = omp_get_wtime() - time;
        gettimeofday(&tim, NULL);
        double tLaunch2=tim.tv_sec+(tim.tv_usec/1000000.0);
        printf("%.6lf Time1\n", tLaunch2-tLaunch1);
        printf("%.6lf Time2\n", time);
}

との結果

#define _OPENMP 201107
gcc (GCC) 4.8.2 20140120 (Red Hat 4.8.2-15)

gcc -march=native -fopenmp -O3 -ftree-vectorizer-verbose=2 test.c -lm

たくさんの

note: Failed to SLP the basic block.
note: not vectorized: failed to find SLP opportunities in basic block.
and note: not consecutive access *_144 = 5.0e-1;

ありがとう

4

1 に答える 1

7

GCC はループの最初のバージョンをベクトル化できません。これは、 (ポインター エイリアシング)pfTab[iIndex]によってスパンされるメモリ内のどこかに含まれていないことを証明できないためです。pfResult[0] ... pfResult[iSize-1]実際、pfTab[iIndex]がそのメモリ内のどこかにある場合は、その値をループ本体の代入によって上書きする必要があり、新しい値を後続の反復で使用する必要があります。キーワードを使用してrestrict、これが決して起こらないことをコンパイラーに示唆する必要があります。そうすれば、コードが喜んでベクトル化されます。

$ cat foo.c
int MyFunc(const float *restrict pfTab, float *restrict pfResult,
           int iSize, int iIndex)
{
   for (int i = 0; i < iSize; i++)
     pfResult[i] = pfResult[i] + pfTab[iIndex];
}
$ gcc -v
...
gcc version 4.6.1 (GCC)
$ gcc -std=c99 -O3 -march=native -ftree-vectorizer-verbose=2 -c foo.c
foo.c:3: note: LOOP VECTORIZED.
foo.c:1: note: vectorized 1 loops in function.

2 番目のバージョンでは、値が自動保存期間を持つ変数に転送されるため、ベクトル化されます。ここでの一般的な前提は、 が格納されpfResultているスタック メモリにまたがらないことfTabです (C99 言語仕様をざっと読んでも、その前提が弱いか、標準で許可されているかどうかはわかりません)。

OpenMP バージョンは、OpenMP が GCC に実装されているため、ベクトル化されません。並列領域にコードのアウトラインを使用します。

int MyFunc(const float *pfTab, float *pfResult, int iSize, int iIndex)
{
  float fTab =  pfTab[iIndex];
  #pragma omp parallel for
  for (int i = 0; i < iSize; i++)
     pfResult[i] = pfResult[i] + fTab;
}

効果的に次のようになります。

struct omp_data_s
{
  float *pfResult;
  int iSize;
  float *fTab;
};

int MyFunc(const float *pfTab, float *pfResult, int iSize, int iIndex)
{
  float fTab =  pfTab[iIndex];
  struct omp_data_s omp_data_o;

  omp_data_o.pfResult = pfResult;
  omp_data_o.iSize = iSize;
  omp_data_o.fTab = fTab;

  GOMP_parallel_start (MyFunc_omp_fn0, &omp_data_o, 0);
  MyFunc._omp_fn.0 (&omp_data_o);
  GOMP_parallel_end ();
  pfResult = omp_data_o.pfResult;
  iSize = omp_data_o.iSize;
  fTab = omp_data_o.fTab;
}

void MyFunc_omp_fn0 (struct omp_data_s *omp_data_i)
{
  int start = ...; // compute starting iteration for current thread
  int end = ...; // compute ending iteration for current thread

  for (int i = start; i < end; i++)
    omp_data_i->pfResult[i] = omp_data_i->pfResult[i] + omp_data_i->fTab;
}

MyFunc_omp_fn0概説された関数コードが含まれています。omp_data_i->pfResultコンパイラは、 が別名のメモリomp_data_i、具体的にはそのメンバーを指していないことを証明できませんfTab

そのループをベクトル化するには、make する必要がありますfTab firstprivate。これにより、概説されたコードの自動変数に変わり、2番目のケースと同等になります。

$ cat foo.c
int MyFunc(const float *pfTab, float *pfResult, int iSize, int iIndex)
{
   float fTab = pfTab[iIndex];
   #pragma omp parallel for firstprivate(fTab)
   for (int i = 0; i < iSize; i++)
     pfResult[i] = pfResult[i] + fTab;
}
$ gcc -std=c99 -fopenmp -O3 -march=native -ftree-vectorizer-verbose=2 -c foo.c
foo.c:6: note: LOOP VECTORIZED.
foo.c:4: note: vectorized 1 loops in function.
于 2014-11-20T18:29:51.393 に答える