Gå til innhold

Anbefalte innlegg

Småinteressant diskusjon. Lurer på om LonelyMan er virkelig, eller om han snakker til oss gjennom et tidshull fra 50-tallet :D

 

Skulle gjerne programmert sammen med deg, og sett hva du kan få til. Klarer ikke å fatte at du kan ha så kontroll at du klarer å lage større og komplekse ting uten abstraksjonene som høyerenivå-språk gir oss. Men at jeg ikke kan fatte det betyr ikke at det ikke er mulig!

Lenke til kommentar
Videoannonse
Annonse

torbjørn, det er ikke enkelt for en c++ programmerer å skjønne. For meg så er det så viktig at jeg ikke får satt ord på det. Det handler om kontroll, det handler om størrelse på program, det handler om hastighet, det handler om fleksibilitet, det handler om utfordringen. Men aller mest handler det om hastighet.

 

For å ta ett lite eksempel på hva jeg kan gjøre i asm, ett helt simpelt eksempel:

 

I ett c++ program med GDI+ så kan du plotte omtrent 1000 pixler i en gitt frame før du opplever problemer.

 

Bare ett lite eksempel, det er en hel verden mellom c++ og asm når det kommer til hastighet. Det fins ikke en sjanse i verden å likestille de to. :tease:

 

Jeg har plottet halve universet i det øyeblikket ett generelt c++ program har plottet halve skjermen.

 

Denne "gevinsten" gjelder ikke bare grafikk, det gjelder alt du gjør i asm. cool.gif

Endret av LonelyMan
Lenke til kommentar

c++ er bra hvis du ønsker å lage programmer for mange plattformer og hvis du ønsker å utvikle programmer raskt. Du kan også i mange tilfeller lage raske programmer, men det blir med det. Bare fordi det oppleves som raskt betyr ikke at det er raskt.

 

Ett c++ program har opp mot 10 ganger mer minnereferering totalt enn ett typisk asm program jeg har skrevet selv. Hvis ett c++ program kjører i ett par sekunder og vi antar det er veldig stort, så vil det måtte lese 100 MB fra minnet hvor mitt program (akkumulert sett) bare har lest 5-10 MB. Mindre minnereferering gjør det uhorvelig mye raskere.

 

Denne minnebruken kan du ikke kontrollere godt i c++. Nesten (eller totalt) umulig. Og dette igjen gjør at c++ programmer ofte er begrenset i hastighet til minnehastigheten og ikke til prosessorhastigheten. Det er som en "slede" som glir gjennom smør, det er motstand hele veien mens ett typisk godt skrevet asm program flyter som gjennom lufta fordi den unngår ekstremt mye minnereferering.

Endret av LonelyMan
Lenke til kommentar

En annen ting som jeg setter ekstremt stor pris på er at jeg vet hvordan jeg kan lage nesten og bortimot uknekkelig kode, de antidebug triksene du kan utføre ved hjelp av asm og macroer er utrolig, jeg ønsker ikke å gå i detaljer, men dette er ting som er helt umulig i c++. Dette har også stor verdi, de mulighetene man har til å skalere programmet i alle mulige retninger.

 

Mange personer vil sikkert fortelle "Det er umulig å gjøre ett program uknekkelig, noen klarer alltid å bryte koden"

 

Mitt svar til dette er: Ja det er sant at c++ utviklere som regel setter av en uke til å forsøke å implementere noen horride antidebug triks, ja disse programmene vil alltid knekkes. C++ utviklere er spesialisert i å lage programmer, ikke antidebugging, så det er naturlig at de ødelegger ryktene på dette området.

Endret av LonelyMan
  • Liker 1
Lenke til kommentar

Det handler om kontroll, det handler om størrelse på program, det handler om hastighet, det handler om fleksibilitet, det handler om utfordringen. Men aller mest handler det om hastighet.

En annen ting som jeg setter ekstremt stor pris på er at jeg vet hvordan jeg kan lage nesten og bortimot uknekkelig kode,

Jeg skjønner hva du sier, og ser poenget.

 

