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.
Jeśli jednak zrobić operacje zapisu przed czytaniem mamy:
rowMajor_write();
colMajor_write();
rowMajor_read();
colMajor_read();
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
Moje przypuszczenie dotyczy pamięci podręcznej systemu i prognozy. – Nit
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. –
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