torbjørn marø Skrevet 12. desember 2011 Del Skrevet 12. desember 2011 Småinteressant diskusjon. Lurer på om LonelyMan er virkelig, eller om han snakker til oss gjennom et tidshull fra 50-tallet 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
LonelyMan Skrevet 16. desember 2011 Del Skrevet 16. desember 2011 (endret) 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. 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. Endret 14. mai 2012 av LonelyMan Lenke til kommentar
LonelyMan Skrevet 16. desember 2011 Del Skrevet 16. desember 2011 (endret) 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 16. desember 2011 av LonelyMan Lenke til kommentar
LonelyMan Skrevet 16. desember 2011 Del Skrevet 16. desember 2011 (endret) 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 16. desember 2011 av LonelyMan 1 Lenke til kommentar
torbjørn marø Skrevet 16. desember 2011 Del Skrevet 16. desember 2011 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? 1 Lenke til kommentar
LonelyMan Skrevet 16. desember 2011 Del Skrevet 16. desember 2011 (endret) 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 16. desember 2011 av LonelyMan Lenke til kommentar
LonelyMan Skrevet 16. desember 2011 Del Skrevet 16. desember 2011 Ett eksempel på å kalle funksjoner i c++ vs asm. c++: if (!InitializeCriticalSectionAndSpinCount(&CriticalSection, 0x80000400) ) return; asm: INVOKE EnterCriticalSection, OFFSET RenderCritical Hvilke av disse ser mest tungvint ut? Lenke til kommentar
LonelyMan Skrevet 16. desember 2011 Del Skrevet 16. desember 2011 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
LonelyMan Skrevet 16. desember 2011 Del Skrevet 16. desember 2011 (endret) 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 16. desember 2011 av LonelyMan Lenke til kommentar
LonelyMan Skrevet 16. desember 2011 Del Skrevet 16. desember 2011 (endret) 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 16. desember 2011 av LonelyMan Lenke til kommentar
LonelyMan Skrevet 16. desember 2011 Del Skrevet 16. desember 2011 (endret) 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 16. desember 2011 av LonelyMan Lenke til kommentar
LonelyMan Skrevet 16. desember 2011 Del Skrevet 16. desember 2011 (endret) 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 16. desember 2011 av LonelyMan Lenke til kommentar
torbjørn marø Skrevet 16. desember 2011 Del Skrevet 16. desember 2011 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
LonelyMan Skrevet 16. desember 2011 Del Skrevet 16. desember 2011 (endret) 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 16. desember 2011 av LonelyMan Lenke til kommentar
LonelyMan Skrevet 16. desember 2011 Del Skrevet 16. desember 2011 (endret) 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 16. desember 2011 av LonelyMan Lenke til kommentar
LonelyMan Skrevet 16. desember 2011 Del Skrevet 16. desember 2011 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
LonelyMan Skrevet 16. desember 2011 Del Skrevet 16. desember 2011 (endret) 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 16. desember 2011 av LonelyMan Lenke til kommentar
David Brown Skrevet 16. desember 2011 Del Skrevet 16. desember 2011 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
David Brown Skrevet 16. desember 2011 Del Skrevet 16. desember 2011 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>(); } 1 Lenke til kommentar
LonelyMan Skrevet 16. desember 2011 Del Skrevet 16. desember 2011 (endret) 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 16. desember 2011 av LonelyMan Lenke til kommentar
Anbefalte innlegg
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 kontoLogg inn
Har du allerede en konto? Logg inn her.
Logg inn nå