Og likevel koder jeg i Ruby, som kjører på en tolker skrevet i .NET-kode, som kompileres til CIL-kode som kjøres på en virtuell maskin skrevet i C/C++, og vet du hva..., det er mer enn raskt nok for de oppgavene jeg bruker det til!

 

Og jeg lager løsninger som krever høy grad av throughput. Da bruker jeg ikke Ruby, men C# - fortsatt på den virtuelle maskinen - gjør jobben så bra at vi aldri blir noen flaskehals, vi tar unna så raskt som nettverket klarer å levere data, og sender ut så raskt som nettverket klarer å ta imot. Hva skal jeg med asm da?

 

Uknekkelig kode er fint det, om det er noen fare for at man blir "knukket", for å si det sånn. Den bekymringen har jeg ikke...

 

Poenget mitt, eller spørsmålet, var at jeg ikke ser at det kan være mulig å løse oppgavene jeg løser i teamet mitt i håndkodet assembly. Hvordan implementerer du et system som løses med 1 million linjer høyerenivå-kode i ren assembly? Uten at skjegget ditt blir så langt og stort at det hverken er mulig å se skjermen eller finne tastaturet?

  • Liker 1
Lenke til kommentar

Om du arbeider med dette til daglig så skal du ikke se tilbake ett sekund, tenk penger hele veien. asm er en hobby (og ett verktøy)

 

Man kan lage veldig flotte biblioteker i asm og så bruke dem i c++.

 

Apropos (løse 1 million linjer i høynivåspråk), det er en myte, en vrangforestilling og t.o.m en fiktiv frykt folk har opparbeidet seg at det tar så utrolig mye lengre tid å kode i asm.

 

1: en million linjer i høynivåspråk blir ikke automatisk 10 millioner linjer i asm.

 

2: vi har biblioteker i asm også, alle basiske funksjoner, string funksjoner, matte funksjoner, grafikk funksjoner og alle windows api'er er selvfølgelig tilgjengelige.

 

3: Alle c biblioteker som eksisterer kan konverteres til assembly include files og brukes på nøyaktig samme måten, uten større problemer. Man bruker ett simpelt verktøy for å konvertere header og include filer.

 

4: Jeg vil faktisk påstå at problemet med c vs asm er at i c så må du hele tiden gå inn i bibliotekene og skjønne hvordan han som laget det, tenkte. I asm så slipper du all denne "overhead" informasjonen som du hele tiden må gravse og tafse i. Og når en ikke gravser og tafser i bibliotekene så må en skjønne språket fullt ut. I asm er der en så lett standard, du vet hvor instruksjonene er, du vet hva de gjør og mer er det ikke.

Endret av LonelyMan
Lenke til kommentar

Ett eksempel på TEXTEQU i asm.

--------------------------

Plott TEXTEQU <PlotPixel>

--------------------------

 

Plott 10, 10, 0FFFF0000h (x=10 y=10 farge=rød)

Plott 10, 10, 0FF00FF00h (x=10 y=10 farge=grønn)

Plott 10, 10, 0FF0000FFh (x=10 y=10 farge=blå)

Plott 10, 10, 0FFFF0000h (x=10 y=10 farge=rød)

Plott 10, 10, 0FFFF0000h (x=10 y=10 farge=rød)

Plott 10, 10, 0FFFF0000h (x=10 y=10 farge=rød)

 

 

Her antar vi at PlotPixel er en macro. og du plotter noen pixler ved hjelp av immediates, dette er bare ett eksempel.

 

La oss si du har 1000 plasser i asm programmet hvor du kaller PlotPixel, alt du behøver å endre på er TEXTEQU på toppen.

 

Du kan kombinere TEXTEQU med konstanter, med macroer og med kondisjonell assembly, alle disse 4 tingene gjør asm utrolig fleksibelt, du kan prinsippielt endre hele funksjonaliteten til ditt asm program ved å bruke disse 4 metodene.

Lenke til kommentar

Ett eksempel på kondisjonell asm (dvs, det skjer under assembleringen, ikke under kjøring av programmet)

 

