Dyskusja:C/Instrukcje sterujące: Różnice pomiędzy wersjami

Usunięta treść Dodana treść
Derbeth (dyskusja | edycje)
Kj (dyskusja | edycje)
Linia 49:
jest zrobiony ok od strony składni, ale zero nie jest podzielne przez nic!
: Ależ zero dzieli się bardzo dobrze, bo przez każdą liczbę całkowitą (poza zerem). [[w:Dzielnik]]. --[[Wikipedysta:Derbeth|Derbeth]] [[Dyskusja Wikipedysty:Derbeth|<sup>talk</sup>]] 02:19, 18 lis 2007 (CET)
 
== Pre/post inkrementacja ==
 
Nie jestem przekonany co do wyższości preinkrementacji nad postinkrementacją. Pozwoliłem sobie przeprowadzić mały test, który wprowadził mnie w osłupienie. Oto kod programu testowego:
<source lang="C">
#define N 100000000
#include <time.h>
#include <stdio.h>
 
int main ()
{
/*register long i asm("rax") = 15; - w tym przypadku program wyraźnie przyspiesza (GCC/AMD64)*/
long i = 15;
long a = 0;
long t1,t2,t3,t4;
t1 = clock();
for (i=0;i<N;) {a=2*(++i);}
t1 = clock()-t1;
t2 = clock();
for (i=0;i<N;) {a=2*(i++);}
t2 = clock()-t2;
printf ("1) %fs\n2) %fs\n",t1/(CLOCKS_PER_SEC*1.0f),t2/(CLOCKS_PER_SEC*1.0f));
}
</source>
Wyniki na moim komputerze są następujące:
 
1) 0.290000s
2) 0.240000s
 
Próby przeprowadzałem kilkukrotnie, aby upewnić się, że test jest wiarygodny. Zamieniałem także kolejność pętli (tj. raz pierwsza pętla zawierała pre- a raz postinkrementację) aby wykluczyć wpływ systemu operacyjnego. Pomimo tego otrzymywałem identyczne wyniki. W opcjach kompilatora nie dodawałem żadnych parametrów optymalizacyjnych (nie o to chodzi). Użyty kompilator: GCC 4.3.2. Z kolei na serwerze uczelnianym na tej samej platformie ale starszym kompilatorze (3.4.3) wyniki były takie:
 
 
1) 0.290000s
2) 0.280000s
 
Lub zamieniały się miejscami (granica błędu). Innymi słowy - nie była widoczna szczególna różnica, uzasadniająca wybór którejkolwiek z rodzajów inkrementacji.
Spotkałem się z [http://www.codeguru.com/forum/archive/index.php/t-384898.html opinią], która uzasadnia wydłużenie działania "i++" względem "++i" w następujący sposób (cytat):
<source lang="c">
int PreIncrement(int a)
{
a = a + 1;
return a;
}
 
int PostIncrement(int a)
{
int temp = a; // This is the reason why it is slightly slower
a = a + 1;
return temp;
}
</source>
Oczywiście ma to sens w przypadku, gdy implementujemy takie operatory w języku C. Nie zapominajmy jednak, że na poziomie asemblera, do którego pośrednio tłumaczony jest C oraz na poziomie kodu maszynowego granica między "i++" a "++i" nieco się zaciera. Spróbujmy się zastanowić, jak mogłaby wyglądać pętla wyrażona w C:
<source lang="C">
int a = 0;
int i = 0;
for (;i<100;) {a=2*(i++);}
</source>
Imho mogłaby wyglądać tak:
<source lang="asm">
/* r9 - "a", r10 - "i" */
movq $0, %r9
movq %r9,%r10
loop:
movq %r10,%r9
mulq $2,%r9
incq %r10
cmpq $99,%r10
jle $loop
</source>
Jak wyglądałaby pętla:
<source lang="C">
int a = 0;
int i = 0;
for (;i<100;) {a=2*(i++);}
</source>
?
Moim zdaniem tak:
<source lang="asm">
/* r9 - "a", r10 - "i" */
movq $0, %r9
movq %r9,%r10
loop:
incq %r10
movq %r10,%r9
mulq $2,%r9
cmpq $99,%r10
jle $loop
</source>
(Kod nie był testowany - jest on raczej pewnego rodzaju demonstracją praktycznej różnicy w budowie pętli na poziomie kodu asemblera. Fakt przechowywania zmiennych w rejestrach także nie powinien być brany pod uwagę - analogiczna sytuacja miałaby miejsce w przypadku operacji na pamięci.)
Różnica polega tylko i wyłącznie na kolejności instrukcji. Nigdzie tutaj nie musimy tworzyć dodatkowej zmiennej, a szybkość "i++" oraz "++i" moim zdaniem zależeć będzie tylko i wyłącznie od kompilatora i jakości generowanego przez niego kodu. Ośmielę się postawić tezę, że przy współczesnych kompilatorach nie ma to znaczenia.
Zachęcam do dyskusji i pozdrawiam. --[[Wikipedysta:Kj|Kj]] 01:38, 20 lut 2009 (CET)
Powrót do strony „C/Instrukcje sterujące”.