A propos de nombres premiers

Partagez votre expérience de PureBasic avec les autres utilisateurs.
Ollivier
Messages : 4190
Inscription : ven. 29/juin/2007 17:50
Localisation : Encore ?
Contact :

Re: A propos de nombres premiers

Message par Ollivier »

J'ai trouvé PSUBD et PSUBQ, pour soustraire des entiers. Peut-être que la syntaxe SSE qui nous intéresse c'est PxxxD et PxxxQ. En légendant,
P = Pack
xxx = Calcul ASM de base (ADD, SUB, MUL, DIV, AND, OR, XOR) (S'il y a des erreurs, ne pas hésiter à me renseigner)
Q/D = (Q ou D, soit l'un, soit l'autre)
Q = Quad (64 bits)
D = Double-word (32 bits)

Avec opérandes xMMn, en légendant :
x : Taille du pack
x=X : 128 bits
x=Y : 256 bits
x=Z : 512 bits
MM : Ça, c'est "touche pas à ça, pti con!"
n : Numéro de registre

Exemple hypothétique :

Code : Tout sélectionner

! PADDD ZMM3, ZMM5
; Additionne les 16 entiers 32 bits contenus dans le registre 3 avec ceux contenus dans le registre 5 et stocke les 16 résultats dans le registre 3
fweil
Messages : 505
Inscription : dim. 16/mai/2004 17:50
Localisation : Bayonne (64)
Contact :

Re: A propos de nombres premiers

Message par fweil »

Je suis concentré, mais parfois je me demande si il n'y a pas un espace perdu dans le mot ...

Donc voici un jeu de test pour REP SCASQ que je propose, mais avec toutes les précautions d'usage. A vérifier, argumenter et on en parle ouvertement jusqu'à ce qu'on soit vraiment d'accord sur tout. J'ai fait ça là, mais je ne suis pas sur ma machine de référence jusqu'à lundi après-midi ou mardi. Là je code sur mon barda de voyage depuis une dizaine de jours. Et il n'est pas très précis sur les temps en utilisation intense des CPUs. C'est un portable avec une clock variable en double coeurs - double thread. Ma machine de référence est en clock fixe sur un 4 core physique sur lequel je peux écarté à peu près toute intrusion pendant une séquence de test.

Le code de test utilise un tableau de mots de 64bits sur lequel on remplit du n'importe nawak et on place un quad à 0 où on veut (pour tester un 0 près du début, loin du début, etc).

Les temps sont données sur Elapsedmilliseconds() ce qui n'est pas trop précis, et on peut remplacer ça par une mesure microseconde, mais vu le nombre de tour qu'on fait avec chaque boucle, ça ne change pas grand chose.

Ce que je vois, et que je connais d'autre part assez bien, c'est qu'une séquence REP SCASx est plus performante qu'un code PB de base, ça on pouvait en être convaincu avant même d'essayer. Quand je descend en optimisation (réduction assembleur, et optimisation à la Fanfan), j'obtiens des résultats qui normalement doivent être sensiblement meilleurs qu'un REP en utilisant des boucles optimisées (utilisation des registres à fond). Mais je veux bien des infos contradictoires si c'est possible. Ce que je sais, c'est que ça dépend un peu des modèles de processeurs aussi. Mais le bruit de fond qu'on a sur les CPU avec n'importe quel PC peuvent induire pas mal d'erreurs d'appréciation.

Un REP avec comparaison et déplacement d'adresse en tout cas sollicite autant les registres que moi quand j'optimise à ma façon, mais ce que ne sais pas faire un REP c'est choisir les registres, et autant que je ne me trompe pas, le REP utilise un jeu de registres d'adresse pour la donnée testée qui prend nécessairement xSI:xDI pour taper la donnée en mémoire. Souvent, j'ai observé que si on prend un registre de base xAX, xBX, xCD, xDX, ou même un registre général, r8-15 (en 64 bits seulement évidemment) le taux de transfert RAM / registre est plus fluide (sans doute en partie grâce à une meilleure fluidité entre RAM et caches et là je n'ai pas d'explication argumentée, mais c'est ce que j'observe le plus souvent).

Donc voilà le code de test à faire tourner pour comparer nos épreuves et en faire des preuves :

Code : Tout sélectionner

ArraySize.q = 100000
Dim MyArray.q(ArraySize)
FillMemory(@MyArray(), ArraySize * SizeOf(INTEGER), $123456789ABCDEF0, #PB_Integer)
MyArray(12345) = 0
res.q
nTimes.q = 100000

OpenConsole()

tz.q = ElapsedMilliseconds()
For i.q = 1 To nTimes
  For j.q = 0 To ArraySize
    If MyArray(j) = 0
      res = j
      Break
    EndIf
  Next
Next
Time.d = (ElapsedMilliseconds() - tz) / 1000
PrintN("res = " + Str(res) + #TAB$ + StrD(Time, 3))

tz.q = ElapsedMilliseconds()
For i.q = 1 To nTimes
  !  MOV     rdi, qword [a_MyArray]
  !  SUB     rcx, rcx
  !  SUB     rax, rax
  !  NOT     rcx
  !  CLD
  !  REPNE SCASQ
  !  NOT     rcx
  !  DEC     rcx
  !  MOV     qword [v_res], rcx
Next
Time.d = (ElapsedMilliseconds() - tz) / 1000
PrintN("res = " + Str(res) + #TAB$ + StrD(Time, 3))

tz.q = ElapsedMilliseconds()
For i.q = 1 To nTimes
  j.q = 0
  Repeat ; For j.q = 0 To SizedDataLength - 1
    If MyArray(j) = 0
      res = j
      Break
    EndIf
    j + 1
  Until j > ArraySize
Next
Time.d = (ElapsedMilliseconds() - tz) / 1000
PrintN("res = " + Str(res) + #TAB$ + StrD(Time, 3))

tz.q = ElapsedMilliseconds()
*Array.Quad
j.q
For i.q = 1 To nTimes
; *Array => r8    j => r9
  !  MOV     r9, 0 ; j.q = 0
  !  MOV     r8, qword [a_MyArray] ; *Array.Quad = @Array()
! Repeat_It_Again: ; Repeat ; For j.q = 0 To SizedDataLength - 1
    !  CMP     qword [r8], 0 ; If *Array\q = 0
    !  JNE     @f
      !  MOV     qword [v_res], r9 ; res = j
      !  JMP     ShutUpYourMouth
! @@: ; EndIf
    !  ADD     r8, 8 ; *Array + 8
    !  INC     r9 ; j + 1
    !  CMP     r9, qword [v_ArraySize]
  !  JLE     Repeat_It_Again ; Until j => SizedDataLength ; Next
! ShutUpYourMouth:
Next
Time.d = (ElapsedMilliseconds() - tz) / 1000
PrintN("res = " + Str(res) + #TAB$ + StrD(Time, 3))

tz.q = ElapsedMilliseconds()
*Array.Quad
j.q
For i.q = 1 To nTimes
; *Array => r8    ArraySize (décr.) => r9
  !  MOV     r8, qword [a_MyArray] ; *Array.Quad = @Array()
  !  MOV     r9, qword [v_ArraySize]
! Repeat_It_AgainAgain: ; Repeat ; For j.q = 0 To SizedDataLength - 1
    !  CMP     qword [r8], 0 ; If *Array\q = 0
    !  JNE     @f
      !  MOV     rax, qword [v_ArraySize] ; res = j
      !  SUB     rax, r9
      !  MOV     qword [v_res], rax
      !  JMP     ShutUpYourMouthAgain
! @@: ; EndIf
    !  ADD     r8, 8 ; *Array + 8
    !  DEC     r9 ; j + 1
  !  JNZ     Repeat_It_AgainAgain ; Until j => SizedDataLength ; Next
! ShutUpYourMouthAgain:
Next
Time.d = (ElapsedMilliseconds() - tz) / 1000
PrintN("res = " + Str(res) + #TAB$ + StrD(Time, 3))

PrintN("")
PrintN("Push any key to quit ...")
While Inkey() = ""
  Delay(50)
Wend

CallDebugger
End
Et bien sûr, avec Debug ça va faire des faux résultats :) donc sans.
Mon avatar reproduit l'image de 4x1.8m présentée au 'Salon international du meuble de Paris' en janvier 2004, dans l'exposition 'Shades' réunisant 22 créateurs autour de Matt Sindall. L'original est un stratifié en 150 dpi.
fweil
Messages : 505
Inscription : dim. 16/mai/2004 17:50
Localisation : Bayonne (64)
Contact :

Re: A propos de nombres premiers

Message par fweil »

Ollivier a écrit :J'ai trouvé PSUBD et PSUBQ, pour soustraire des entiers ...
Vais regarder ça après miam.
Mon avatar reproduit l'image de 4x1.8m présentée au 'Salon international du meuble de Paris' en janvier 2004, dans l'exposition 'Shades' réunisant 22 créateurs autour de Matt Sindall. L'original est un stratifié en 150 dpi.
Avatar de l’utilisateur
djes
Messages : 4252
Inscription : ven. 11/févr./2005 17:34
Localisation : Arras, France

Re: A propos de nombres premiers

Message par djes »

Si je peux suggérer un petit truc qu'on oublie souvent, pour les benchmarks, ça peut être utile de booster un peu la priorité du processus (pardon pour la mise en forme, je poste de mon mobile)

Code : Tout sélectionner

SetPriorityClass_(GetCurrentProcess_(), #REALTIME_PRIORITY_CLASS)
  SetThreadPriority_( GetCurrentThread_() , #THREAD_PRIORITY_TIME_CRITICAL)     ;warning : keyboard lock
; Code to benchmark
 SetPriorityClass_(  GetCurrentProcess_(), #NORMAL_PRIORITY_CLASS)
  SetThreadPriority_( GetCurrentThread_() , #THREAD_PRIORITY_NORMAL)
  
Ollivier
Messages : 4190
Inscription : ven. 29/juin/2007 17:50
Localisation : Encore ?
Contact :

Re: A propos de nombres premiers

Message par Ollivier »

Objection! Djes : tu m'as déjà fait la blague il y a sept ans et grosse gamelle! Tu pourrais indiquer en commentaire "Attention! Risque de plantage, sauvegardez vos documents avant test de ce code svp".

On se croirait avant une partie de chasse à préparer les cartouches. L'étui, la poudre, les plombs. Et là, Djes, ton truc prépare une cartouche où il y a beaucoup trop de poudre! Je n'adhère pas. Et j'espère, en tant que "compétent" (avec espace possible selon la cuisine de la veille) que le "concentré" verra la gamelle avant de valider.

Bon, moi je rajoute des petites étoiles dans le code source explicatif pour Ar-S et je le poste sous peu...
PAPIPP
Messages : 534
Inscription : sam. 23/févr./2008 17:58

Re: A propos de nombres premiers

Message par PAPIPP »

Bonjour à tous

Pour obtenir une plus grande précision dans l’exécution du code il serait préférable d’utiliser l’instruction ASL RDTSC sur 64 bits

Exemple de RDTSC comparée à ElapsedMilliseconds():

Code : Tout sélectionner

 Macro _q_t_
 "
 EndMacro
 Macro _n (__n)
 _q_t_#__n#=_q_t_+Str(__n)+" "
 EndMacro

OpenConsole()
;****** calcul du nombre de cycles d' horloge d'une ou de plusieurs instructions machines  *****************
ProcedureDLL.q P_NBCH()
  !RDTSC
  ProcedureReturn
EndProcedure
Macro M_NBCH (__cc)
  __cc.q
  !RDTSC
  !PUSH edx
  !PUSH eax
  !POP dword[v_#__cc]
  !POP dword[v_#__cc+4]
EndMacro
printn ( "****************** AVEC LA MACRO *****************")
M_NBCH(D1)
SetPriorityClass_(GetCurrentProcess_(), #REALTIME_PRIORITY_CLASS)
SetThreadPriority_( GetCurrentThread_() , #THREAD_PRIORITY_TIME_CRITICAL)     ;warning : keyboard lock
M_NBCH(D2)
; Code to benchmark
SetPriorityClass_(  GetCurrentProcess_(), #NORMAL_PRIORITY_CLASS)
SetThreadPriority_( GetCurrentThread_() , #THREAD_PRIORITY_NORMAL)
M_NBCH(D3)
printn (_n(D2-D1)+_n(D3-D2)+"  Total="+_n(D3-D2) )
printn ("**************** AVEC LA PROCEDURE ************")
P1.q=P_NBCH()
SetPriorityClass_(GetCurrentProcess_(), #REALTIME_PRIORITY_CLASS)
SetThreadPriority_( GetCurrentThread_() , #THREAD_PRIORITY_TIME_CRITICAL)     ;warning : keyboard lock
P2.q=P_NBCH()
SetPriorityClass_(  GetCurrentProcess_(), #NORMAL_PRIORITY_CLASS)
SetThreadPriority_( GetCurrentThread_() , #THREAD_PRIORITY_NORMAL)
P3.q=P_NBCH()
printn ( _n(P2-P1)+_n(P3-P2)+"  Total="+_n(P3-P2) )
printn ( "**************** AVEC l'instruction ElapsedMilliseconds() ************")
E1.q=ElapsedMilliseconds()
SetPriorityClass_(GetCurrentProcess_(), #REALTIME_PRIORITY_CLASS)
SetThreadPriority_( GetCurrentThread_() , #THREAD_PRIORITY_TIME_CRITICAL)     ;warning : keyboard lock
E2.q=ElapsedMilliseconds()
SetPriorityClass_(  GetCurrentProcess_(), #NORMAL_PRIORITY_CLASS)
SetThreadPriority_( GetCurrentThread_() , #THREAD_PRIORITY_NORMAL)
E3.q=ElapsedMilliseconds()

printn ( _n(E2-E1)+_n(E3-E2)+"  Total="+_n(E3-E2) )
printn("******* on peut déterminer le nombre de cycles d'horloge utilsé par la procédure ou par la macro exemple pour la macro ")
nb1.q=P_NBCH()
M_NBCH(MM1)
nb2.q=P_NBCH()
M_NBCH(MM2)
NB3.q=P_NBCH()
printn( _n(nb2-nb1)+_n(NB3-NB2)+"  Total="+_n(nb3-NB1)+" Nb cycles macro="+_n(MM2-MM1))

printn( "Taper sur entrée pour terminer")
input()
CloseConsole()


A+
Il est fort peu probable que les mêmes causes ne produisent pas les mêmes effets.(Einstein)
Et en logique positive cela donne.
Il est très fortement probable que les mêmes causes produisent les mêmes effets.
Avatar de l’utilisateur
djes
Messages : 4252
Inscription : ven. 11/févr./2005 17:34
Localisation : Arras, France

Re: A propos de nombres premiers

Message par djes »

Ollivier a écrit :Objection! Djes : tu m'as déjà fait la blague il y a sept ans et grosse gamelle! Tu pourrais indiquer en commentaire "Attention! Risque de plantage, sauvegardez vos documents avant test de ce code svp".

On se croirait avant une partie de chasse à préparer les cartouches. L'étui, la poudre, les plombs. Et là, Djes, ton truc prépare une cartouche où il y a beaucoup trop de poudre! Je n'adhère pas. Et j'espère, en tant que "compétent" (avec espace possible selon la cuisine de la veille) que le "concentré" verra la gamelle avant de valider.
Un rien t'effraie ;) Tu vois, PAPIPP, lui, n'a pas peur ! Essaye donc ton code en laissant appuyé sur une touche et en bougeant la souris pour voir à quel point le benchmark est fiable avec un OS mûle-qui-tache comme Windows...
Ollivier
Messages : 4190
Inscription : ven. 29/juin/2007 17:50
Localisation : Encore ?
Contact :

Re: A propos de nombres premiers

Message par Ollivier »

Visiblement, mon objection a été refusé !!!
Bonjour à PAPIPP au passage.

@fweil

Au passage aussi, je vois un "ArraySize = 100000"
Le mieux serait un test logarithmique avec ArraySize qui prend les valeurs suivantes :
1
10
100
1 000
10 000
100 000
1 000 000
10 000 000
100 000 000
...Et, éventuellement, mettre un bémol si ton algo n'utilise pas des plages aussi grandes. Dans ce cas, mon algo imaginé ne serait pas intéressant, puisqu'avec moins de traitement, tu réussirais le coup.
fweil
Messages : 505
Inscription : dim. 16/mai/2004 17:50
Localisation : Bayonne (64)
Contact :

Re: A propos de nombres premiers

Message par fweil »

@tout le monde, Ollivier à quand même un peu raison de dire qu'il faut pas démarrer vitesse enclenchée et pied sur l'accélérateur. On pourrait se faire refaire le portrait par le premier tronc d'arbre qui aurait préféré pousser il y a un siècle sur le chemin que l'impulsivité vous envoie suivre demain.

Moi je fais des tests à la con, souvent, et régulièrement le dernier programme de secours qui me reste pour arrêter de tester le genre des adjoints du bon dieu c'est le bouton power-off.

Il vaut mieux pas trop jouer à moi, si vous avez envie de passer des soirées peinardes.

Ceci étant, je suis un homme de synthèse :

Code : Tout sélectionner

Macro RDTSC(this)
rdtsc.q
!  RDTSC
!  SHL     rdx, 32
!  ADD     rax, rdx
!  MOV     qword [v_rdtsc], rax
this = rdtsc
EndMacro

RDTSC(a.q)
RDTSC(b.q)

ArraySize.q = 100000
Dim MyArray.q(ArraySize)
FillMemory(@MyArray(), ArraySize * SizeOf(INTEGER), $123456789ABCDEF0, #PB_Integer)
MyArray(12345) = 0
res.q
nTimes.q = 100000

OpenConsole()
tz.q = ElapsedMilliseconds()
RDTSC(t0.q)
For i.q = 1 To nTimes
  For j.q = 0 To ArraySize
    If MyArray(j) = 0
      res = j
      Break
    EndIf
  Next
Next
RDTSC(t1.q)
Time.d = (ElapsedMilliseconds() - tz) / 1000
Time1.d = t1 - t0
PrintN("res = " + Str(res) + #TAB$ + StrD(Time, 3) + #TAB$ + Str(Time1) + #TAB$ + StrD(Time1 / Time, 3))

tz.q = ElapsedMilliseconds()
RDTSC(t0.q)
For i.q = 1 To nTimes
  !  MOV     rdi, qword [a_MyArray]
  !  SUB     rcx, rcx
  !  SUB     rax, rax
  !  NOT     rcx
  !  CLD
  !  REPNE SCASQ
  !  NOT     rcx
  !  DEC     rcx
  !  MOV     qword [v_res], rcx
Next
RDTSC(t1.q)
Time.d = (ElapsedMilliseconds() - tz) / 1000
Time1.d = t1 - t0
PrintN("res = " + Str(res) + #TAB$ + StrD(Time, 3) + #TAB$ + Str(Time1) + #TAB$ + StrD(Time1 / Time, 3))

tz.q = ElapsedMilliseconds()
RDTSC(t0.q)
For i.q = 1 To nTimes
  j.q = 0
  Repeat ; For j.q = 0 To SizedDataLength - 1
    If MyArray(j) = 0
      res = j
      Break
    EndIf
    j + 1
  Until j > ArraySize
Next
RDTSC(t1.q)
Time.d = (ElapsedMilliseconds() - tz) / 1000
Time1.d = t1 - t0
PrintN("res = " + Str(res) + #TAB$ + StrD(Time, 3) + #TAB$ + Str(Time1) + #TAB$ + StrD(Time1 / Time, 3))

tz.q = ElapsedMilliseconds()
RDTSC(t0.q)
*Array.Quad
j.q
For i.q = 1 To nTimes
; *Array => r8    j => r9
  !  MOV     r9, 0 ; j.q = 0
  !  MOV     r8, qword [a_MyArray] ; *Array.Quad = @Array()
! Repeat_It_Again: ; Repeat ; For j.q = 0 To SizedDataLength - 1
    !  CMP     qword [r8], 0 ; If *Array\q = 0
    !  JNE     @f
      !  MOV     qword [v_res], r9 ; res = j
      !  JMP     ShutUpYourMouth
! @@: ; EndIf
    !  ADD     r8, 8 ; *Array + 8
    !  INC     r9 ; j + 1
    !  CMP     r9, qword [v_ArraySize]
  !  JLE     Repeat_It_Again ; Until j => SizedDataLength ; Next
! ShutUpYourMouth:
Next
RDTSC(t1.q)
Time.d = (ElapsedMilliseconds() - tz) / 1000
Time1.d = t1 - t0
PrintN("res = " + Str(res) + #TAB$ + StrD(Time, 3) + #TAB$ + Str(Time1) + #TAB$ + StrD(Time1 / Time, 3))

tz.q = ElapsedMilliseconds()
RDTSC(t0.q)
*Array.Quad
j.q
For i.q = 1 To nTimes
; *Array => r8    ArraySize (décr.) => r9
  !  MOV     r8, qword [a_MyArray] ; *Array.Quad = @Array()
  !  MOV     r9, qword [v_ArraySize]
! Repeat_It_AgainAgain: ; Repeat ; For j.q = 0 To SizedDataLength - 1
    !  CMP     qword [r8], 0 ; If *Array\q = 0
    !  JNE     @f
      !  MOV     rax, qword [v_ArraySize] ; res = j
      !  SUB     rax, r9
      !  MOV     qword [v_res], rax
      !  JMP     ShutUpYourMouthAgain
! @@: ; EndIf
    !  ADD     r8, 8 ; *Array + 8
    !  DEC     r9 ; j + 1
  !  JNZ     Repeat_It_AgainAgain ; Until j => SizedDataLength ; Next
! ShutUpYourMouthAgain:
Next
RDTSC(t1.q)
Time.d = (ElapsedMilliseconds() - tz) / 1000
Time1.d = t1 - t0
PrintN("res = " + Str(res) + #TAB$ + StrD(Time, 3) + #TAB$ + Str(Time1) + #TAB$ + StrD(Time1 / Time, 3))

PrintN("")
PrintN("Push any key to quit ...")
While Inkey() = ""
  Delay(50)
Wend

CallDebugger
End
Mais il y a un truc qu'il faut bien garder en tête. RDTSC compte des cycles. Elapsedmilliseconds() compte des estimations de secondes. L'horloge de votre PC est peut-être stable à + ou - x %% près. Avec ça tu mets ta pipe à la pompe, tu mesures en gros l'épaisseur d'un courant d'air. Mais ça donne une idée.

L'exemple revu de code, ci-dessus, permet de voir si le nombre de cycles divisé par le temps estimé donne une vitesse d'horloge proche de la vitesse d'horloge connue.

Avec ça, si tu sais que ton horloge de CPU est stable, mais vraiment assez stable, ça va te donner une idée pour savoir si ton code bouffe vraiment pas loin de 100% de CPU (une CPU). Maintenant si tu veux tester un truc qui chafouine les 4 cylindres de ton moteur en même temps, va pas chercher à comparer chacune des pattes, ça fait perdre un temps fou à mesurer et ça sert à rien.

Le bon, le vrai test, une fois qu'on a fini de travailler au doigt mouillé, c'est de lancer un truc qu'on connaît bien, et de regarder sa tocante. Y a pas mieux. Et quand le moulin pédale très vite, on lui mets de quoi tenir au moins un tour de trotteuse et on se rend mieux compte.

Donc on va avancer avec tout ça, et on mesurera un scan de 1e12 qui donne la quantité de premiers et les écarts comptés et rangés quand on aura fait le tour de tout ça.

:)
Mon avatar reproduit l'image de 4x1.8m présentée au 'Salon international du meuble de Paris' en janvier 2004, dans l'exposition 'Shades' réunisant 22 créateurs autour de Matt Sindall. L'original est un stratifié en 150 dpi.
Ollivier
Messages : 4190
Inscription : ven. 29/juin/2007 17:50
Localisation : Encore ?
Contact :

Re: A propos de nombres premiers

Message par Ollivier »

D'accord, c'est bon ça les synthèses! Mais la conclusion, c'est quoi?
>> Je peux aller me brosser avec REP SCASQ et mon crible à l'envers? :mrgreen:
(ça semble à l'eau, sur papier, j'ai un nombre premier trouvé toutes les 3 secondes à 1e9)
fweil
Messages : 505
Inscription : dim. 16/mai/2004 17:50
Localisation : Bayonne (64)
Contact :

Re: A propos de nombres premiers

Message par fweil »

Olllivier, pour la brosse, mets la toujours de côté, ça peut servir, mais je vais sans doute d'ici une petite poignée de jours, quelques vraiment pas plus, une procédure en place, utilisant un scan avec rep là où il peut être productif. Ce sera une version d'algo dans le programme, parmi les autres, donc pas de chamboule tout et on verra ce qui rend service.

Dans le même temps j'essaye de voir comment injecter des données et utiliser la vectorisation au mieux, mais si tu as des idées là-dessus, tu es largement bienvenu. Moi je vais tenter de trouver où et comment vectoriser dans mon code, en priorité, mais tu peux aussi avoir d'autres idées, parce que j'ai pas l'impression d'être plus créatif que ça.

Ce qui me chagrine sur la totalité de lo'ffre SIMD sur x64 c'est qu'on entre bien dans le moule de l'arithmétique flottante, mais dès qu'il s'agit d'arithmétique entière, il y a des portes qui se ferment. Là je suis en train d'essayer de me documenter en langage humain sur VCVTTPD2Q et je ne trouve que de l'antarien ou du végan.
Mon avatar reproduit l'image de 4x1.8m présentée au 'Salon international du meuble de Paris' en janvier 2004, dans l'exposition 'Shades' réunisant 22 créateurs autour de Matt Sindall. L'original est un stratifié en 150 dpi.
Ollivier
Messages : 4190
Inscription : ven. 29/juin/2007 17:50
Localisation : Encore ?
Contact :

Re: A propos de nombres premiers

Message par Ollivier »

@Ar-S

Petite parenthèse sur ce qu'est le crible d'Erathostène visuellement. J'ai mis les cases des premiers en rouge et des flèches pour se déplacer : fonce vers la droite et tâche de tenir la "ligne rouge" au milieu de l'écran avec les flèches haut et bas.

Pas d'optimisation (thread, ASM), juste de la recherche de diviseurs premiers (attention ! Tu vois du Erathostène, mais ce n'est pas du Erathostène!)

(Ça me fait penser : on n'aurait pas perdu Huitbit?)

Code : Tout sélectionner

;********************************************************************************************************************************************
Procedure InitScreen()
        ExamineDesktops()
        Global Dw = DesktopWidth(0)
        Global Dh = DesktopHeight(0)
        Global Dd = DesktopDepth(0)
        InitSprite()
        InitKeyboard()
        OpenScreen(Dw, Dh, Dd, "")
EndProcedure

Global Dim CharFont.I(80)
Global Dim CharSprite.I(80, 255)
Global Dim CharW.I(80, 255)
Global Dim CharH.I(80, 255)

#BackColor = $406080
#CaseBackColor = $FFFFFF
#CaseTextColor = $000000

Procedure InitSpriteCharacter(SizeList.S)
        Define SizeSize = CountString(SizeList, ";")
        Dim Size.I(SizeSize)
        For K = 0 To SizeSize
                Size(K) = Val(StringField(SizeList, 1 + K, ";") )
        Next
        For K = 0 To SizeSize
                J = Size(K)
                CharFont(J) = LoadFont(#PB_Any, "Verdana", J)
        Next

StartDrawing(ScreenOutput() )
        For K = 0 To SizeSize
                J = Size(K)
        DrawingFont(FontID(CharFont(J) ) )
        For I = 1 To 255
                CharW(J, I) = TextWidth(Chr(I) )
                CharH(J, I) = TextHeight(Chr(I) )
        Next
        Next
StopDrawing()

        For K = 0 To SizeSize
                J = Size(K)
        For I = 48 To 57
                CharSprite(J, I) = CreateSprite(#PB_Any, CharW(J, I), CharH(J, I), #PB_Sprite_AlphaBlending)
                StartDrawing(SpriteOutput(CharSprite(J, I) ) )
                        DrawingFont(FontID(CharFont(J) ) )
                        DrawingMode(#PB_2DDrawing_AllChannels)
                        DrawText(0, 0, Chr(I), RGBA(0, 0, 0, 255), RGBA(255, 255, 255, 0) )
                StopDrawing()
        Next
        For I = 65 To 90
                CharSprite(J, I) = CreateSprite(#PB_Any, CharW(J, I), CharH(J, I), #PB_Sprite_AlphaBlending)
                StartDrawing(SpriteOutput(CharSprite(J, I) ) )
                        DrawingFont(FontID(CharFont(J) ) )
                        DrawingMode(#PB_2DDrawing_AllChannels)
                        DrawText(0, 0, Chr(I), RGBA(0, 0, 0, 255), RGBA(255, 255, 255, 0) )
                StopDrawing()
        Next
Next
EndProcedure

Procedure DisplayText(x, y, A$, Size, Color, Angle = 0)
        Define AngleRad.F = Angle * #PI / 180.0
        If Angle
                For I = 1 To Len(A$)
                        A = Asc(Mid(A$, I, 1) )
                        If Angle
                                RotateSprite(CharSprite(Size, A), Angle, #PB_Absolute)
                        EndIf
                        DisplayTransparentSprite(CharSprite(Size, A), X, Y, 255, Color)
                        X + (CharW(Size, A) * Cos(AngleRad) )
                        Y + (CharW(Size, A) * Sin(AngleRad) )
                Next
        Else
                For I = 1 To Len(A$)
                        A = Asc(Mid(A$, I, 1) )
                        RotateSprite(CharSprite(Size, A), 0, #PB_Absolute)
                        DisplayTransparentSprite(CharSprite(Size, A), X, Y, 255, Color)
                        X + CharW(Size, A)
                Next
        EndIf
EndProcedure

Macro GraphDrawExemple()
InitSpriteCharacter()
Repeat
        Lap0 = Lap
        Lap = ElapsedMilliseconds()
        Delay(16)
        ExamineKeyboard()
        ClearScreen(#BackColor)
        DisplayText(0, 0, Str(Lap - Lap0), 20, RGB(255, 0, 0) )
        FlipBuffers()        
Until KeyboardPushed(#PB_Key_Escape)
EndMacro

Macro AjoutePremier(X)
        Prim(PrimeQuantity) = X
        Prime2(PrimeQuantity) = X * X
        PrimeQuantity + 1
EndMacro

Macro IsPrime5(Nombre)
        bPremier = 1
        For Jprime = 2 To MostPrime2
                If Nombre % Prim(Jprime) = 0
                        bPremier = 0
                        Break
                EndIf
        Next
EndMacro

Macro TestPrime5(Pas)
        If Prime2(MostPrime2 + 1) <= Iprime
                MostPrime2 + 1
        EndIf
        IsPrime5(Iprime)
        If bPremier
                AjoutePremier(Iprime)
        EndIf
        Iprime + Pas
EndMacro

InitScreen()
InitSpriteCharacter("10;20")


nDebut = 5
nFin = 100000
Global Dim Prim.I(nFin)
Global Dim Prime2.I(nFin)
AjoutePremier(2) 
AjoutePremier(3)
Iprime = nDebut
Repeat
        TestPrime5(2)
        TestPrime5(4)
Until Iprime > nFin

MaxPrime = PrimeQuantity - 1

BackColor = RGB(0, 0, 255)

CellW = 64
CellH = 24
FirstColumnW = 40
FirstRowH = 80

CribleSp = CreateSprite(#PB_Any, CellW, CellH, #PB_Sprite_AlphaBlending)
If StartDrawing(SpriteOutput(CribleSp) )
        DrawingMode(#PB_2DDrawing_AllChannels)
        W = OutputWidth()
        H = OutputHeight()
        Box(0, 0, W, H, RGBA(0, 0, 0, 255) )
        StopDrawing()
EndIf

Crible1er = CreateSprite(#PB_Any, CellW, CellH, #PB_Sprite_AlphaBlending)
If StartDrawing(SpriteOutput(Crible1er) )
        DrawingMode(#PB_2DDrawing_AllChannels)
        W = OutputWidth()
        H = OutputHeight()
        Box(0, 0, W, H, RGBA(255, 0, 0, 255) )
        StopDrawing()
EndIf

DecalYMax = (CellH * (MaxPrime + 1) ) - (Dh - FirstRowH) 
Repeat
        Lap0 = Lap
        Lap = ElapsedMilliseconds()
        Delay(16)
        ExamineKeyboard()
        If KeyboardPushed(#PB_Key_Right)
                DecalAX + 2
        EndIf        
        If KeyboardPushed(#PB_Key_Left)
                DecalAX - 2
        EndIf        
        If KeyboardPushed(#PB_Key_Down)
                DecalAY + 2
        EndIf        
        If KeyboardPushed(#PB_Key_Up)
                DecalAY - 2
        EndIf        
        If DecalAX
                DecalX + DecalAX
                If DecalAX > 0
                        DecalAX - 1
                Else
                        DecalAX + 1
                EndIf
                If DecalX < 0
                        DecalX = 0
                        DecalAX = 0
                EndIf
        EndIf
        If DecalAY
                DecalY + DecalAY
                If DecalAY > 0
                        DecalAY - 1
                Else
                        DecalAY + 1
                EndIf
                If DecalY < 0
                        DecalY = 0
                        DecalAY = 0
                EndIf
                If DecalY > DecalYMax
                        DecalY = DecalYMax
                        DecalAY = 0
                EndIf
        EndIf
        ClearScreen(BackColor)
        DisplayText(0, 0, Str(Lap), 20, RGB(255, 0, 0) )
        Displaytext(200, 0, Str(Prim(MaxPrime) ), 20, RGB(0, 255, 255) )
        JDebutY = DecalY / CellH
        JFinY = JDebutY + ((Dh - FirstRowH) / CellH)
        For I = JDebutY To JFinY
                DisplayText(0, FirstRowH + CellH * I - DecalY, Str(Prim(I) ), 10, RGB(255, 255, 255) )
        Next
        JDebutX = DecalX / CellW + 1
        JFinX = JDebutX + ((Dw - FirstColumnW) / CellW)
        For J = JDebutX To JFinX
                DisplayText(FirstColumnW + CellW * J - DecalX, 40, Str(J), 10, RGB(255, 255, 255), -20)
        Next
        For I = JDebutY To JFinY
                For J = JDebutX To JFinX
                        X = FirstColumnW + CellW * J - DecalX
                        Y = FirstRowH + CellH * I - DecalY
                        If (J % Prim(I) ) = 0
                                If J = Prim(I)
                                        DisplaySprite(Crible1er, X, Y)
                                Else
                                        DisplaySprite(CribleSp, X, Y)
                                EndIf
                        EndIf
                Next
        Next
        FlipBuffers()        
Until KeyboardPushed(#PB_Key_Escape)
fweil
Messages : 505
Inscription : dim. 16/mai/2004 17:50
Localisation : Bayonne (64)
Contact :

Re: A propos de nombres premiers

Message par fweil »

Alors moi j'ai réussi à monter une marche d'escalier ...

Code : Tout sélectionner

Structure PACKED4D ; structure permettant de précharger des Doubles
  d0.d             ; le format est 4 x 64 = 256 bits.
  d1.d             ; Ce format correspond à un registre ymm
  d2.d
  d3.d
EndStructure

Structure PACKED8D ; structure en 512 bits correspondant à deux PACKED4D
  D4_0.PACKED4D    ; Ce format correspond à un registre zmm (ou 2 ymm)
  D4_1.PACKED4D
EndStructure

nTimes.q = 100000000

  If OpenConsole()
    ;
    ; On fait des presets
    ;
    packed_in.PACKED8D
    packed_in\D4_0\d0 = 120
    packed_in\D4_0\d1 = 121
    packed_in\D4_0\d2 = 122
    packed_in\D4_0\d3 = 123
    packed_in\D4_1\d0 = 7
    packed_in\D4_1\d1 = 7
    packed_in\D4_1\d2 = 7
    packed_in\D4_1\d3 = 7
    *packed_in = @packed_in
    packed_out.PACKED4D
    *packed_out = @packed_out
    
    ;
    ; Premier jeu de test en PB classique
    ;
    tz.q = ElapsedMilliseconds()
    For i.q = 1 To nTimes
      packed_out\d0 = packed_in\D4_0\d0 - packed_in\D4_1\d0 * Int(packed_in\D4_0\d0 / packed_in\D4_1\d0)
      packed_out\d1 = packed_in\D4_0\d1 - packed_in\D4_1\d1 * Int(packed_in\D4_0\d1 / packed_in\D4_1\d1)
      packed_out\d2 = packed_in\D4_0\d2 - packed_in\D4_1\d2 * Int(packed_in\D4_0\d2 / packed_in\D4_1\d2)
      packed_out\d3 = packed_in\D4_0\d3 - packed_in\D4_1\d3 * Int(packed_in\D4_0\d3 / packed_in\D4_1\d3)
      result.q = #True
      If packed_out\d0 = 0
        result.q = #False
      EndIf
      If packed_out\d1 = 0
        result.q = #False
      EndIf
      If packed_out\d2 = 0
        result.q = #False
      EndIf
      If packed_out\d3 = 0
        result.q = #False
      EndIf
    Next i
    Time.d = (ElapsedMilliseconds() - tz) / 1000
    PrintN("result = " + Str(result) + #TAB$ + StrD(Time, 3))
    
    ;
    ; Second jeu de test en PB classique mais en utilisant des 
    ; variables directes (test de comparaison pour info sur le coût
    ; de l'utilisation d'une structure de preset
    ;
    tz.q = ElapsedMilliseconds()
    n0.q = packed_in\D4_0\d0
    n1.q = packed_in\D4_0\d1
    n2.q = packed_in\D4_0\d2
    n3.q = packed_in\D4_0\d3
    d0.q = packed_in\D4_1\d0
    d1.q = packed_in\D4_1\d1
    d2.q = packed_in\D4_1\d2
    d3.q = packed_in\D4_1\d3
    For i.q = 1 To nTimes
      r0.q = n0 - d0 * Int(n0 / d0)
      r1.q = n1 - d1 * Int(n1 / d1)
      r2.q = n2 - d2 * Int(n2 / d2)
      r3.q = n3 - d3 * Int(n3 / d3)
      result.q = #True
      If r0 = 0
        result.q = #False
      EndIf
      If r1 = 0
        result.q = #False
      EndIf
      If r2 = 0
        result.q = #False
      EndIf
      If r3 = 0
        result.q = #False
      EndIf
    Next i
    Time.d = (ElapsedMilliseconds() - tz) / 1000
    PrintN("result = " + Str(result) + #TAB$ + StrD(Time, 3))
    
    ;
    ; Même traitement en AVX (les tests à la fin seront en AVX
    ; quand je serai arrivé à trouver comment faire.
    ;
    tz.q = ElapsedMilliseconds()
    For i.q = 1 To nTimes
      !  mov      rax, [p_packed_in]
      !  mov      rbx, [p_packed_out]
      !  vmovupd  ymm0, [rax]
      !  vmovupd  ymm1, [rax+32]
      !  vdivpd   ymm2, ymm0, ymm1 ; D4_0 / D4_1
      !  vroundpd ymm2, ymm2, $01 ; INT() [round floor]
      !  vmulpd   ymm3, ymm2, ymm1 ; * D4_1
      !  vsubpd   ymm0, ymm0, ymm3 ; D4_0 -
      !  vmovupd  yword [rbx], ymm0
      result.q = #True
      If packed_out\d0 = 0
        result.q = #False
      EndIf
      If packed_out\d1 = 0
        result.q = #False
      EndIf
      If packed_out\d2 = 0
        result.q = #False
      EndIf
      If packed_out\d3 = 0
        result.q = #False
      EndIf
    Next i
    Time.d = (ElapsedMilliseconds() - tz) / 1000
    PrintN("result = " + Str(result) + #TAB$ + StrD(Time, 3))
    
    PrintN("")
    PrintN("Push any key to quit ...")
    While Inkey() = ""
      Delay(50)
    Wend
    CloseConsole()
  EndIf
  
End
Ca me dit en gros un ratio temps 7s sur le test 1, 5s sur le test 2 et 3s sur le test 3, très approximativement, et en valeur moyenne moyennée. Donc ce serait prometteur, mais il y a du boulot pour bien arriver à faire ça propre dans le programme.
Mon avatar reproduit l'image de 4x1.8m présentée au 'Salon international du meuble de Paris' en janvier 2004, dans l'exposition 'Shades' réunisant 22 créateurs autour de Matt Sindall. L'original est un stratifié en 150 dpi.
fweil
Messages : 505
Inscription : dim. 16/mai/2004 17:50
Localisation : Bayonne (64)
Contact :

Re: A propos de nombres premiers

Message par fweil »

J'ai utilisé un préchargement par une structure parce que c'est sans doute ça qui sera utilisé au final. Pour alimenter AVX il faut des données contigües en mémoire.

Ollivier c'est sympa la présentation en graphique ... et c'est du boulot à rédiger ^^
Mon avatar reproduit l'image de 4x1.8m présentée au 'Salon international du meuble de Paris' en janvier 2004, dans l'exposition 'Shades' réunisant 22 créateurs autour de Matt Sindall. L'original est un stratifié en 150 dpi.
Ollivier
Messages : 4190
Inscription : ven. 29/juin/2007 17:50
Localisation : Encore ?
Contact :

Re: A propos de nombres premiers

Message par Ollivier »

C'est mon Exzèleux en godeux zource bilingue!
Il te manque les flags... Tu peux "shifter"?

Les comparaisons en SSE&co, c'est bourrin : le résultat est dans l'opérande destination sous forme 0 (faux) ou -1 ($Ffff...ffffF) (vrai).
Répondre