PB im Vergleich mit anderen Sprachen in Bezug auf Speed

Anfängerfragen zum Programmieren mit PureBasic.
Benutzeravatar
Mok
BotHunter
Beiträge: 1484
Registriert: 26.12.2005 14:14
Computerausstattung: MSI GX780R
Intel Core i5-2410M
Nvidia GT 555M
Windows 7 Home Premium 64 bit
Wohnort:   

Re: PB im Vergleich mit anderen Sprachen in Bezug auf Speed

Beitrag von Mok »

Den Code bis ins allerkleinste Eck optimieren kann man sowieso nur mit Inline Assembler.
Hab gerade keinen Bock, ein Beispiel zu kompilieren und assemblieren, falls jemand unbedingt ein Beispiel will, kann ich aber gerne eines posten!
Win 7 Home Premium 64 bit | PureBasic 5.20 - x86 und x86-64 | Firefox [aktuelle stable-Version hier einfügen]
"Jeder macht irgendwann mal Fehler, darum gibt's auch Bleistifte mit Radiergummi." --Carl
c4s
Beiträge: 1235
Registriert: 19.09.2007 22:18

Re: PB im Vergleich mit anderen Sprachen in Bezug auf Speed

Beitrag von c4s »

Hier ein umfangreiches Beispiel was man so alles optimieren kann. Code funktioniert zwar nicht mit PB 4.51 und ich weiß auf die schnelle nicht wordan es liegt, aber ich denke er ist trotzdem sehr veranschaulichend:

Code: Alles auswählen

;
; Small ASM tutorial
; Petit TUT ASM
;
; F.Weil 20040617
;
; This listing is a workshop about ASM insertion in PureBasic code. Nothing exhaustive but a possible first basis.
;
; The workshop is based on 'les taches de Martin', meaning each point of an image is set according to the average of it's neighbors.
; A limited color palette is necessary to render appropriate effect.
;
; I propose to use a regular 32 bits depth for the screen and to solve this by calculating the final color of each point.
;
; Focus is made on how to address the drawing buffer directly.
;
; Purpose of this workshop is not to show that it is possible to do much better than PureBasic as it demonstrates the opposite.
; Only in some cases using ASM will help, but most of the time not. This workshop shows how to code in PureBasic well, and how
; few improvements can be obtained using ASM coding.
;
; For a better user experience, it is possible to parse methods execution by using left / right keyboard arrows, and to change
; drawing area size using NumPad4 / NumPad6
;
; The algorithm name, frames per second obtained and drawing size are displayed in real time.
;================================================================================
; Ce listing a pour but de montrer comment étudier la transposition en ASM de certaines parties de code.
; Rien d'exhaustif mais une prise en main qui peut donner de bonnes bases.
;
; Reprise du principe des taches de Martin
;
; LÃ  j'ai mis l'accent sur la gestion directe du DrawingBuffer
;
; L'algorithme proposé est de même principe que celui d'origine pour les taches de Martin, c'est à dire
; pour chaque point P(X, Y) on associe une couleur calculée à partir de la moyenne des couleurs des 4 points
; voisins + 1
; Soit couleur(P(X, Y)) = (couleur(P1(X, Y - 1)) + couleur(P2(X - 1, Y)) + couleur(P3(X + 1, Y)) + couleur(P4(X, Y + 1))) / 4 + 1
;
; Pour rendre l'effet escompté on doit travailler dans une palette limitée.
;
; Pour faire les choses sans passer par un screen en 8 bits ou autre chose, je prends le mode par défaut de l'écran en 32 bits, et
; je convertis en recalculant couleur
;
; couleur = couleur % 64 + 192
; couleur = couleur << 10 + couleur
;
; Le but de cette démonstration ne consiste pas à montrer des carences de PureBasic, bien au contraire. Il ressort que l'utilisation de l'ASM
; n'apporte que peu d'améliorations si le codage PureBasic est bien conçu par le développeur.
;
; Pour une meilleure expérimentation des méthodes ici, il est possible de naviguer sur les différents types de code en utilisant les
; flèches gauche / droite du clavier et de changer la taille de la zone de dessin avec les touches 4 / 6 du clavier numérique.
;
; Un affichage temps réel de l'algorithme utilisé, du nombre de trames par seconde et de la taille du dessin est effectué.
;

; This enumeration is made to make the different codes to executes easier to access
; Also the codes names are placed in strings to display stats
;
; Cette énumération est mise en place pour rendre l'accès aux différents algorithmes plus aisés.
; Les noms des codes à exécuter sont également placés dans un tableau de chaînes pour l'affichage des stats.

;
; Here is a chart of experimental results (inFPS) I found on my design PC
; Voici un tableau des résultats expérimentaux (en FPS) obtenus sur mon PC de développement
;
; FlipBuffers(1)                        128x128    256x256    320x240    480x360    640x480    800x600
; PureBasic_Regular_Code             4              2               1              -                -              -
; PureBasic_Better_Code             60             30             30            21              10             8
; PureBasic_Optimized_Code       60             30             30            22              10             8
; PureBasic_LowLevel_Code        60             30             30            22              10             8
; PureBasic_Chewed_Code          60             30             30            19                9             7
; ASM_Code                               60             30             30            23              10             8
; PureBasic_LowLevel_Code2       60             59             59            45              20           15
; ASM_LowLevel_Code2              60             60             59             50              22           16
;       
; FlipBuffers(0)                        128x128    256x256    320x240    480x360    640x480    800x600
; PureBasic_Regular_Code             5              2               1              -                -              -
; PureBasic_Better_Code            260            55             46            21              12              8
; PureBasic_Optimized_Code       251            50             49           22               11              8
; PureBasic_LowLevel_Code        251            50             49           22               11              8
; PureBasic_Chewed_Code         216            44             42            19              10              7
; ASM_Code                              255            55             52            23              12              9
; PureBasic_LowLevel_Code2      604          126            100           45              27             17
; ASM_LowLevel_Code2              627          143            113           51              30             20
;
; Results measured on my 1.2GHz / 32MB graphic PC
; Résultats mesurés sur mon PC 1,2GHz / Graphique 32MO
;

