2014-10-31 8 views
8

Przeprowadzam eksperyment z dostępem do pamięci, w którym użyto macierzy 2D, przy czym każdy wiersz jest wielkości strony pamięci. Eksperyment polega na odczytaniu każdego elementu za pomocą wiersza/kolumny głównej, a następnie zapisaniu każdego elementu za pomocą wiersza/kolumny głównej. Macierz, do której uzyskano dostęp, została zadeklarowana z globalnym zakresem w celu uproszczenia wymagań programistycznych.Czy odczyt "zera" z pamięci jest szybszy niż czytanie innych wartości?

Punktem tego pytania jest to, że gdy macierz testowa jest zadeklarowana statycznie, wartości są inicjowane do zera przez kompilator, a wyniki, które znalazłem, są całkiem interesujące. Kiedy najpierw wykonałem operacje odczytu, tj.

rowMajor_read(); 
colMajor_read(); 
rowMajor_write(); 
colMajor_write(); 

Następnie moja operacja colMajor_read zakończyła się bardzo szybko. enter image description here

Jeśli jednak zrobić operacje zapisu przed czytaniem mamy:

rowMajor_write(); 
colMajor_write(); 
rowMajor_read(); 
colMajor_read(); 

enter image description here

i operacja odczytu column-major wzrosła prawie o rząd wielkości.

Uznałem, że musi to mieć coś wspólnego z optymalizacją kodu przez kompilator. Ponieważ globalna macierz była identycznie równa zero dla każdego elementu, czy kompilator całkowicie usunął operacje odczytu? Czy może "łatwiej" odczytać wartość z pamięci, która jest identyczna zero?

Nie przekazuję żadnych specjalnych poleceń kompilatora w odniesieniu do optymalizacji, ale zadeklarowałem swoje funkcje w ten sposób.

inline void colMajor_read(){ 
    register int row, col; 
    register volatile char temp __attribute__((unused)); 
    for(col = 0; col < COL_COUNT; col++) 
     for(row = 0; row < ROW_COUNT; row++) 
      temp = testArray[row][col]; 
} 

Ponieważ biegałam w kwestiach, w których kompilator całkowicie usunęła temp zmienną z powyższej funkcji, ponieważ nigdy nie był używany. Myślę, że posiadanie obu volatile i __attribute__((unused)) jest zbędne, ale mimo to dołączyłem to. Miałem wrażenie, że nie wprowadzono optymalizacji zmiennej lotnej.

Wszelkie pomysły?


Przyjrzałem się wygenerowanemu montażowi, a wyniki są identyczne dla funkcji colMajor_read. Wersja (nie w linijce): (montaż): http://pastebin.com/C8062fYB

+5

Moje przypuszczenie dotyczy pamięci podręcznej systemu i prognozy. – Nit

+1

Zgadzam się z @Nit. Lokalizacja pamięci podręcznej jest najprawdopodobniej źródłem wariancji. Skrytki mogą łatwo dać 10-krotny czas dostępu. Jeśli podejrzewasz, że kompilator optymalizuje operacje na odległość (mało prawdopodobne w różnych funkcjach, ale nie jest to całkowicie niemożliwe), uzyskaj wynik asemblera funkcji C do sprawdzenia. –

+2

Trzymajcie się chłopaków. Nie sądzę, żeby to wszystko było skomplikowane. Ponieważ metody są wbudowane, oznacza to, że wszystkie te funkcje znajdują się w tej samej jednostce kompilacji, więc kompilator może robić fantastyczne rzeczy. Przede wszystkim może stwierdzić, czy zmienna została zmieniona od czasu odczytu i zapisu, więc mogłaby z łatwością ponownie zinterpretować kod jako 'temp = 0;', który w porównaniu z nim byłby szalony. Czy możesz wysłać zespół? – IdeaHat

Odpowiedz

7

Sprawdź zużycie pamięci w twoim procesie przed i po zapisaniu wartości do macierzy. Jeśli na przykład jest przechowywany w sekcji .bss w systemie Linux, wyzerowane strony zostaną zmapowane na pojedynczą stronę tylko do odczytu z semantyką "kopiuj przy zapisie". Tak więc, mimo że czytasz przez kilka adresów, możesz w kółko czytać tę samą stronę pamięci fizycznej.

Ta strona http://madalanarayana.wordpress.com/2014/01/22/bss-segment/ ma dobre wytłumaczenie.

Jeśli tak jest, wyzeruj matrycę po raz kolejny i uruchom ponownie test odczytu i nie powinno to już być znacznie szybsze.

+0

+1 Już miałem to opublikować, gdy zauważyłem, że spóźniłem się 16 godzin. – Mehrdad