skutecznosc autooptymalizacji

0

Zastanawiam się nad pewnym faktem o którym krążą w necie przeciwstawne opinie, mi samemu brakuje wiedzy, żeby to w tej chwili ogarnąć .

  • Czy w dzisiejszych czasach ma sens klepanie niskopoziomewego kodu biorąc za wyznacznik jego optymalizację pod kątem szybkości wykonania kodu, czy jednak faktycznie superzautomatyzowany kompilator zrobi to za człowieka dużo lepiej i ciężko by było go przegonić ?
    Chciał bym w tym temacie przynajmniej rozjaśnić temat podpierając się praktycznymi rozwiązaniami nie zaś zapewnieniami, typu "tak jest\ nie jest".

Problemem jest to, że nie znam C++ a to właśnie któryś z kompilatorów tego języka chciałbym wziąć na tapetę.
Zrobiłem tak: W DevCpp zaznaczyłem opcje kompilatora "Best optimization" wklepałem taką funkcję (ma policzyć znaki stringu):

INT   ansiLength ( LPSTR pstring )
{
      INT          i=0;
      while (pstring[i] != 0)
      { 
            i++; 
      }
      i++;     
      return i;
}

następnie wrzucam w IDE i widzę takie coś:

xor     eax, eax
cmp     byte ptr [ebp+var_18], 0
lea     edx, [ebp+var_18]
loc_401365:
inc     eax
cmp     byte ptr [eax+edx], 0
jnz     short loc_401365
loc_40136A:
     ...

No chyba każdy przyzna, że nie jest to najlepszy przykład kodu ...
Ciekaw jestem jakiejś fachowej opinii na ten temat jakichś innych przykładów lub kompilatorów .

0

Nie używaj DevCpp.

Nie, poważnie, nie używaj DevCpp. Ma starą wersję kompilatora, pełno bugów, jest opóźniony do świata o jakieś 10 lat.

xor     eax, eax
cmp     byte ptr [ebp+var_18], 0
lea     edx, [ebp+var_18]
loc_401365:
inc     eax
cmp     byte ptr [eax+edx], 0
jnz     short loc_401365
loc_40136A:

No chyba każdy przyzna, że nie jest to najlepszy przykład kodu ...

Dlaczego?
Głównym problemem jest niepotrzebny (ignorowany?) pierwszy cmp, ale biorąc pod uwagę że masz archaiczną wersję kompilatora...
Poza tym wygląda to tak, czyli minimalistyczny odpowiednik Twojego kodu:

eax = 0;
edx = argument_funkcji_text;
while(edx[eax] != 0) {
    edx++
}

Jaki kod byłby Twoim zdaniem najlepszym przykładem (tak, akurat da się to zrobić lepiej za pomocą różnych hacków na poziome bitowym - ale to jest bardzo trudne do wymyślenia dla człowieka, nie wspominając o kompilatorze i trzeba by było 'wywnioskować' z kodu że ta funkcja to akurat strlen, czyli mieć specjal-case dla rozpoznawania funkcji wbudowanej - bez sensu)?

Wykonajmy zresztą test:

#include <windows.h>
#include <stdio.h>

INT   ansiLength ( LPSTR pstring )
{
      INT          i=0;
      while (pstring[i] != 0)
      { 
            i++; 
      }
      i++;     
      return i;
}

int main() {
   printf("%d", ansiLength("alamakota"));
   return 0;
}

Kompilujemy:

T:\>g++ --version
g++ (GCC) 4.6.2
Copyright (C) 2011 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.


T:\>g++ a.cpp -O3
a.cpp: In function 'int main()':
a.cpp:16:39: warning: deprecated conversion from string constant to 'LPSTR {aka
char*}' [-Wwrite-strings]

Patrzymy na wynik:

.text:00401BAA                 xor     eax, eax
.text:00401BAC
.text:00401BAC loc_401BAC:                             ; CODE XREF: sub_401B9C+18j
.text:00401BAC                 inc     eax
.text:00401BAD                 cmp     ds:byte_403064[eax], 0
.text:00401BB4                 jnz     short loc_401BAC
.text:00401BB6                 inc     eax
.text:00401BB7                 mov     [esp+10h+var_C], eax
.text:00401BBB                 mov     [esp+10h+var_10], offset aD ; "%d"
.text:00401BC2                 call    printf
.text:00401BC7                 xor     eax, eax

Widać ładny inlining funkcji (szkoda że nie wyliczył tego w trakcie kompilacji, no ale nie można mieć wszystkiego - C++11-owe constexpr może pomoże). - funkcja ansiLength zniknęła i została połączona z printf.
Moim zdaniem całkiem ładny kawałek kodu.

0

Visual Studio raczej ma słabą optymalizację (nawet 2010, nowszego jeszcze nie testowałem) i czasem opłaca się "pomóc" wygenerować kompilatorowi lepszy kod.

0

