Domanda

Non ho certo codice in un ciclo

for(int i = 0; i < n; i++)
{
  u[i] = c * u[i] + s * b[i];
}

Quindi, u e b sono vettori della stessa lunghezza, e C e s sono scalari. E 'questo codice un buon candidato per vettorializzazione da utilizzare con SSE al fine di ottenere un aumento di velocità?

Aggiorna

ho imparato vettorializzazione (scopre che non è così difficile se si utilizza intrinseci) e realizzato il mio ciclo in SSE. Tuttavia, quando si imposta il flag SSE2 in VC ++ compiler, ottengo circa le stesse prestazioni con il mio codice SSE. Il compilatore Intel d'altra parte era molto più veloce di quanto il mio codice SSE o VC ++ compilatore.

Ecco il codice che ho scritto per riferimento

double *u = (double*) _aligned_malloc(n * sizeof(double), 16);
for(int i = 0; i < n; i++)
{
   u[i] = 0;
}

int j = 0;
__m128d *uSSE = (__m128d*) u;
__m128d cStore = _mm_set1_pd(c);
__m128d sStore = _mm_set1_pd(s);
for (j = 0; j <= i - 2; j+=2)
{
  __m128d uStore = _mm_set_pd(u[j+1], u[j]);

  __m128d cu = _mm_mul_pd(cStore, uStore);
  __m128d so = _mm_mul_pd(sStore, omegaStore);

  uSSE[j/2] = _mm_add_pd(cu, so);
}
for(; j <= i; ++j)
{
  u[j] = c * u[j] + s * omegaCache[j];
}
È stato utile?

Soluzione

Sì, questo è un ottimo candidato per vettorializzazione. Ma, prima di farlo, assicurati di aver profilato il codice per essere sicuri che questo è in realtà la pena di ottimizzazione. Detto questo, la vettorializzazione sarebbe qualcosa di simile a questo:

int i;
for(i = 0; i < n - 3; i += 4)
{
  load elements u[i,i+1,i+2,i+3]
  load elements b[i,i+1,i+2,i+3]
  vector multiply u * c
  vector multiply s * b
  add partial results
  store back to u[i,i+1,i+2,i+3]
}

// Finish up the uneven edge cases (or skip if you know n is a multiple of 4)
for( ; i < n; i++)
  u[i] = c * u[i] + s * b[i];

Per prestazioni ancora di più, si può considerare prefetching ulteriori elementi dell'array, e / o srotolando il ciclo e utilizzando il software pipelining per interlacciare il calcolo in un loop con accesso alla memoria da un'iterazione differente.

Altri suggerimenti

Probabilmente sì, ma bisogna aiutare il compilatore con qualche accenno. __restrict__ immessi sul puntatori dice compilatore che non v'è alcun alias tra due puntatori. se si conosce l'allineamento dei vostri vettori, comunicare che al compilatore (Visual C ++ può avere qualche struttura).

Non ho familiarità con Visual C ++ me stesso, ma ho sentito dire che è non va bene per la vettorializzazione. Considerare l'utilizzo di Intel compilatore invece. Intel permette un controllo a grana fine bella over assembly generato: http://www.intel.com/software/products/compilers/docs/clin/main_cls/cref_cls/common/cppref_pragma_vector.htm

_mm_set_pd non è vettorializzare. Se preso alla lettera, legge i due doppie con operazioni scalari, quindi combina le due doppie scalari e copiarli nel registro SSE. Usa _mm_load_pd invece.

Sì, questo è un ottimo candidato per vectorizaton, supponendo che non v'è alcuna sovrapposizione di U e B matrice. Ma il codice è vincolato da accesso alla memoria (load / store). Vettorializzazione aiuta a ridurre i cicli per loop, ma le istruzioni in stallo a causa della cache-miss su U e B array. Il processore Intel C / C ++ Compiler genera il seguente codice con le bandiere di default per il processore Xeon x5500. Il compilatore srotola il loop 8 e impiega SIMD ADD (addpd) e rispettivamente moltiplicano (mulpd) utilizzando xmm [0-16] registri SIMD. In ciascun ciclo, il processore può emettere 2 SIMD istruzioni cedevoli 4 vie scalari ILP, a patto di avere i dati pronti nei registri.

Qui U, B, C ed S sono doppia precisione (8 byte).

    ..B1.14:                        # Preds ..B1.12 ..B1.10
    movaps    %xmm1, %xmm3                                  #5.1
    unpcklpd  %xmm3, %xmm3                                  #5.1
    movaps    %xmm0, %xmm2                                  #6.12
    unpcklpd  %xmm2, %xmm2                                  #6.12
      # LOE rax rcx rbx rbp rsi rdi r8 r12 r13 r14 r15 xmm0 xmm1 xmm2 xmm3
    ..B1.15:     # Preds ..B1.15 ..B1.14
    movsd     (%rsi,%rcx,8), %xmm4                          #6.21
    movhpd    8(%rsi,%rcx,8), %xmm4                         #6.21
    mulpd     %xmm2, %xmm4                                  #6.21
    movaps    (%rdi,%rcx,8), %xmm5                          #6.12
    mulpd     %xmm3, %xmm5                                  #6.12
    addpd     %xmm4, %xmm5                                  #6.21
    movaps    16(%rdi,%rcx,8), %xmm7                        #6.12
    movaps    32(%rdi,%rcx,8), %xmm9                        #6.12
    movaps    48(%rdi,%rcx,8), %xmm11                       #6.12
    movaps    %xmm5, (%rdi,%rcx,8)                          #6.3
    mulpd     %xmm3, %xmm7                                  #6.12
    mulpd     %xmm3, %xmm9                                  #6.12
    mulpd     %xmm3, %xmm11                                 #6.12
    movsd     16(%rsi,%rcx,8), %xmm6                        #6.21
    movhpd    24(%rsi,%rcx,8), %xmm6                        #6.21
    mulpd     %xmm2, %xmm6                                  #6.21
    addpd     %xmm6, %xmm7                                  #6.21
    movaps    %xmm7, 16(%rdi,%rcx,8)                        #6.3
    movsd     32(%rsi,%rcx,8), %xmm8                        #6.21
    movhpd    40(%rsi,%rcx,8), %xmm8                        #6.21
    mulpd     %xmm2, %xmm8                                  #6.21
    addpd     %xmm8, %xmm9                                  #6.21
    movaps    %xmm9, 32(%rdi,%rcx,8)                        #6.3
    movsd     48(%rsi,%rcx,8), %xmm10                       #6.21
    movhpd    56(%rsi,%rcx,8), %xmm10                       #6.21
    mulpd     %xmm2, %xmm10                                 #6.21
    addpd     %xmm10, %xmm11                                #6.21
    movaps    %xmm11, 48(%rdi,%rcx,8)                       #6.3
    addq      $8, %rcx                                      #5.1
    cmpq      %r8, %rcx                                     #5.1
    jl        ..B1.15       # Prob 99%                      #5.1

che dipende da come si ue B situato in memoria. se entrambi blocco di memoria sono lontani gli uni dagli altri, SSE non sarebbe spinta tanto in questo scenario.

si suggerisce che la matrice u e b sono AOE (array di struttura) invece di SOA (struttura della matrice), perché è possibile caricare entrambi nel registro in un'unica istruzione.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top