definer denne i include filen eller hvor som helst

-----------

USE_SSE = 1

-----------

 

...............

 

 

IF USE_SSE EQ 1

.XMM

ENDIF

 

// her sjekker assembleren om USE_SSE er er 1, hvis den er det, så vil assembleren inkludere .XMM statement og dermed gjør programmet i stand til å kode sse og mmx.

 

senere i programmet kan du videreføre conditional assembly til noe slikt som dette:

 

IF USE_SSE EQ 1

mov eax, 10

mov ecx, 20

ELSE

mov edx, 30

ENDIF

 

Hvis USE_SSE er 1 vil bare de to instruksjonene bli assemblert av assembleren. Dvs, den fjerner den tredje helt fra exe fila og denne typen koding gjør programmer mindre totalt sett.

Endret av LonelyMan
Lenke til kommentar

Ett eksempel på microsoft macro assembler sin høynivå syntaks du kan bruke i asm:

 

.IF eax==10
   INVOKE ExitProcess, eax
.ELSE
   INVOKE MessageBox, hWnd, OFFSET Text, OFFSET Caption, MB_OK
.ENDIF

 

 

hvis eax registeret er 10, så vil exitprocess kjøre, ellers vil en messageboks dukke opp.

Endret av LonelyMan
Lenke til kommentar

Ett eksempel på vindus handler i c++ vs asm:

============================================

 

c++:

LRESULT CALLBACK WndProc(   HWND hwnd,
                           UINT message,
                           WPARAM wparam,
                           LPARAM lparam )
{
   switch( message )
   {
   case WM_CREATE:

       return 0;
       break;

   case WM_PAINT:
       {
       }
       return 0;
       break;

   case WM_DESTROY:
       PostQuitMessage( 0 ) ;
       return 0;
       break;

   }

 

asm:

 

WndProc proc hWnd:HWND, uMsg:UINT, wParam:WPARAM, lParam:LPARAM

.IF uMsg==WM_CREATE

	...

.ELSEIF uMsg==WM_PAINT

               ...

.ELSEIF uMsg==WM_DESTROY

               INVOKE PostQuitMessage, 0

.ELSE

	INVOKE DefWindowProc, hWnd, uMsg, wParam, lParam
	ret

.ENDIF

xor eax,eax
ret

WndProc endp 

 

 

Dette kan da umulig være så veldig mye mer komplisert enn folk påstår.

Endret av LonelyMan
Lenke til kommentar

Ett eksempel på en helt simpel funksjon for å legge sammen to tall i asm:

 

LeggSammen PROC Tall1:DWORD, Tall2:DWORD
   mov eax, Tall1
   mov ecx, Tall2
   add eax, ecx
   ret
LeggSammen ENDP

 

og når du skal kalle disse kan du f.eks gjøre slik:

 

INVOKE LeggSammen, 10, 20

 

da vil funksjonen returnere 30 i eax.

Endret av LonelyMan
Lenke til kommentar

Igjen synes jeg det er flott at du elsker asm, og det er kjekt å ha et fleksibelt språk å jobbe med, spesielt som en hobby. Vil likevel diskutere litt mer...

 

2: vi har biblioteker i asm også, alle basiske funksjoner, string funksjoner, matte funksjoner, grafikk funksjoner og alle windows api'er er selvfølgelig tilgjengelige.

 

3: Alle c biblioteker som eksisterer kan konverteres til assembly include files og brukes på nøyaktig samme måten, uten større problemer. Man bruker ett simpelt verktøy for å konvertere header og include filer.

 

Så du er villig til å bruke bibloteker, selv de som er skrevet i C. Koden du benytter kan vel da umulig være optimal, utifra det du har sagt tidligere?! Hvis du skal gjøre noe av en viss størrelse vil kjøretiden til programmet ditt i nokså stor grad befinne seg i biblotekene-for å si det sånn. Gjør ikke dette overheadet det nesten meningsløst å bruke asm?

 

Uansett.., siden du poster litt eksempler så kunne jeg vært veldig interessert i å se en implementasjon av Euler-oppgave 1 implementert i assembly. På bloggen min nå i desember kan du se den oppgaven implementert i 24 ulike språk, men assembly er ikke ett av dem. Er veldig lysten på å se hva som skal til for å skrive ut summen av alle multipler av 3 eller 5.

 

Tar du utfordringen?

Lenke til kommentar

Får tenke på det, da måtte jeg sette meg inn i algoritmen først, så må jeg overføre c++ syntaksen til pseudo og så må jeg implementere det, deretter optimalisere den. Det tar tid å gjøre det, men her er sikkert en haug av folk i asm forumet som har tid og lyst, men forklar gjerne hva algoritmen gjør for noe. Det er interessant dog. :)

 