Zainstalowałem MinGW w katalogu MinGW\bin mam około mnóstwo execów ciężko się połapać - to jest okienkowy w ogóle kompilator ?
Co do kodu no pewnie, że można a nawet powinno by to być coś z zastosowaniem do tego przeznaczonych instrukcji ( movs*, scas*, cmps* ...)

      

            xor       eax, eax
            mov       ecx, 0FFFFFFFFh
            mov       edi, pointer_do_stringa
            cld
            repne     scasb
            not       ecx
            xchg      eax, ecx  ; wynik do EAX
  

I to też nie była by najszybsza metoda - jeżeli chodzi o nowsze procki można by sprawdzać czy procesor posiada odpowiednie funkcje i korzystać z niemalże hll'owych instrukcji od i3 w górę procki (przenośnia ale tak to wygląda).

Gdzieś czytałem, że kompilator C++ można tak ustawić w opcjach żeby wypluwał kod wzbogacony o różne mmx'y do tego stopnia, że nie ma sensu próbować tego ręcznie prześcignąć - miałem nadzieje zobaczyć coś takiego po dekompilacji. Jest na to szansa ? Może to zły przykład jest ?

0

to jest okienkowy w ogóle kompilator

A znasz jakikolwiek okienkowy kompilator? o_O Co on by miał w tych okienkach wyświetlać?

0

Ja się tam nie znam, ale pokaże co produkuje u mnie:

Trochę zmodyfikowałem kod w C, ale to nie ma chyba żadnego znaczenia:

#include <stdio.h>
 
unsigned int ansiLength(const char *str) {
 unsigned int i = 0;
 
  while (str[i] != '\0') { 
    i++; 
  }
  
  i++;     
  return i;
}
 
int main() {
  printf("%u", ansiLength("alamakota"));
  return 0;
}

(Nie wiem po co to i++ na końcu, przez to wynik jest niepoprawny. Zostawiłem to jak jest)

Kompilacja:

endrju@kormoran smieciki % gcc --version
gcc (Gentoo 4.7.2 p1.1, pie-0.5.3) 4.7.2
(...)

endrju@kormoran smieciki % gcc -O3 -march=native -masm=intel -S ansiLengthTest.c

Wynik:

ansiLength:
.LFB22:
	.cfi_startproc
	cmp	BYTE PTR [rdi], 0
	je	.L4
	xor	eax, eax
	.p2align 5,,24
	.p2align 3
.L3:
	inc	eax
	mov	edx, eax
	cmp	BYTE PTR [rdi+rdx], 0
	jne	.L3
	inc	eax
	ret
.L4:
	mov	eax, 1
	ret
	.cfi_endproc

Co mi o tym powiecie? ;-) Teoretycznie GCC powinien teraz wykorzystać wszystkie znane mu instrukcje, jakie obsługuje mój procesor (Phenom II). Jak widać cudów nie robi.

0

Nie ma jakiejś magicznej instrukcji SSE o magicznej nazwie, która to robi? :]

0

Visual Studio 2010 (Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.30319.01 for 80x86) z włączonymi optymalizacjami (/Ox). VS2012 generuje identyczny kod.

xor     eax, eax
.text:00000032
.text:00000032 loc_32:                                 ; CODE XREF: _main+Aj
.text:00000032                 inc     eax
.text:00000033                 cmp     $SG66584[eax], 0
.text:0000003A                 jnz     short loc_32
.text:0000003C                 inc     eax
.text:0000003D                 push    eax
1

Gdzieś czytałem, że kompilator C++ można tak ustawić w opcjach żeby wypluwał kod wzbogacony o różne mmx'y do tego stopnia, że nie ma sensu próbować tego ręcznie prześcignąć - miałem nadzieje zobaczyć coś takiego po dekompilacji. Jest na to szansa ? Może to zły przykład jest ?

Kiedyś porównywałem sobie różne możliwości znalezienia minimum w tablicy czterech elementów. Sprawdzałem jak wygląda wynik kompilacji super brzydkiej funkcji:

float naive_min4(float data[4]) {
  float min = data[0];
  
  if (data[1] < min) {
    min = data[1];  
  }
  
  if (data[2] < min) {
    min = data[2]; 
  }
  
  if (data[3] < min) {
    min = data[3];  
  }
  
  return min;
}

I chyba nie jest on najgorszy:

_Z10naive_min4Pf:
.LFB996:
	.cfi_startproc
	movss	xmm0, DWORD PTR [rdi+4]
	movss	xmm1, DWORD PTR [rdi+8]
	minss	xmm0, DWORD PTR [rdi]
	minss	xmm1, xmm0
	movss	xmm0, DWORD PTR [rdi+12]
	minss	xmm0, xmm1
	ret
	.cfi_endproc

Jeżeli wymusi się kompilację pod i386 wynik jest obrzydliwy.

To tez może nie jest najszczęśliwszy przykład, ale pokazuje, że kompilator jest w stanie wykorzystać to co może procesor. Może zainteresuje Cię ten artykuł: http://locklessinc.com/articles/vectorize/ tematem jest zmuszenie GCC do wykorzystania wektorowych instrukcji SSE.

1 użytkowników online, w tym zalogowanych: 0, gości: 1