2013-08-23 11 views
10

Zadanie to jest bardzo proste, pisząc do seqence zmiennej całkowitej pamięci:Szybki (est) sposób na zapisanie sekwencji całkowitej do pamięci globalnej?

kod oryginalny:

for (size_t i=0; i<1000*1000*1000; ++i) 
{ 
    data[i]=i; 
}; 

kod parallelized:

size_t stepsize=len/N; 

#pragma omp parallel num_threads(N) 
    { 
     int threadIdx=omp_get_thread_num(); 

     size_t istart=stepsize*threadIdx; 
     size_t iend=threadIdx==N-1?len:istart+stepsize; 
#pragma simd 
     for (size_t i=istart; i<iend; ++i) 
      x[i]=i; 
    }; 

Wydajność ssie, trwa 1,6 sek do pisania zmiennych 1G uint64 (co odpowiada 5 GB na sekundę), poprzez prostą równoległość (open mp parallel) powyższego kodu, zwiększenie prędkości nieco, ale wydajność wciąż bani się 1,4 sek z 4 gwintów i 1.35 z 6 włókien o i7 3970.

theortical przepustowości pamięci mego wiertniczego (i7 3970/64G DDR3-1600) jest 51,2 GB/sek., dla powyższego przykładu, uzyskana przepustowość pamięci to tylko około 1/10 z theoritcal przepustowości, nawet za pomocą aplikacji jest dość ograniczona przepustowość pamięci.

Ktoś wie, jak poprawić kod?

Napisałem dużo kodu związanego z pamięcią na GPU, dość łatwo GPU, aby w pełni wykorzystać przepustowość pamięci urządzenia GPU (na przykład 85% + szerokości pasma).

EDIT:

Kod jest opracowany przez Intel MTK 13.1 do 64bit binarny, a przy maksymalnym optimzation (O3) i kod na ścieżce AVX, jak również auto-wektoryzacji.

UPDATE:

Próbowałem wszystkie z poniższych kodów (dzięki Paul R), nic specjalnego się dzieje, uważam, że kompilator jest w pełni zdolny do tego rodzaju optymalizacji SIMD/wektoryzacja.

Jak, dlaczego chcę wypełnić numery tam, dobrze, krótko mówiąc:

Jego częścią wysokiej wydajności niejednorodny obliczeniowej algorthim, po stronie urządzenia, po wykonaniu algorthim jest bardzo wydajny do tego stopnia, że zestaw wielu GPU jest tak szybki, że znalazłem wąskie gardło wydajności, gdy procesor próbuje zapisać kilka liczb w pamięci.

Przyczyny wiedząc, że procesor zasysa w wypełnienie liczby (w przeciwieństwie, GPU może wypełnić seqence numeru prędkością bardzo blisko (238GB/s z 288GB/s na GK110 vs żałosnym 5 GB/s. z 51,2GB/s. na procesor) do teoretycznej przepustowości globalnej pamięci GPU), mógłbym nieco zmienić mój algortym, ale zastanawiam się, dlaczego procesor jest tak kiepski w wypełnianiu numerów liczb tutaj.

chodzi o przepustowość pamięci mojej platformie, wierzę przepustowość (51.2GB) wynosi około poprawna, na podstawie mojego testu memcpy(), osiągnięty przepustowość jest o 80% + z obrona teoretycznej przepustowości (> 40GB/s).

+4

Czy próbowałeś zoptymalizować kod? Na przykład. użyć -O3, jeśli używasz 'gcc'? –

+1

Powinieneś przynajmniej spróbować go rozwinąć. * Optymalizacja fotela *. – unwind

+0

Spróbuj wykonać 2 zapisy w każdej iteracji. Może 4, 8, 16. –

Odpowiedz

10

Zakładając to x86, a które nie są już nasycając dostępnego pasma DRAM, można spróbować użyć SSE2 lub AVX2 napisać 2 lub 4 elementy naraz:

SSE2:

#include "emmintrin.h" 

const __m128i v2 = _mm_set1_epi64x(2); 
__m128i v = _mm_set_epi64x(1, 0); 

for (size_t i=0; i<1000*1000*1000; i += 2) 
{ 
    _mm_stream_si128((__m128i *)&data[i], v); 
    v = _mm_add_epi64(v, v2); 
} 

AVX2:

#include "immintrin.h" 

const __m256i v4 = _mm256_set1_epi64x(4); 
__m256i v = _mm256_set_epi64x(3, 2, 1, 0); 

for (size_t i=0; i<1000*1000*1000; i += 4) 
{ 
    _mm256_stream_si256((__m256i *)&data[i], v); 
    v = _mm256_add_epi64(v, v4); 
} 

data pamiętać, że musi być odpowiednio dostosowane (16 bajtów lub 32 granicy bajtu).

AVX2 jest dostępny tylko w Intel Haswell i późniejszych wersjach, ale SSE2 jest obecnie prawie uniwersalny.


FWIW umieścić razem wiązkę testową z pętlą skalarnego i wyżej SSE i AVX pętle skompilowany z brzękiem i testowano je na Haswell MacBook Air (1600MHz LPDDR3 DRAM). Mam następujące wyniki:

# sequence_scalar: t = 0.870903 s = 8.76033 GB/s 
# sequence_SSE: t = 0.429768 s = 17.7524 GB/s 
# sequence_AVX: t = 0.431182 s = 17.6941 GB/s 

również próbowałem go na Linux Desktop PC z 3,6 GHz Haswell, kompilacji z gcc 4.7.2 i mam następujące:

# sequence_scalar: t = 0.816692 s = 9.34183 GB/s 
# sequence_SSE: t = 0.39286 s = 19.4201 GB/s 
# sequence_AVX: t = 0.392545 s = 19.4357 GB/s 

tak wygląda podobnie jak implementacje SIMD dają 2x lub więcej ulepszeń w porównaniu z 64-bitowym kodem skalarnym (chociaż 256-bitowe SIMD nie wydaje się poprawiać w porównaniu do 128-bitowego SIMD), a typowa przepustowość powinna być znacznie większa niż 5 GB/s.

Zgaduję, że coś jest nie tak z systemem OP lub kodem testowym, co skutkuje najwyraźniej zmniejszoną przepustowością.

+2

Czy ... porównałeś go, aby zobaczyć, czy jest on faktycznie? Szybszy? – sehe

+2

Zostało to jako ćwiczenie dla czytelnika i oczywiście będzie zależeć od różnych czynników. Ale ponieważ przepustowość DRAM jest rzekomo nisko nasycona w przypadku PO, spodziewam się niewielkiej poprawy. –

+0

@PaulR Te wskaźniki rzucają na '__m256i *' dzwoni moje ostre aliasingowe dzwony ... –

5

Czy jest jakiś powód, dla którego można oczekiwać, że wszystkie data[] będą na zasilanych stronach pamięci RAM?

Pre-fetchter DDR3 poprawnie przewiduje większość dostępów, ale często występujące mogą być granice strony x86-64. Piszemy do pamięci wirtualnej, więc na każdej granicy strony istnieje potencjalna niedoszacowanie pre-fetchera. Możesz to znacznie zmniejszyć, używając dużych stron (np. MEM_LARGE_PAGES w systemie Windows).

Powiązane problemy