EDIT: Det er en relativt simpel funksjon, jeg antar det kan gjøres på 15 linjer asm instruksjoner (bare sånn rask antakelse). Men å optimalisere den så må jeg analysere bitene i konstantene, mulig jeg kan benytte parity flags her til noe konstruktivt, men modulus er jo rett frem da. Den får du i edx etter en div instruksjon.

Endret av LonelyMan
Lenke til kommentar

Ok nå har jeg skrevet en rutine, men den er på ingen måte optimalisert, men det er bare for å vise hva som skal til.

 

Multipler PROC
push esi
push edi
push ebx
push ebp
movd mm0, esp
mov ebx, 1000         ; Sett denne til loopcounter (1000 f.eks)
mov esi, 3
mov edi, 5
mov ebp, ebx
xor esp, esp
again:
mov eax, ebx
xor edx, edx
div esi
test edx, edx
jnz nomatch1
add esp, ebx
jmp nomatch2
nomatch1:
mov eax, ebx
xor edx, edx
div edi
test edx, edx
jnz nomatch2
add esp, ebx
nomatch2:	
sub ebx, 1
jnz again
mov eax, esp
movd esp, mm0
pop ebp
pop ebx
pop edi
pop esi
ret
Multipler ENDP

 

Summen av multipliers er nå i eax

 

Tiden denne rutinen bruker på 100 millioner tall er 936 millisekunder i uoptimalisert form.

Endret av LonelyMan
Lenke til kommentar

Over 20 av instruksjonene i rutinen er "rutinemessig" forberedelse av rutinen og involverer ikke at en "tenker" for å få til, det er bare noen fåtall instruksjoner som er relevante, derfor kan det se ut som om assembler er så vanskelig, men de fleste instr. du ser her er bare rutinemessig forberedelse.

Lenke til kommentar

Du kunne f.eks skrevet en bittelitt tregere variant, med ferre instruksjoner, slik som dette:

 

GetModifier PROC Divident:DWORD, Divider:DWORD
  mov eax, Divident
  div Divider
  mov eax, edx
  ret
GetModifier ENDP

 

og så kunne du loopet gjennom alle tall ved å kalle GetModifier og teste om resultatet er 0.

 

Grunnen til at den forrige rutinen er så lang er fordi den krever litt ekstra "forberedelse" for å utnytte prosessoren maksimalt, istedet for å måtte være avhengig av så mye minnereferering. Så ikke la deg lure av størrelsen på den forrige rutinen, det fins mange måter å gjøre ting på i asm.

Endret av LonelyMan
Lenke til kommentar

Igjen synes jeg det er flott at du elsker asm, og det er kjekt å ha et fleksibelt språk å jobbe med, spesielt som en hobby. Vil likevel diskutere litt mer...

 

2: vi har biblioteker i asm også, alle basiske funksjoner, string funksjoner, matte funksjoner, grafikk funksjoner og alle windows api'er er selvfølgelig tilgjengelige.

 

3: Alle c biblioteker som eksisterer kan konverteres til assembly include files og brukes på nøyaktig samme måten, uten større problemer. Man bruker ett simpelt verktøy for å konvertere header og include filer.

 

Så du er villig til å bruke bibloteker, selv de som er skrevet i C. Koden du benytter kan vel da umulig være optimal, utifra det du har sagt tidligere?! Hvis du skal gjøre noe av en viss størrelse vil kjøretiden til programmet ditt i nokså stor grad befinne seg i biblotekene-for å si det sånn. Gjør ikke dette overheadet det nesten meningsløst å bruke asm?

 

