2010-02-10 11 views
5

Zastanawiam się, czy jest to oczekiwane zachowanie w C++. Poniższy kod jest uruchamiany na około 0,001 MS:Powolne pisanie do tablicy w C++

for(int l=0;l<100000;l++){ 
     int total=0; 
     for(int i = 0; i < num_elements; i++) 
     { 
      total+=i; 
     } 
    } 

Jednak jeśli wyniki są zapisywane na tablicy, czas realizacji pędy do 15 MS:

int *values=(int*)malloc(sizeof(int)*100000); 
     for(int l=0;l<100000;l++){ 
      int total=0; 
      for(unsigned int i = 0; i < num_elements; i++) 
      { 
       total+=i; 
      } 
      values[l]=total; 
     } 

mogę docenić, że pisanie do tablica wymaga czasu, ale czy czas jest proporcjonalny?

Zdrówko wszyscy

+0

Twoje pytanie mówi C, ale twoje tagi mówią C++. Który to? –

+0

Przepraszam, ściśle C++, ale jeśli deklaracje int zostały przeniesione poza pętle for, to C – Ljdawson

+0

@Laurence - Nie, twój kod jest całkowicie standardowy w C99, a większość kompilatorów C89 zaakceptuje składnię, której używasz. –

Odpowiedz

11

Pierwszy przykład może zostać zaimplementowany przy użyciu tylko rejestrów procesora. Dostęp do nich jest miliard razy na sekundę. Drugi przykład wykorzystuje tak dużo pamięci, że z pewnością przepełnia pamięć podręczną L1 i ewentualnie L2 (w zależności od modelu procesora). To będzie wolniejsze. Mimo to, 15 ms/100 000 zapisów wychodzi na 1,5 ns na zapis - efektywnie 667 Mhz. To jest , a nie wolno.

+1

+1 za wskazanie rejestrów cpu. W drugim przypadku kod przechodzi przez pamięć, zapisuje bajty (wciąga strony pamięci, wymusza buforowanie, ... zapisuje tylko jedną wartość). W pierwszym przypadku może to być czysty L1. Ta odpowiedź powinna zostać bardziej wznowiona. – anon

+0

świetna odpowiedź, dzięki – Ljdawson

10

Wygląda na to, że kompilator optymalizuje pętli się całkowicie w pierwszym przypadku.

Łączny efekt pętli to operacja "no-op", więc kompilator po prostu ją usuwa.

+0

aha! Myślałem tak samo, czy istnieje sposób na wyłączenie tej optymalizacji do benchmarkingu, czy też to? – Ljdawson

+0

W dużej mierze zależy to od używanego kompilatora i IDE. Sprawdź plik pomocy/stronę podręcznika, aby dowiedzieć się, co należy zrobić, aby wyłączyć optymalizacje. –

+1

spróbuj użyć całkowitej po pętli; lub dodaj -O0, jeśli używasz gcc. – tristan

1

Podejrzewam, że to, co widzisz, jest efektem działania virtual memory i prawdopodobnie stronicowania. Wywołanie malloc przydzieli porządną porcję pamięci, która jest prawdopodobnie reprezentowana przez kilka wirtualnych stron. Każda strona jest osobno połączona z pamięcią procesową.

Możesz również mierzyć koszt połączenia pod numer malloc w zależności od tego, w jaki sposób ustawiłeś czas w pętli. W obu przypadkach wydajność będzie bardzo wrażliwa na opcje optymalizacji kompilatora, opcje wątkowania, wersje kompilatora, wersje uruchomieniowe i prawie wszystko. Nie można bezpiecznie założyć, że koszt jest liniowy w stosunku do wielkości przydziału. Jedyną rzeczą, którą możesz zrobić, to zmierzyć go i wymyślić, jak najlepiej zoptymalizować , gdy zostanie udowodnione, że jest to problem.

3

To bardzo proste. W pierwszym przypadku Masz tylko 3 zmienne, które mogą być łatwo zapisane w GPR (rejestrach ogólnego przeznaczenia), ale to nie znaczy, że są tam cały czas, ale prawdopodobnie są w pamięci podręcznej L1, co oznacza, że można uzyskać bardzo szybko.

W drugim przypadku Masz więcej niż 100k zmiennych i potrzebujesz około 400kB do ich przechowywania. To zdecydowanie za dużo dla rejestrów i pamięci podręcznej L1. W najlepszym przypadku może to być pamięć podręczna L2, ale prawdopodobnie nie wszystkie z nich będą znajdować się w L2. Jeśli coś nie jest w rejestrze, L1, L2 (zakładam, że twój procesor nie ma L3) oznacza to, że musisz wyszukać go w pamięci RAM i wymaga to więcej czasu.