Enumeration
  #PureBasic_Regular_Code
  #PureBasic_Better_Code
  #PureBasic_Optimized_Code
  #PureBasic_LowLevel_Code
  #PureBasic_Chewed_Code
  #ASM_Code
  #PureBasic_LowLevel_Code2
  #ASM_LowLevel_Code2
EndEnumeration

#LastCode = #ASM_LowLevel_Code2

Global tz.l, FPS.l, NFrames.l, AFrames.l, TFrames.l ; Stats

;
; ResetAll allows to clear the screen's buffers and thearrays used in the program
; ResetAll permet d'effacer les buffers et de remettre les tableaux utilisés à zéro.
Procedure ResetAll()
;  FlipBuffers(1)
;  ClearScreen(0)
;  FlipBuffers(1)
;  ClearScreen(0)
  Dim DrawingArray.l(1024, 768)
  Dim DrawingArea.l(1024 * 768)
  tz = ElapsedMilliseconds()
  FPS = 0
  NFrames = 0
  AFrames = 0
  TFrames = 0
EndProcedure

ExecuteCode = #ASM_Code

Dim CodeType.s(10)
Dim DrawingWidth.l(10)
Dim DrawingHeight.l(10)

Global FontID1 = LoadFont(23, "Verdana", 8, #PB_Font_HighQuality | #PB_Font_Bold)

; A list of code parts names is placed in the DataSection
; Une liste des noms des parties du code est placée en DataSection.
For i = 0 To #LastCode
  Read CodeType(i)
  CodeType(i) + Space(40 - Len(CodeType(i)))
Next

LastSize = 5
WidthHeight = 3

; A list of drawing sizes is placed in DataSection
; Une liste des tailles de zone de dessin est donnée en DataSection
For i = 0 To LastSize
  Read DrawingWidth(i)
  Read DrawingHeight(i)
Next

;
; DrawingArray is a 2 dimensions array to store screen's x, y pixels in some part of the program
; DrawingArea is a single dimension array used in the same purpose in some other parts
;
; DrawinArray est un tableau à 2 dimensions utilisé pour mémoriser les pixels x, y de l'écran dans certaines parties du programme
; DrawingArea est un tableau à une seule dimension utilisé dans le même but, mais dans d'autres parties.
;
Dim DrawingArray.l(1024, 768)
Dim DrawingArea.l(1024 * 768)

; The current drawing width and heigth are set and xTop, yTop, xBottom, yBottom are bounds
; La largeur et la hauteur courante pour le dessin sont initalisées ainsi que les limites xTop, yTop, xBottom, yBottom
DrawingWidth = DrawingWidth(WidthHeight)
DrawingHeight = DrawingHeight(WidthHeight)
ScreenXSize = GetSystemMetrics_(#SM_CXSCREEN)
ScreenYSize = GetSystemMetrics_(#SM_CYSCREEN)
xTop = (ScreenXSize - DrawingWidth) / 2 + 1
yTop = (ScreenYSize - DrawingHeight) / 2 + 1
xBottom = xTop + DrawingWidth - 2
yBottom = yTop + DrawingHeight - 2

;
;
;
  If InitKeyboard() And InitSprite() And OpenScreen(ScreenXSize, ScreenYSize, 32, "Taches de Matin")
      StartDrawing(ScreenOutput())
        DrawingFont(FontID1)
      StopDrawing()
      Repeat
        ;
        ; FlipBuffers is used with two possible argument value : 0 without buffer buffer sync which is shorter but
        ; may generate more flickering on the screen, or 1 to wait the buffer ready before to display it.
        ;
        ; Mode change is accesible using up / down arrows
        ;=========================================================================
        ; On utilise la commande FlipBuffers(0) (sans synchronisation plus rapide mais qui peut produire des scintillements
        ; ou FlipBuffers(1) qui attend que le buffer soit prêt avant affichage.
        ;
        ; Le changement de mode se fait en appuyant les flèches haut ou bas.
        ;
        FlipBuffers(FlipBuffers)
        StartDrawing(ScreenOutput())
          Select ExecuteCode
            ;
            ; To start the following code just do what the top comments describe, using Point(x, y) to know a pixel value. Neighbors are
            ; injected in a formula and the given point set using Plot.
            ;=====================================================================================
            ; Pour commencer voici dans une écriture conventionnelle le code correspondant à la spécification donnée en commentaire ci-dessus.
            ;
            ; Pour l'ensemble des points à traiter, on calcule la somme des valeurs des 4 points adjacents que l'on injecte dans une formule.
            ;
            ; Le point courant est ensuite simplement dessiné à la position voulue.
            ;
            Case #PureBasic_Regular_Code
              For x = 1 To DrawingWidth - 1
                For y = 1 To DrawingHeight - 1
                  Value = ((Point(x + xTop, y + yTop - 1) + Point(x + xTop, y + yTop + 1) + Point(x + xTop + 1, y + yTop) + Point(x + xTop - 1, y + yTop)) / 4 + 1) % 64 + 192
                  Color = Value << 10 + Value
                  Plot(x + xTop, y + yTop, Color)
                Next
              Next
            ;
            ; In #PureBasic_Better_Code, we do no more use Point(x, y) but an 2 dimensions array to store points values.
            ;
            ; This array contains elements corresponding to screen pixels.
            ;
            ; Using this makes the listing easy to understand but the performances are much better
            ;=======================================================================================
            ; Dans la version #PureBasic_Better_Code on ne va plus utiliser la fonction Point(x, y) mais un tableau qui permet de mémoriser les
            ; valeurs des points.
            ;
            ; Ce tableau est à deux dimensions et chaque élément x, y du tableau représente fidèlement un point de l'écran
            ;
            ; Par contre le tracé du point courant est fait avec la fonction Plot, dont les performances restent assez bonnes.
            ;
            ; De là l'écriture est encore assez lisible ... et les performances très différentes
            ;
            Case #PureBasic_Better_Code
              For x = 1 To DrawingWidth - 1
                For y = 1 To DrawingHeight - 1
                  Value = ((DrawingArray(x, y - 1) + DrawingArray(x, y + 1) + DrawingArray(x - 1, y) + DrawingArray(x + 1, y)) / 4 + 1) & 63 + 192
                  Color = Value << 10 + Value
                  DrawingArray(x, y) = Color
                  Plot(x + xTop, y + yTop, Color)
                Next
              Next
            ;
            ; In #PureBasic_Optimized_Code a single dimension array is used.
            ;
            ; This makes the code a bit more difficult to understand but will make easier later optimization.
            ;=============================================================================
            ; Dans la version #PureBasic_Optimized_Code on utilise un tableau linéaire et non plus une matrice à deux dimensions.
            ;
            ; L'écriture du code est sensiblement plus lourde mais permettra d'aborder la phase d'optimisation suivante lus aisément.
            ;
            Case #PureBasic_Optimized_Code
              For x = 1 To DrawingWidth - 1
                For y = 1 To DrawingHeight - 1
                  xy = y * DrawingWidth + x
                  Value = ((DrawingArea(xy - DrawingWidth) + DrawingArea(xy + DrawingWidth) + DrawingArea(xy - 1) + DrawingArea(xy + 1)) / 4 + 1) & 63 + 192
                  Color = Value << 10 + Value
                  DrawingArea(xy) = Color
                  Plot(x + xTop, y + yTop, Color)
                Next
              Next
            ;
            ; #PureBasic_LowLevel_Code uses both the array and a direct calculation of pixels addresses to the drawing buffer.
            ;
            ; This calculation will not make performances better right now, but will help to master direct copy of array values to pixels
            ;====================================================================================
            ; Dans #PureBasic_LowLevel_Code, on va utiliser simultanément un tableau pour mémoriser les points et un calcul de l'adresse des
            ; points dans le buffer d'écran.
            ;
            ; Ce calcul n'apportera pas directement d'améliorations de performances, mais permettra de maîtriser la copie directe d'une valeur du tableau sur l'écran.
            ;
            Case #PureBasic_LowLevel_Code
              DrawingBuffer = DrawingBuffer()
              DrawingBufferPitch = DrawingBufferPitch()
              For x = 1 To DrawingWidth - 1
                X4 = DrawingBuffer + (x + xTop) << 2
                For y = 1 To DrawingHeight - 1
                  xy = y * DrawingWidth + x
                  Value = ((DrawingArea(xy - DrawingWidth) + DrawingArea(xy + DrawingWidth) + DrawingArea(xy - 1) + DrawingArea(xy + 1)) / 4 + 1) & 63 + 192
                  Color = Value << 10 + Value
                  DrawingArea(xy) = Color
                  Color = (Color & $FF0000) >> 16 + (Color & $00FF00) + (Color & $0000FF) << 16
                  Address = DrawingBufferPitch * (y + yTop) + X4
                  PokeL(Address, Color)
                Next
              Next
            ;
            ; Here we try to "chew" the former code in order to get only simple low level instructions.
            ;
            ; Only one operation per line and no more than two variables in a single line.
            ;
            ; Thanks to PureBasic coding convention we can write :
            ;
            ; a + 1 instead of a = a + 1
            ; a + b instead of a = a + b
            ;
            ; etc
            ;
            ; c = a + b will be replaced by
            ; c = a
            ; c + b
            ;
            ; The resulting code will not be optimized as good as the compiler does, but it will be fast and close to machine level.
            ;===========================================================================
            ; Le but du code développé ici est de se rapprocher le plus possible des instructions les plus élémentaires.
            ;
            ; On essaye de proscrire toute écriture qui consiste à cumuler plusieurs opérations sur une même ligne
            ;
            ; Pour y parvenir il suffit d'ajouter des variables temporaires lorsque c'est nécessaire.
            ;
            ; Dans ce style d'écriture on utilisera de préférence la notation unaire de PureBasic, c'est à dire par exemple :
            ;
            ; a + 1 au lieu de a = a + 1
            ; a + b au lieu de a = a + b
            ;
            ; etc
            ;
            ; On s'interdit également d'utiliser trois variables sur une même ligne :
            ;
            ; c = a + b sera donc déployé en 2 lignes
            ; c = a
            ; c + b
            ;
            ; Cette écriture donnera un code très rapide, même si il n'est pas tout à fait aussi rapide que le modèle #PureBasic_LowLevel_Code précédent
            ;
            ; Cette étape est une manière réaliste de passer de l'optimisation langage évolué à l'optimisation assembleur. La tentative de traduire
            ; directement le code #PureBasic_Optimized_Code est généralement vouée à de malheureuses déconvenues et n'apporte jamais de meilleurs résultats
            ; que le code généré par le compilateur.
            ;
            Case #PureBasic_Chewed_Code
              DrawingBuffer = DrawingBuffer()
              DrawingBufferPitch = DrawingBufferPitch()
              For x = 1 To DrawingWidth - 1
                X4 = x
                X4 + xTop
                X4 << 2
                X4 + DrawingBuffer
                For y = 1 To DrawingHeight - 1
                  xy = DrawingWidth
                  xy * y
                  xy + x
                  a = xy
                  a - DrawingWidth
                  Value = DrawingArea(a)
                  a = xy
                  a + DrawingWidth
                  Value + DrawingArea(a)
                  a = xy
                  a - 1
                  Value + DrawingArea(a)
                  a = xy
                  a + 1
                  Value + DrawingArea(a)
                  Value >> 2
                  Value + 1
                  Value & 63
                  Value + 192
                  Color = Value
                  Color << 10
                  Color + Value
                  DrawingArea(xy) = Color
                  Color1 = Color
                  Color1 & $FF0000
                  Color1 >> 16
                  Color2 = Color
                  Color2 & $00FF00
                  Color3 = Color
                  Color3 & $0000FF
                  Color3 << 16
                  Color = Color1
                  Color + Color2
                  Color + Color3
                  Address = y
                  Address + yTop
                  Address * DrawingBufferPitch
                  Address + X4
                  PokeL(Address, Color)
                Next
              Next
            ;
            ; Here we take the chewed code and translate it to ASM with some more optimization
            ;
            ; Especially we take care to avoid to store values in unnecessary variables.
            ;
            ; We can consider processor's registers as variables but with the limitation that any PureBasic instruction inserted between ASM lines
            ; may change registers values.
            ;
            ; So this is necessary to stay in ASM as far as possible to not loose registers values otherwise it is necessary to store registers all the time.
            ;=========================================================================================
            ; Le code #ASM_Code est la reprise du code #PureBasic_Chewed_Code avec quelques optimisations
            ;
            ; On s'attache en particulier à éliminer le report de valeurs de registres dans des variables temporaires.
            ;
            ; La limitation en nombre de registres oblige bien entendu à être très soigneux dans le choix des variables temporaires éliminées.
            ;
            ; On peut considérer pratiquement que les registres sont des variables, mais en conservant à l'esprit que l'exécution
            ; de toute instruction langage évolué remet en question l'état de tous les registres CPU.
            ;
            ; Il faut par conséquent rester en assembleur le plus longtemps possible pour éviter de risquer de perdre la valeur d'un registre en cours de route,
            ; ou inversement d'être contraint de sauvegarder tel ou tel registre dans une variable mémoire ce qui diminuerait la qualité d'optimisation.
            ;
            Case #ASM_Code
              DrawingBuffer = DrawingBuffer()
              DrawingBufferPitch = DrawingBufferPitch()
              X4.l
              xy.l
              For x = 1 To DrawingWidth - 1
                !  MOV     eax, dword[v_x]                                     ; eax = x
                !  ADD     eax, dword[v_xTop]                               ; eax + xTop
                !  SAL      eax, 2                                                    ; eax << 2
                !  ADD     eax, dword[v_DrawingBuffer]                  ; eax + DrawingBuffer
                !  MOV     dword[v_X4], eax                                   ; X4 = eax
                For y = 1 To DrawingHeight - 1
                  !  MOV     eax, dword[v_DrawingWidth]                           ; eax = DrawingWidth
                  !  IMUL    eax, dword[v_y]                                    ; eax * y
                  !  ADD     eax, dword[v_x]                                   ; eax + x

                  !  MOV     ebp, dword[a_DrawingArea]                   ; ebp = @DrawingArea()
                  !  MOV     ecx, eax                                               ; ecx = eax
                  !  SUB      ecx, dword[v_DrawingWidth]                           ; ecx - DrawingWidth
                  !  SAL      ecx, 2                                                   ; ecx * 4
                  !  MOV     edi, dword[ebp+ecx]                              ; edi = PeekL(ebp + ecx)

                  !  MOV     ecx, eax                                                ; ecx = eax
                  !  ADD     ecx, dword[v_DrawingWidth]                            ; ecx + DrawingWidth
                  !  SAL      ecx, 2                                                   ; ecx * 4
                  !  ADD     edi, dword[ebp+ecx]                              ; edi + PeekL(ebp + ecx)

                  !  MOV     ecx, eax                                                ; ecx = eax
                  !  DEC     ecx                                                        ; ecx - 1
                  !  SAL      ecx, 2                                                    ; ecx * 4
                  !  ADD     edi, dword[ebp+ecx]                               ; edi + PeekL(ebp+ecx)

                  !  ADD     ecx, 8                                                    ; ecx + 8 (soit ecx = ((eax - 1) + 2) * 4
                  !  ADD     edi, dword[ebp+ecx]                               ; edi + PeekL(ebp + ecx)
                 
                  !  SAR     edi, 2                                                     ; edi >> 2
                  !  INC      edi                                                        ; edi + 1
                  !  And     edi, 63                                                   ; edi & 63
                  !  ADD     edi, 192                                                  ; edi + 192

                  !  MOV     edx, edi                                                 ; edx = edi
                  !  MOV     ebx, edx                                                ; ebx = edx
                  !  SAL      edx, 10                                                  ; eax = edx << 10
                  !  ADD     edx, ebx                                                ; edx = ebx

                  !  MOV      ecx, eax                                                ; ecx = eax
                  !  SAL       ecx, 2                                                   ; ecx * 4
                  !  MOV      [ebp+ecx], edx                                      ; PokeL(ebp+ecx, edx)

                  !  MOV      ecx, dword[v_y]                                  ; ecx = y
                  !  ADD      ecx, dword[v_yTop]                            ; ecx + yTop
                  !  IMUL     ecx, dword[v_DrawingBufferPitch]         ; ecx * DrawingBufferPitch
                  !  ADD      ecx, dword[v_X4]                                ; ecx + X4
                  !  BSWAP  edx                                                     ; edx (00RRGGBB) => (BBGGRR00)
                  !  SAR       edx, 8                                                 ; edx(BBGGRR00) => (00BBGGRR)
                  !  MOV      [ecx], edx                                           ; PokeL(ecx, edx)
                Next
              Next
            ;
            ; In #PureBasic_LowLevel_Code2 the same idea is used than in former #PureBasic_LowLevel_Code but addressing is linearized
            ; Buffer addressing is no more x, y based but only a single variable is used.
            ;
            ; The address of the horizontal neighbor of a given point is the address of this point -/+ 1.
            ; The address of the vertical neighbor of a given point is the address of this point -/+ DrawingWidth.
            ;
            ; Using this method the point to draw is accessed the fastest.
            ;
            ; Meantime pixels values are stored in the array which is addressed using two variables to calculate unary index
            ; of the array easily, allowing to avoid the double address calculation.
            ;
            ; Obviously this does not look far from former code, it renders completely different results.
            ;
            ; In order to access to the best possible ASM optimization later, For / Next loops are replaced by Repeat / Until.
            ;
            ; Pixel value calculation is unchanged.
            ;=====================================================================
            ; Dans la version #PureBasic_LowLevel_Code2 on reprend le même principe mais on a linéarisé l'adressage
            ; Ici le buffer de l'écran est adressé avec une seule variable d'adresse qui est incrémentée de 4 octets pour passer
            ; point suivant et d'une ligne moins la DrawingWidth de tracé pour passer à la ligne suivante.
            ;
            ; Cette méthode permet d'adresser le pixel à tracer au plus vite.
            ;
            ; Dans le même temps les valeurs des points sont toujours stockées dans un tableau qui lui est adressé
            ; à partir de deux variables x et y en recalculant l'indice unaire du tableau pour éviter le double calcul d'adresse.
            ;
            ; La méthode paraît peu différente à priori, mais les résultats n'ont rien à voir.
            ;
            ; Pour amener à une meilleure optimisation ultérieure en assembleur, les boucles For / Next ont été remplacées par des Repeat / Until
            ;
            ; Le calcul de valeur d'un pixel reste inchangé.
            ;
            Case #PureBasic_LowLevel_Code2
              DrawingBuffer = DrawingBuffer()
              DrawingBufferPitch = DrawingBufferPitch()
              BufferAddress = DrawingBuffer + 4 * (xTop + 1) + yTop * DrawingBufferPitch
              ArrayAddress = DrawingWidth + 1
              y = 1
              Repeat
                x = 1
                Repeat
                  Value = ((DrawingArea(ArrayAddress - DrawingWidth) + DrawingArea(ArrayAddress + DrawingWidth) + DrawingArea(ArrayAddress - 1) + DrawingArea(ArrayAddress + 1)) / 4 + 1) & 63 + 192
                  Color = Value << 10 + Value
                  DrawingArea(ArrayAddress) = Color
                  Color = (Color & $FF0000) >> 16 + (Color & $00FF00) + (Color & $0000FF) << 16
                  PokeL(BufferAddress, Color)
                  BufferAddress + 4
                  ArrayAddress + 1
                  x + 1
                Until  x > DrawingWidth - 1
                ArrayAddress + 1
                BufferAddress + DrawingBufferPitch - 4 * (DrawingWidth - 1)
                y + 1
              Until y > DrawingHeight - 1
            ;
            ; Here is the optimized ASM listing of the #PureBasic_LowLevel_Code2
            ;======================================================
            ; Voici une version assembleur optimisée pour le code #PureBasic_LowLevel_Code2
            ;
            Case #ASM_LowLevel_Code2
              DrawingBuffer = DrawingBuffer()
              DrawingBufferPitch = DrawingBufferPitch()
              !  MOV    ebx, dword[v_DrawingBuffer]                      ; ebx = DrawingBuffer
              !  MOV    edi, dword[v_xTop]                                     ; edi = xTop
              !  INC     edi                                                             ; edi + 1
              !  SAL     edi, 2                                                         ; edi * 4
              !  ADD    ebx, edi                                                      ; ebx + edi
              !  MOV    edi, dword[v_yTop]                                     ; edi = yTop
              !  IMUL   edi, dword[v_DrawingBufferPitch]                 ; edi * DrawingBufferPitch
              !  ADD    ebx, edi                                                     ; ebx + edi
              !  MOV    dword[v_BufferAddress], ebx                       ; BufferAddress = ebx
              !  MOV    ebx, dword[v_DrawingWidth]                       ; ebx = DrawingWidth
              !  INC     ebx                                                            ; ebx + 1
              !  MOV    dword[v_ArrayAddress], ebx                        ; ArrayAddress = ebx
              !  MOV    dword[v_y], 1                                             ; y = 1
              !_RepeatBN21:                                                         ; Repeat / Until return label
                !  MOV    dword[v_x], 1                                           ; x = 1
                !_RepeatBN22:                                                       ; Repeat / Until return label
                  !  MOV      ebp, dword[a_DrawingArea]                     ; ebp = DrawingArea
                  !  MOV      ecx, dword[v_ArrayAddress]                    ; ecx = @ArrayAddress
                  !  MOV      eax, ecx                                                 ; eax = ecx
                  !  SUB      eax, dword[v_DrawingWidth]                    ; eax - DrawingWidth
                  !  SAL       eax, 2                                                    ; eax * 4
                  !  MOV      edi, dword[ebp+eax]                               ; edi = PeekL(ebp+eax]

                  !  MOV      eax, ecx                                                 ; eax = ecx
                  !  ADD      eax, dword[v_DrawingWidth]                    ; eax - DrawingWidth
                  !  SAL       eax, 2                                                    ; eax * 4
                  !  ADD      edi, dword[ebp+eax]                               ; edi = PeekL(ebp+eax]

                  !  MOV      eax, ecx                                                 ; eax = ecx
                  !  DEC      eax                                                        ; eax - 1
                  !  SAL       eax, 2                                                    ; eax * 4
                  !  ADD      edi, dword[ebp+eax]                               ; edi = PeekL(ebp+eax]

                  !  ADD      eax, 8                                                    ; eax + 8
                  !  ADD      edi, dword[ebp+eax]                               ; edi = PeekL(ebp+eax]

                  !  SAR      edi, 2                                                      ; edi / 4
                  !  INC       edi                                                         ; edi + 1
                  !  And      edi, 63                                                    ; edi & 63
                  !  ADD      edi, 192                                                  ; edi + 192
                  !  MOV      ebx, edi                                                  ; ebx = edi

                  !  SAL       ebx, 10                                                   ; ebx << 10
                  !  ADD      ebx, edi                                                  ; ebx + edi

                  !  MOV      eax, ecx                                                  ; eax = ecx
                  !  SAL       eax, 2                                                     ; eax * 4
                  !  MOV      dword[ebp+eax], ebx                               ; PokeL(ebp+eax, ebx)

                  !  BSWAP  ebx                                                         ; ebx (00RRGGBB) => (BBGGRR00)
                  !  SAR       ebx, 8                                                     ; ebx(BBGGRR00) => (00BBGGRR)

                  !  MOV      eax,dword[v_BufferAddress]                     ; eax = @BufferAddress
                  !  MOV      [eax], ebx                                               ; PokeL(eax, ebx)

                  !  ADD      dword[v_BufferAddress], 4                        ; BufferAddress + 4
                  !  INC       dword[v_ArrayAddress]                             ; ArrayAddress + 1
                  !  INC       dword[v_x]                                               ; x + 1
                  !  MOV      ebx, dword[v_x]                                        ; ebx = x
                  !  MOV      edi, dword[v_DrawingWidth]                       ; edi = DrawingWidth
                  !  DEC      edi                                                           ; edi - 1
                  !  CMP      ebx, edi                                                    ; if ebx <= edi
                  !  JLE        _RepeatBN22                                            ; Goto RepeatBN22
             
              !  INC     dword[v_ArrayAddress]                                   ; ArrayAddress + 1
              !  MOV    ebx, dword[v_DrawingWidth]                           ; ebx = DrawingWidth
              !  DEC    ebx                                                                ; ebx - 1
              !  SAL     ebx, 2                                                            ; ebx / 4
              !  NEG    ebx                                                                 ; -ebx
              !  ADD    ebx, dword[v_DrawingBufferPitch]                    ; ebx + DrawingBufferPitch
              !  ADD    dword[v_BufferAddress], ebx                           ; BufferAddress + ebx
              !  INC     dword[v_y]                                                     ; y + 1
              !  MOV    edi, dword[v_DrawingHeight]                            ; edi = DrawingHeight
              !  ADD    edi, -1                                                            ; edi - 1
              !  MOV    ebx, dword[v_y]                                              ; ebx = y
              !  CMP    ebx, edi                                                          ; if ebx <= edi
              !  JLE      _RepeatBN21                                                  ; Goto RepeatBN21
          EndSelect

          ;
          ; Keyboard events part
          ;============================
          ; Section de gestion des évènements clavier
          ;
          ExamineKeyboard()
          ; Change the code part to execute
          ; Modifie la section de code à exécuter
          If KeyboardPushed(#PB_Key_Left)
              ResetAll()
              ExecuteCode - 1
              If ExecuteCode < 0
                  ExecuteCode = 0
              EndIf
              Repeat
                ExamineKeyboard()
              Until KeyboardReleased(#PB_Key_Left)
          EndIf
          If KeyboardPushed(#PB_Key_Right)
              ResetAll()
              ExecuteCode + 1
              If ExecuteCode > #LastCode
                  ExecuteCode = #LastCode
              EndIf
              Repeat
                ExamineKeyboard()
              Until KeyboardReleased(#PB_Key_Right)
          EndIf
          ; Set / Unset the buffers synchronization
          ; Valide / invalide la synchronisation des buffers
          If KeyboardPushed(#PB_Key_Up)
              ResetAll()
              FlipBuffers = 1 - FlipBuffers
              Repeat
                ExamineKeyboard()
              Until KeyboardReleased(#PB_Key_Up)
          EndIf
          If KeyboardPushed(#PB_Key_Down)
              ResetAll()
              FlipBuffers = 1 - FlipBuffers
              Repeat
                ExamineKeyboard()
              Until KeyboardReleased(#PB_Key_Down)
          EndIf
          ; Change the drawing area size
          ; Modifie la taille de la zone de dessin
          If KeyboardPushed(#PB_Key_Pad4)
              ResetAll()
              WidthHeight - 1
              If WidthHeight < 0
                  WidthHeight = 0
              EndIf
              Repeat
                ExamineKeyboard()
              Until KeyboardReleased(#PB_Key_Pad4)
              DrawingWidth = DrawingWidth(WidthHeight)
              DrawingHeight = DrawingHeight(WidthHeight)
              xTop = (ScreenXSize - DrawingWidth) / 2 + 1
              yTop = (ScreenYSize - DrawingHeight) / 2 + 1
              xBottom = xTop + DrawingWidth - 2
              yBottom = yTop + DrawingHeight - 2
          EndIf
          If KeyboardPushed(#PB_Key_Pad6)
              ResetAll()
              WidthHeight + 1
              If WidthHeight > LastSize
                  WidthHeight = LastSize
              EndIf
              Repeat
                ExamineKeyboard()
              Until KeyboardReleased(#PB_Key_Pad6)
              DrawingWidth = DrawingWidth(WidthHeight)
              DrawingHeight = DrawingHeight(WidthHeight)
              xTop = (ScreenXSize - DrawingWidth) / 2 + 1
              yTop = (ScreenYSize - DrawingHeight) / 2 + 1
              xBottom = xTop + DrawingWidth - 2
              yBottom = yTop + DrawingHeight - 2
          EndIf
          ; Control the elapsed time since the last integer second
          ; Contrôle le temps écoulé depuis la dernière seconde entière
          If ElapsedMilliseconds() - tz => 1000
              tz = ElapsedMilliseconds()
              FPS = NFrames
              TFrames + FPS
              NFrames = 0
              AFrames + 1
          EndIf
          NFrames + 1
          ; Display statistics and parameters
          ; Affiche les statistiques et les paramètres
          BackColor(0)
          FrontColor(RGB(255, 255, 255))
          DrawText(10, 40, "FPS " + Str(FPS) + "      Average : " + StrF(TFrames / AFrames, 2))
          DrawText(10, 60, CodeType(ExecuteCode) + "            ")
          DrawText(10, 80, "FlipBuffers(" + Str(FlipBuffers) + ")")
          DrawText(10, 100, Str(DrawingWidth) + "x" + Str(DrawingHeight))
        StopDrawing()
      Until KeyboardReleased(#PB_Key_Escape)
  EndIf
End

DataSection
  Data.s "#PureBasic_Regular_Code", "#PureBasic_Better_Code", "#PureBasic_Optimized_Code", "#PureBasic_LowLevel_Code"
  Data.s "#PureBasic_Chewed_Code", "#ASM_Code", "#PureBasic_LowLevel_Code2", "#ASM_LowLevel_Code2"
  Data.l 128, 128
  Data.l 256, 256
  Data.l 320, 240
  Data.l 480, 360
  Data.l 640, 480
  Data.l 800, 600
EndDataSection 
"Menschenskinder, das Niveau dieses Forums singt schon wieder!" — GronkhLP ||| "ich hogffe ihr könnt den fehle endecken" — Marvin133 ||| "Ideoten gibts ..." — computerfreak ||| "Jup, danke. Gruss" — funkheld
SebastianJu2
Beiträge: 180
Registriert: 24.09.2010 10:39

Re: PB im Vergleich mit anderen Sprachen in Bezug auf Speed

Beitrag von SebastianJu2 »

@ts-soft

Ich glaube eine Möglichkeit zur Beschleunigung mit Pointern habe ich schon mal gefunden. Ich beschreib sie mal falls andere, die neu bei PB sind, das auch noch nicht wissen.

Ich habe mir wegen deinem Hinweis auf Pointer das Tutorial über Pointer durchgelesen was es hier gibt: http://www.purearea.net/pb/english/tutorials.htm

Allerdings wurde dort nicht auf Geschwindigkeit eingegangen.

In den Bereichen wo es in meinem Code auf Geschwindigkeit ankommt ist mir auch nichts aufgefallen wie Pointer die Sache beschleunigen könnten. Bei einer nicht geschwindigkeitsrelevanten Funktion habe ich dann versucht statt eines Strings einen Pointer auf den String zu übergeben. Weil dann anstatt eines Strings nur ein Zeiger in der Funktion neu erstellt werden muss glaube ich.

Ich habe es dann getestet... OHNE DEBUGGER :) und es scheint wie wenn die neue Version mit einem Pointer schneller ist. Etwa 3/8 der Zeit. Vermutlich weil keine String-Variable erstellt werden musste samt String kopieren sondern nur eine Pointervariable die eine Zahl ist.

Ich habe mich gefragt welchen Einfluss es hat dass ich bei Übergabe per Pointer den String ja vorher in einer Variable speichern muss um einen Pointer zu erhalten. Beim Testen schien mir dass, wenn ich den String direkt im Funktionsaufruf an die Prozedur übergebe es außerdem ein wenig mehr Zeit braucht als wenn der String per Variable übergeben wird, also vorher einer Variable zugewiesen wird.
Aus einer Ahnung hab ich dann die beiden Codeteile umgedreht in der Reihenfolge. Anstatt 800 für direkte Übergabe und 600 per Var wie vorher waren es dann 600 per Var und 350 direkt. Und das reproduzierbar wenn ich es wieder umdrehe. Vielleicht hängt das irgendwie mit Speicher zusammen der nach der ersten Funktion besser nutzbar ist für die folgende? Der Testcode per Pointer bleibt von seiner Position unbeeindruckt. Er bleibt gleich egal wo er in der Reihenfolge er steht... Auf jeden Fall ist die Pointerversion immer noch am schnellsten bei mir.

Mir scheint man muss viel austesten wenn es um Geschwindigkeit geht. Trotzdem braucht man erst die Ideen...

Der Testcode:

Code: Alles auswählen

Procedure In2(ValueToCheck.i,*ValueString)
EndProcedure

starttime.i = ElapsedMilliseconds()
For x=0 To 2000000  
  s$ = "1,2,3,4,5,6,7,8"
  in2(2,@s$)
Next
MessageRequester("",Str(ElapsedMilliseconds()-starttime))

Procedure In1(ValueToCheck.i,ValueString$)
EndProcedure

starttime.i = ElapsedMilliseconds()
For x=0 To 2000000
  in1(2,"1,2,3,4,5,6,7,8")
Next
MessageRequester("",Str(ElapsedMilliseconds()-starttime))

Procedure In3(ValueToCheck.i,ValueString$)
EndProcedure

starttime.i = ElapsedMilliseconds()
For x=0 To 2000000
  s$ = "1,2,3,4,5,6,7,8"
  in3(2,s$)
Next
MessageRequester("",Str(ElapsedMilliseconds()-starttime))
Benutzeravatar
ts-soft
Beiträge: 22292
Registriert: 08.09.2004 00:57
Computerausstattung: Mainboard: MSI 970A-G43
CPU: AMD FX-6300 Six-Core Processor
GraKa: GeForce GTX 750 Ti, 2 GB
Memory: 16 GB DDR3-1600 - Dual Channel
Wohnort: Berlin

Re: PB im Vergleich mit anderen Sprachen in Bezug auf Speed

Beitrag von ts-soft »

Das ist ja nicht wirklich eine Optimierung.
Hier nochmal meine Escape-Sequenzen: http://www.purebasic.fr/german/viewtopi ... =8&t=11858
Schreib das mal um mit ReplaceString und prüfe dann!
PureBasic 5.73 LTS | SpiderBasic 2.30 | Windows 10 Pro (x64) | Linux Mint 20.1 (x64)
Nutella hat nur sehr wenig Vitamine. Deswegen muss man davon relativ viel essen.
Bild
SebastianJu2
Beiträge: 180
Registriert: 24.09.2010 10:39

Re: PB im Vergleich mit anderen Sprachen in Bezug auf Speed

Beitrag von SebastianJu2 »

@ts-soft

Etwa 5 mal so langsam mit ReplaceString. Ich werd mir deinen Code mal ansehen und sehen was ich nachmachen kann.

@c4s

Beeindruckende Statistik wenn ich da nichts falsch interpretiere. Aber wenn ich mir dann den Code ansehe... :o Leicht ist das vermutlich nicht zu lernen.
super_castle
Beiträge: 557
Registriert: 29.11.2005 15:05

Re: PB im Vergleich mit anderen Sprachen in Bezug auf Speed

Beitrag von super_castle »

na..., freebasic erreicht ca 95% der geschwindigkeit von c++, das noch... gfa32-basic erreicht ca 97% der geschwindigkeit von c++ und purebasic erreicht ca 82% der geschwindigkeit von c++...

gruss
Benutzeravatar
cxAlex
Beiträge: 2111
Registriert: 26.06.2008 10:42

Re: PB im Vergleich mit anderen Sprachen in Bezug auf Speed

Beitrag von cxAlex »

super_castle hat geschrieben:na..., freebasic erreicht ca 95% der geschwindigkeit von c++, das noch... gfa32-basic erreicht ca 97% der geschwindigkeit von c++ und purebasic erreicht ca 82% der geschwindigkeit von c++...

gruss
Und deine Angaben beziehen sich auf was? Was für Algos werden da getestet? Oder saugst du dir das alles aus den Fingern? :freak:
Projekte: IO.pbi, vcpu
Pausierte Projekte: Easy Network Manager, µC Emulator
Aufgegebene Projekte: ECluster

Bild

PB 5.1 x64/x86; OS: Win7 x64/Ubuntu 10.x x86
SebastianJu2
Beiträge: 180
Registriert: 24.09.2010 10:39

Re: PB im Vergleich mit anderen Sprachen in Bezug auf Speed

Beitrag von SebastianJu2 »

Ja, gibt es den Code für den Test einzusehen? Es dürfte ja kein Anwendungsprogramm sein weil dann das eine oder andere mehr oder weniger optimiert sein könnte. Vermutlich müsste man einzelne Befehle mit Schleifen ausführen und davon ein möglichst breites Spektrum und dann schauen was am schnellsten ist. Aber ich denke trotzdem wird auf jeden Fall bei dem einen Befehl im Testprogramm mal die eine und beim nächsten wieder eine andere Sprache schneller sein.
c4s
Beiträge: 1235
Registriert: 19.09.2007 22:18

Re: PB im Vergleich mit anderen Sprachen in Bezug auf Speed

Beitrag von c4s »

Offtopic:
super_castle hat geschrieben:na..., freebasic erreicht ca 95% der geschwindigkeit von c++, das noch... gfa32-basic erreicht ca 97% der geschwindigkeit von c++ und purebasic erreicht ca 82% der geschwindigkeit von c++...

gruss
...Und diese Aussage ist nur annähernd 5% richtig. Zudem sind 98,76% deiner Beiträge Schwachfug. /:->
"Menschenskinder, das Niveau dieses Forums singt schon wieder!" — GronkhLP ||| "ich hogffe ihr könnt den fehle endecken" — Marvin133 ||| "Ideoten gibts ..." — computerfreak ||| "Jup, danke. Gruss" — funkheld
Benutzeravatar
PMV
Beiträge: 2765
Registriert: 29.08.2004 13:59
Wohnort: Baden-Württemberg

Re: PB im Vergleich mit anderen Sprachen in Bezug auf Speed

Beitrag von PMV »

c4s hat geschrieben:Offtopic:
super_castle hat geschrieben:na..., freebasic erreicht ca 95% der geschwindigkeit von c++, das noch... gfa32-basic erreicht ca 97% der geschwindigkeit von c++ und purebasic erreicht ca 82% der geschwindigkeit von c++...

gruss
...Und diese Aussage ist nur annähernd 5% richtig. Zudem sind 98,76% deiner Beiträge Schwachfug. /:->
Optimist!
alte Projekte:
TSE, CWL, Chatsystem, GameMaker, AI-Game DLL, Fileparser, usw. -.-
Antworten