Uansett.., siden du poster litt eksempler så kunne jeg vært veldig interessert i å se en implementasjon av Euler-oppgave 1 implementert i assembly. På bloggen min nå i desember kan du se den oppgaven implementert i 24 ulike språk, men assembly er ikke ett av dem. Er veldig lysten på å se hva som skal til for å skrive ut summen av alle multipler av 3 eller 5.

 

Tar du utfordringen?

 

Du har ikke Python på bloggen, så vidt jeg kunne se - den blir svært enkel (selv om det er litt off-topic her):

 

sum([i for i in range(1000) if (i % 3 == 0) or (i % 5 == 0)])

 

Skal man lage det generelt :

 

def sum3or5(m) :

return sum([i for i in range(m) if (i % 3 == 0) or (i % 5 == 0)])

 

sum3or5(1000)

 

 

Jeg regner med at du er klar over at det fins en O(0) algoritme istedenfor O(1) metoden i bloggen din? Det er alltid viktigst å finne den beste algoritmen før man begynner å optimisere...

Lenke til kommentar

Siden vi diskutere hvor ueffektivt C++ er, hva med dette?

 

 

template <int n>

int div3or5() { return (((n % 3) == 0) || ((n % 5) == 0)) ? n : 0; }

 

// Note - we want sum to less than n, so "call" div3or5 with n-1

template <int n>

int sumDiv() { return sumDiv<n - 1>() + div3or5<n - 1>(); }

 

template <>

int sumDiv<0>() { return 0; }

 

int test10() {

return sumDiv<10>();

}

 

int test100() {

return sumDiv<100>();

}

 

int test1000() {

return sumDiv<1000>();

}

 

 

Kompilasjon tar litt tid, og kanskje krever at man øker template depth (-ftemplate-depth=1000 med gcc), men genererte koden er jo optimalt.

 

En versjon som skalere bedre (med lavere template depth) men litt lengre kompileringstider er:

 

template <int n>

int div3or5() { return (((n % 3) == 0) || ((n % 5) == 0)) ? n : 0; }

 

// Sum from a up to but not including b

template <int a, int b>

int partSum() {

if (a == (b - 1)) {

return div3or5<a>();

}

return partSum<a, ((a + b) / 2)>() + partSum<((a + b) / 2), b>();

}

 

template <>

int partSum<0, 0>() { return 0; }

 

int partTest1000() {

return partSum<0, 1000>();

}

  • Liker 1
Lenke til kommentar

Første steg i optimaliseringen, nå er koden 9% raskere og kjører på 858 ms. (100 millioner tall), det høres kanskje ikke mye ut til å begynne med, men denne økningen betyr at du kan prosessere 9 millioner ekstra tall i samme tidsmengde.

 

OPTION PROLOGUE:NONE
OPTION EPILOGUE:NONE
ALIGN 4
Multipler PROC
push ebx
push esi
push edi
push ebp
mov ebx, 100000000
mov esi, 3
mov edi, 5
xor ebp, ebp
ALIGN 16
n1:	mov eax, ebx
xor edx, edx
div esi
test edx, edx
jz n2
mov eax, ebx
xor edx, edx
div edi
test edx, edx
jnz n3
n2:	add ebp, ebx
n3:	sub ebx, 1
jnz n1
mov eax, ebp
pop ebp
pop ebx
pop edi
pop esi
ret
Multipler ENDP
OPTION PROLOGUE:PROLOGUEDEF
OPTION EPILOGUE:EPILOGUEDEF

Endret av LonelyMan
Lenke til kommentar

Opprett en konto eller logg inn for å kommentere

Du må være et medlem for å kunne skrive en kommentar

Opprett konto

Det er enkelt å melde seg inn for å starte en ny konto!

Start en konto

Logg inn

Har du allerede en konto? Logg inn her.

Logg inn nå
×
×
  • Opprett ny...