A propos de nombres premiers

Partagez votre expérience de PureBasic avec les autres utilisateurs.
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 »

Starwolf20 a écrit :Djes : je suis vraiment impressioné par l'elegance et les performances de ton dernier code.
Ce thread est super interessant et les echanges entre Djes, Fweil et Ollivier passionants.

NB : je suis tombé sur ce lien http://compoasso.free.fr/primelistweb/p ... sthene.php
Je le soumets a votre sagacité.
Merci pour le compliment, je pense que nous prenons tous un certain plaisir à nous triturer ainsi les méninges ! La page que tu fournis est très impressionnante, j'ai l'impression de recréer chacune des améliorations au fur et à mesure ! Pourtant je ne me documente presque pas, car ici je m'amuse :) J'ai vu qu'à la fin il parle de masques, j'en parlais précédemment, si on s'amuse à faire des masques (et avec mini 64 bits, il y a de quoi faire), on peut cribler à toute vitesse, et ça ferait gagner pas mal de temps.
fweil a écrit :Bon pour le résultat final, mon 1e12 sort en moins de 3.000 secondes calcul et statistiques sur les écarts compris. C'est pas pour me la péter, mais je vais plus vite ... et plus loin grâce à la pagination.
Bon, bon, on le sait bien ! J'espère quand même qu'Ollivier va nous pondre un truc miraculeux pour sauver l'honneur ;)

Moi, pour le moment, je suis sur un travail de compression des nombres, car si on veut travailler plus loin, il faudra y passer. J'ai bricolé un truc sur les écarts. C'est améliorable ! Le fichier des écarts résultant est plus gros que le fichier des np, certes, mais regardez sa composition :) En le compressant en zip, on voit tout de suite qu'on y gagne... (nb : le code est moins propre, c'est du test...)

Code : Tout sélectionner

;All rights reserved :) All rights reversed :)) Feel aaaaaalright !!!
#Max = 100000000

EnableExplicit

Define u.i, time.i, counter = 0, file
Define *pn.INTEGER, *sieve.INTEGER
Define lastpn.i

OpenConsole()

;-*** Primes lookup ***

*pn = AllocateMemory(#Max)
*sieve = AllocateMemory(#Max / 8 )   
time = ElapsedMilliseconds()

u = 3 ;start after the "2"

EnableASM

mov rax, u
mov r9, rax
mov rdx, *sieve
mov rbx, *pn
;*** 1 is set manually ;)
mov qword [rbx], 1
add rbx, 8
;***
!LP:
;If BitNotSet(*sieve, u)
bt [rdx], rax
jc BITSET
;Store pn number
mov [rbx], rax
add rbx, 8
;Sieve(*sieve, u)
mov rcx, rax
mov r8, #Max
!@@:
bts [rdx], rax
add rax, rcx
cmp rax, r8
jl @b
!BITSET:

add r9, 2 ;jump over even numbers
mov rax, r9
cmp r9, #Max
jl LP
; How many pn ? (rbx - *pn)  / 8
sub rbx, *pn
shr rbx, 3
mov counter, rbx
DisableASM        

time = ElapsedMilliseconds() - time
PrintN("Time elapsed : " + Str(time) + " ms")
FreeMemory(*sieve)
     
file = CreateFile(#PB_Any, #PB_Compiler_FilePath + "primes-org.txt")
WriteStringN(file, "2, ")
For u = 1 To counter - 1
  WriteStringN(file, StrU(PeekI(*pn + u * 8)) + ",")
Next u
WriteStringN(file, "Found " + counter + " prime numbers")

lastpn = PeekI(*pn + counter * 8 - 8)
PrintN("Found " + counter + " prime numbers")

;- *** Crunching ***

PrintN("Let's start the crunch demo")

Define *pnPtr.INTEGER = *pn, n.i, *gaps, *gapsPtr.INTEGER, gapsNb.i

*gaps = AllocateMemory(#Max * 8)
*gapsPtr = *gaps

;First operation : pn / 2 (as a pn is always after a multiple of 2)
For u = 0 To counter - 1
  *pnPtr\i = *pnPtr\i >> 1
  *pnPtr + 8
Next u

*pnPtr = *pn

For u = 0 To lastpn / 2 + 2
  ;store only the gaps in the (0, 1, 2, 3, 4, 5, ...) serie
  If *pnPtr\i = u 
    *pnPtr + 8
  Else
    *gapsPtr\i = u
    *gapsPtr + 8
  EndIf
Next u

gapsNb = (*gapsPtr - *gaps) / 8
PrintN("Found " + StrU(gapsNb) + " gaps")

FreeMemory(*pn)

Define *crunched, *crunchedPtr.INTEGER
*crunched = AllocateMemory(#Max * 8)

file = CreateFile(#PB_Any, #PB_Compiler_FilePath + "primes-crunched.txt")

*gapsPtr = *gaps
*crunchedPtr = *crunched

n = 0

For u = 0 To gapsNb - 1
  ;store the difference between two gaps
  *crunchedPtr\i = *gapsPtr\i - n
  n = *gapsPtr\i
  WriteString(file, StrU(*crunchedPtr\i) + ",")        
  *crunchedPtr + 8
  *gapsPtr + 8
Next u

CloseFile(file)
FreeMemory(*gaps)
PrintN("Done ! ")

;- *** Uncrunching ***
PrintN("Let's start the uncrunch")     

*gaps = AllocateMemory(#Max * 8)
*gapsPtr = *gaps     
*crunchedPtr = *crunched

n = 0

For u = 0 To gapsNb - 1
  ;extract the difference between two gaps
  n + *crunchedPtr\i
  *gapsPtr\i = n
  *crunchedPtr + 8
  *gapsPtr + 8
Next u

*pn = AllocateMemory(#Max * 8)
*pnPtr = *pn
*gapsPtr = *gaps

For u = 0 To lastpn / 2  + 2
  ;recreate the pn based upon gaps
  If u <> *gapsPtr\i 
    *pnPtr\i = u
    *pnPtr + 8
  Else
    *gapsPtr + 8
  EndIf
Next u

FreeMemory(*gaps)

*pnPtr = *pn

For u = 0 To counter - 1
  *pnPtr\i = *pnPtr\i << 1 + 1
  *pnPtr + 8
Next u

;- PN definition bugfix ;)
*pn\i + 1

file = CreateFile(#PB_Any, #PB_Compiler_FilePath + "primes-uncrunched.txt")
For u = 0 To counter -1
  WriteStringN(file, StrU(PeekI(*pn + u * 8)) + ",")
Next u

FreeMemory(*pn)     
WriteStringN(file, "Found " + counter + " prime numbers")
CloseFile(file)

PrintN("Found " + counter + " prime numbers")
PrintN("Press enter to exit...")
Input()
CloseConsole()
Voici aussi un petit code qui permet de chercher les nombres premiers à l'aide de la méthode de l'essai de division (trial division). J'ai aussi implémenté la version avec le modulo comme dans la page citée par Starwolf20, pour comparer/valider.

Code : Tout sélectionner

;Primes nb lookup with trial division
; (c)djes 2016
; http://www.purebasic.fr/french/viewtopic.php?p=187333#p187333

Min = 100000000000000003
Max = 100000000000000100

EnableExplicit

Define u.i, i.i, n.i, time.i, counter = 0, sqrt.i
Define *pn, *sieve

OpenConsole()

If Max > 2 And Max >= Min + 1 And Min >= 3
    
  If Min & 1 = 0
    Min + 1
  EndIf
  
  PrintN("Lookup for primes between " + Str(Min) + " and " + Str(Max))

  For i = Min To Max Step 2
    
    sqrt = Int(Sqr(i) + 1)  ;beware of the int() !
    
    time = ElapsedMilliseconds()
    
    For n = 3 To sqrt Step 2
      u = i / n
      If u * n = i
        Break
      EndIf
    Next n
    
    If n >= sqrt
      Print(Str(i) + ", ")
      Print("time elapsed : " + Str(ElapsedMilliseconds() - time) + " ms (div/mul),  ")
    EndIf
    
     time = ElapsedMilliseconds()
    
    For n = 3 To sqrt Step 2
      If i%n = 0
        Break
      EndIf
    Next n
    
    If n >= sqrt
      counter + 1
      Print(Str(i) + ", ")
      PrintN(Str(ElapsedMilliseconds() - time) + " ms (modulo)")
    EndIf
    
  Next i
  
  PrintN("Finished !")
  PrintN("Found " + Str(counter) + " primes numbers ")
  PrintN("")
  PrintN("Press enter to exit")
  
  Input()
  CloseConsole()
  
EndIf


Starwolf20
Messages : 10
Inscription : mar. 01/sept./2009 18:39

Re: A propos de nombres premiers

Message par Starwolf20 »

Voici un de mes vieux code qui permet de calculer egalement les 1ers nombres premiers par le crible d'erastotheme.
Il ameliore le parcours habituel de 2 en 2 en utilisant une serie d'increments qui elimine les multiples de 2, 3, 5, 7.
D'apres mes souvenirs, on divise par #2 le nombre de boucles.

Code : Tout sélectionner

; Recherche de nombres premiers.
; Starwolf : 11/10/2010

Global Dim NPrimes.l(10000000)   ; Tableau des nombres premiers
Global CPTPrimes.l = 0           ; Nombre des nombres premiers dans le tableau NPrimes.
Global n.l

Procedure NPremiers_Erasto7(n)
; Recherche des nombres premiers jusqu'a n par le crible d'erastotene amelioré.
; Le tableau NPrimes contient la liste des nombres premiers.
; Le dernier element dans le tableau NPrimes vaut -1.
; CPTPrimes contient la nombre de nombres premiers trouvé.
;
Protected i.l, j.l
Protected Dim Erasto.l(47)
Protected CptErasto.l = -1
Protected nbTest.l = 0

Protected dtime = ElapsedMilliseconds()

;=== Initialisation du tableau Erasto
DataSection
Erasto7:    
Data.i 2, 4, 2, 4, 6, 2, 6, 4, 2, 4, 6, 6, 2, 6, 4, 2, 6, 4, 6, 8, 4, 2, 4, 2, 4, 8, 6, 4, 6, 2, 4, 6, 2, 6, 6, 4, 2, 4, 6, 2, 6, 4, 2, 4, 2, 10, 2, 10
EndDataSection

Restore Erasto7
For i = 0 To 47
  Read.i Erasto(i)
  ;Erasto(i) = 2
Next i

;=== Initialisation du tableau des nombres premiers.
NPrimes(0) = 1
NPrimes(1) = 2
NPrimes(2) = 3
NPrimes(3) = 5
NPrimes(4) = 7
NPrimes(5) = 11
CPTPrimes = 5

;=== Boucle principale de traitement de calcul des nombres premiers
i=11
Repeat
  ;=== Proposition d'un nombre premier potentiel.
	NbTest +1
	CptErasto +1
	i +Erasto(CptErasto)
	If i > n
		Break
	EndIf
	If CptErasto >= 47 
		CptErasto = -1
	EndIf
	
	;=== Validation du nombre premier.
	j = 5     ; j=5 avec erasto7 et j=3 avec 2
	Protected IsPrime = #True
	Protected Root.l=Sqr(i)
	While NPrimes(j) <= Root
		If i % NPrimes(j) = 0
		  IsPrime = #False
		  Break
		EndIf
		j +1
	Wend
	
	;=== C'est un nombre premier.
	If IsPrime = #True
		CPTPrimes +1
		NPrimes(CPTPrimes) = i
	EndIf
	
ForEver

NPrimes(CPTPrimes+1) = -1 ; marque la fin du tableau

PrintN("Erasto7 t=" + Str((ElapsedMilliseconds() - dtime)) + " " + Str(NbTest) + " tests pour " + Str(CPTPrimes) + " nombres premiers")

EndProcedure



OpenConsole()

n=10000000   

dtime_Total = ElapsedMilliseconds()
NPremiers_Erasto7(n)
PrintN("Temps de calcul total t=" + Str((ElapsedMilliseconds() - dtime_total)))
PrintN("Appuyez sur Entrée pour sortir")

a$=Input()
Ollivier
Messages : 4190
Inscription : ven. 29/juin/2007 17:50
Localisation : Encore ?
Contact :

Re: A propos de nombres premiers

Message par Ollivier »

Djes a écrit :Bon, bon, on le sait bien ! J'espère quand même qu'Ollivier va nous pondre un truc
miraculeux pour sauver l'honneur
Ton espoir n'est pas vain !
Le moment d'apporter un code source dépend de mon temps disponible, de ma possibilité de disposer d'un ordinateur, de mes possibilités de me concentrer sur le sujet.

Je n'ai pas encore pu exécuter le code (page 1) de fweil. Et j'ai déjà conçu un code (je l'avais communiqué à fweil) non optimisé qui pulse "virtuellement" 1.E09 vérifications en une bonne poignées de secondes : il doit être, au grand maximum 10 fois plus lent que celui de fweil (je n'ai pas encore comparé).

Pour le "bit à bit", je vais te décevoir, car je vais tricher!

C'est assez motivant d'avoir un leader qui tire la bourre!

Je salue StarWolf et remercie son info sur le site d'une équipe qui retrace et communique sa "montée en puissance".

C'est très troublant de voir à quel point la logique et les mathématiques nous unissent : nous cherchons des solutions au plus profond de notre esprit et nous aboutissons à des conclusions complexes comme si nous nous étions tous plaggié !!!

J'ai eu ce sentiment quand PAPIPP a posté son sujet il y a deux ans. Je me suis dit "C'est pas possible ! Le gars a été lire mes notes écrites : les mêmes nombres, les mêmes méthodes, les mêmes questions, les mêmes observations, et les mêmes erreurs !!!" Pourtant, PAPIPP n'a aucun lien avec moi (il pourra le dire).

Là, sur le site proposé par StarWolf, cette histoire de carré est bien là, comme si j'avais lu le site, et secrètement plaggia ! Je dirais qu'heureusement que j'ai posté un épais commentaire qui explique comment j'en suis arrivé à choisir ce départ au carré!

fweil devrait rajouter un message en tête de sujet "Attention, les nombres premiers forment une quête qui peut vous faire péter un câble s'il vous manque une case au préalable : ne pénétrez ce sujet que si cette quête n'est qu'un jeu cérébral ludique pour vous!"
Ollivier
Messages : 4190
Inscription : ven. 29/juin/2007 17:50
Localisation : Encore ?
Contact :

Re: A propos de nombres premiers

Message par Ollivier »

@fweil

Je te parie sans vérifier que, dans cet extrait du dernier code de StarWolf (que je remercie aussi pour sa participation au sujet, c'est sympatique)

Code : Tout sélectionner

Data.i 2, 4, 2, 4, 6, 2, 6, 4, 2, 4, 6, 6, 2, 6, 4, 2, 6, 4, 6, 8, 4, 2, 4,
2, 4, 8, 6, 4, 6, 2, 4, 6, 2, 6, 6, 4, 2, 4, 6, 2, 6, 4, 2, 4, 2, 10, 2, 10
La somme de ces nombres donne 210
avec 210 = pFact(7) = 7 * 5 * 3 * 2
!
Ça correspond au "Pif11()"

C'est incroyable! Même pas besoin de toucher un clavier!
Ollivier
Messages : 4190
Inscription : ven. 29/juin/2007 17:50
Localisation : Encore ?
Contact :

Re: A propos de nombres premiers

Message par Ollivier »

@Djes

Je pense que tu si regardes ma derniére "truc&astuce", je vais convertir les états en bits : c'est ça ma "tricherie".
Il y a besoin du binaire pour la partie réseaux local et internet.
La compression se fera en amont. Elle est "naturelle" de par les règles arithmétiques qui poussent à exclure le superflu.
En aval, la conversion quad/binaire est aussi une seconde couche de compression "naturelle".

Cela permet un traitement AVX immédiat qui a notamment pour rôle de "fusionner" les cribles.

Cette architecture permet de dicerner les calculs de premiers par échelles : par exemple, certains ordinateurs peuvent traiter les tests des nombres premiers inférieurs à 64, recevoir des pages binaires de vérification de nombres supérieurs à 64, faire la fusion AVX et transmettre ça au serveur central.

En gros, je pense (fortement) que l'on a besoin des nombres binaires pour hiérarchiser le réseaux, à l'instar de nos vertèbres qui ne réexpédient pas toutes les infos qu'elles reçoivent des axones en aval.

Un point important aussi c'est d'établir les ordinateurs qui ont le compilateur PureBasic en fonctionnement de ceux qui ne l'ont pas. Les premiers vont pouvoir être fournisseur de routines de criblages (fonctions DLL). Et les seconds exécuteront (après un test d'authenticité aussi puissant que simple, vu qu'il n'y a que 3 instructions machine qui devront être utilisées, et 1 qui se répètera des milliers de fois tandis que les 2 autres serviront l'une de départ de fonction DLL, et l'autre de fin de fonction DLL) ces fonctions DLL qui auront, comme référence dans leur noms l' "étage" (ou échelle) de calcul, le "rang" du calcul et l' "offset" du calcul.

Je pense que si fweil veut battre un tel système, il faudra qu'il demande des renseignements sur le commencement de crible à P * P * P quand un premier P est détecté. Là, je crois que seul un mathématicien peut proposer des solutions. Là, je suis en touche. Je sais qu'il faut étudier dans les fameuses exceptions (ce sont des cribles déjà effectués alors qu'ils ne sont pas censés l'être encore). C'est trop costaud pour moi, alors qu'un mathématicien, ça, c'est son dada.
fweil
Messages : 505
Inscription : dim. 16/mai/2004 17:50
Localisation : Bayonne (64)
Contact :

Re: A propos de nombres premiers

Message par fweil »

Pour l'instant je me concentre encore sur quelques détails, moins mathématiques et plus électroniques en quelque sorte. Bon je finirai bien un jour par trouver une voie alternative pour aller encore plus vite, et je pense qu'en travaillant encore le sujet avec vous qui vous y intéressez ici, on y arrivera.

En attendant, ma version 7.3 est presque cuite. Encore un peu de patience.

Ah oui, j'allais oublier : mon code à très peu évolué, mais tout de même quelques petits trucs ont bougé.

Donc les valeurs temps de référence sur mon PC fixe :

- 1e9 en 1,122 secondes
- 1e10 en 11,614 secondes
- 1e11 en 104,704 secondes
- 1e12 en 1241,170 secondes

Ces temps sont "tout compris", avec la production des écarts et leurs stats inclus.

:) :) :)

Je suis fatigué mais content d'avoir assassiné mon record précédent (divisé par plus de 2 pour 1e12).
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 »

On synthétise. Je t'ai filé il y a environ 2 semaines un petit source, qui, par déduction prend 10 fois plus de temps.
On va faire large : 100 fois plus de temps.

Il n'a pas d'ASM.
Il n'est pas multi-threadé.
Il n'y a pas d'AVX.
Il n'est pas virtualisé.

Pendant une quinzaine de jours, je ne pourrai rien faire, ça te laisse encore du temps pour pousser l'optimisation!

Après ça, sachant que je n'ai rien compris à ta pagination, ce sera à mon tour de virtualiser.

Je peux t'assurer qu'il sera moins gros que ta pavasse en page 1, qu'il y aura l'affichage, et que je comptabiliserai en temps, plus que ce qui est déjà comptabilisé (allocation tableaux, alloc mém., etc...) sinon tu vas me traiter de tricheur!
fweil
Messages : 505
Inscription : dim. 16/mai/2004 17:50
Localisation : Bayonne (64)
Contact :

Re: A propos de nombres premiers

Message par fweil »

@Ollivier, tu te trompes de client je crois sur un point. Je ne suis pas en compétition sur ce sujet, avec personne, et je suis dans une démarche projet sur laquelle j'ai mes propres priorités.

Donc je ne vais pas écrire le programme d'un autre qui ne rentrerai pas dans le fil de mon travail précis, mais je suis tout à fait disposé à contribuer à l'élaboration d'un progrès sur un algorithme semblable ou différent du mien. La seule chose c'est que je le fais, et je le ferai si cela sert à la fois à me faire avancer, et si cela permet de faire avancer les autres.

Tu m'as envoyé un code sur lequel j'ai apporté des points de correction et des indications pour le rendre correct. A toi de faire en sorte qu'il fonctionne déjà bien. En particulier sur la question des débordements mémoire. Je n'ai pas vu ton retour sur ce point. Je ferai de l'optimisation en profondeur, sans aucun problème, sur un code qui fonctionne sans erreur.

Ceci est le premier point.

Le second est celui de la pagination. J'ai fourni un listing complet qui décline l'algorithme de calcul en trois versions, et la pagination qui est principalement traitée dans la partie principale du programme. J'ai posté ici plusieurs commentaires pour expliquer le principe de découpe du calcul. Il est d'une simplicité absolue. Cela consiste à prendre une plage 1 à nmax et à la diviser en sous-plages 1 à n1, n1+1 à n2, n2 + 1 à n3, ... np + 1 à nmax. La production des nombres premiers se fait alors à l'intérieur de chaque sous-plages et le travail du programme principal est de collecter les résultats pour les fusionner.

Pour ce faire il suffit d'utiliser un jeu de tables qui accumulent les résultats.

Dans mon programme, accessoirement, on ne produit pas un inventaire des nombres premiers trouvés, mais seulement des écarts entre eux, et un décompte du total des nombres premiers trouvés. C'est une démographie qui ne nécessite pas de connaître à postériori les valeurs individuelles. Mais il est simple et facile d'envisager aussi le stockage des valeurs des nombres premiers en sortie sur des fichiers disque si on souhaite les avoir sous la main. Ca ne ralentirait pas beaucoup le processus, sauf que stocker les valeurs des nombres premiers sur une portée de 1e15 est un point qui pose question pour un stockage sur le disque local.

La difficulté à lever est donc, pour mon projet, de calculer vite, très vite, et de gérer les pièces du puzzle, en se donnant les moyens de faire cela en multithread. Pour comparer concrètement les performances d'algorithmes "concurrents", le mieux est que chacun tente de résoudre cette aspect de performance qui est le multithread. A chacun d'y apporter une réponse si possible. Moi j'ai approté la mienne, et j'en partage le code qui fonctionne déjà.

Au bout de cela, je ne me permettrai pas de juger du travail de chacun, au contraire, je suis ouvert et empathique pour tout contributeur à la réflexion générale sur le sujet, et je ne juge surtout pas les qualités de chacun ni sur le fond ni sur la forme. On est là pour partager quelque chose de manière conviviale et pour mesurer ce que donnent ces p...n de machines quand on leur cause correct.

Voili, voilà.

Aujourd'hui c'est une belle journée d'automne, donc il faut que j'invente une nouvelle idée sinon les feuilles vont finir par toutes tomber.
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 »

Oui, il faut continuer à avoir cet esprit scientifique et collaboratif. Notre but est le même, et nous partageons nos ressources et notre savoir-faire. Moi je pars de très loin, aussi j'essaye d'attraper le train en marche, de bien comprendre le sujet. C'est pour cela que je ne commente pas les codes présentés par fweil, ollivier, pappip : je n'y suis pas encore !

Hier j'ai passé une heure à essayer de faire une multiplication 64 bits «native» en assembleur. La syntaxe fasm est parfois déroutante... C'est du temps perdu, mais au moins, je maîtriserai le truc.

Fweil> Chapeau pour les nouvelles perfs !
fweil
Messages : 505
Inscription : dim. 16/mai/2004 17:50
Localisation : Bayonne (64)
Contact :

Re: A propos de nombres premiers

Message par fweil »

@djes pour la multiplication native il faut faire la part des choses entre deux composants du processeur, l'UAL classique qui permet de faire des multiplications en arithmétique entière, et le FPU qui permet de faire des multiplications en flottants.

Pour faire simple, l'UAL de base permet le traitement d'une arithmétique entière en 32 ou 64 bits. On va s'en tenir au 64 bits ici. L'instruction FASM est IMUL qui fonctionne comme les autres instructions arithmétiques en utilisant des arguments sources et une destination. On gère le débordement de valeur pour le résultat avec les flags du registre d'état.

Pour la partie FPU c'est un peu plus poilu, puisqu'on ne négocie pas les sources et destinations du FPU avec les registres de l'UAL. Le FPU travaille en source et destination avec des adresses mémoire vive et des registres qui lui sont propres. De plus ces registres sont dans un format qui assure un calcul de résultats sur 80 bits en interne, de manière à garantir une précision de résultat supérieure à ce que permet la représentation des flottants en 64 bits. Mais les échanges entre mémoire et FPU se font bien en 64 bits. L'instruction FASM pour multiplier avec le FPU est FMUL.

Pour bien négocier avec ces instruments, il est important de comprendre comment alimenter les composants et comment récupérer les résultats.

Ici en quelques lignes :

Code : Tout sélectionner

qa.q = 12
qb.q = 13
qc.q

!  mov     rax, qword [v_qa] ; chargement d'un registre choisi avec qa
!  imul    rax, qword [v_qb] ; multiplication registre x mémoire qb et résultat dans le registre utilisé
!  mov     qword [v_qc], rax ; envoi du registre dans qc

Debug qc

da.d = 12
db.d = 13
dc.d

!  fld     qword [v_da] ; envoi de da dans le premier registre
!  fld     qword [v_db] ; envoi de db dans le second registre
!  fmul    st0, st1 ; multiplication des deux registres. Le résultat est mis au sommet de la pile
!  fstp    qword [v_dc] ; envoi du sommet de la pile dans dc

Debug dc

CallDebugger

End
Ce sont des exemples, non exhaustifs des possibilités, mais clairs puisque simples, et faciles à adapter.

Une chose particulière différencie les registres UAL des registres FPU. L'UAL dispose de registres nommés, accessibles par leur nom. Le FPU dispose d'un jeu de registres que l'on peut nommer dans certains cas, st0, st1, ... mais de fait ils sont des éléments d'une pile qu'il ne faut pas concevoir autrement que comme une pile. Certaines instructions utilisent un élément nommé de la pile, d'autres non. Et certaines instructions vont permettre de prendre l'élément du haut de la pile (st0) et de shifter la pile (on supprime l'élément et on fait remonter tous les autres d'un cran), et certaines instructions vont maintenir st0 pour permettre sa réutilisation (par exemple dans l'instruction suivante). C'est assez touffu à bien gérer, et le mieux est de bien lire la doc FASM (https://flatassembler.net/docs.php et de jouer un peu avec.

J'espère que ça répond à ton questionnement ?
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 »

Merci de prendre un peu de temps pour répondre à cette question particulière. J'aurais bien apprécié ton aide hier ! En fait j'ai bataillé car je voulais faire un simple carré, et j'ai donc fort logiquement tenté un mul rax, rax. Manque de bol, ça ne fonctionne pas. J'ai cherché du côté des nouvelles instructions et trouvé un mulx du plus bel effet. Pour faire court, ça n'a rien donné, pas plus que le mulq que ne connaît pas fasm. Après un détour par x topics sans résultat, je suis revenu à un bête mul rax (qui multiplie par l'accumulateur). Super, ça fonctionne ! Pas de chance, j'ai une erreur sur l'instruction suivante, et ça, c'est plus pénible ! Du coup j'ai laissé choir ! Il y a des jours comme ça... C'est marrant parce que pour la racine carré, j'avais trouvé tout de suite, alors qu'au final, je ne m'en suis pas servi. :roll:

Edit : tiens, essaye ça avec le déboggueur...

Code : Tout sélectionner

b = 0
*a = @b
EnableASM
;DisableDebugger
mov rdx,*a
!mov rax, 4
!mul rax
!mov [rdx], rax
DisableASM
;EnableDebugger
Debug b
End
Edit2: Ca va mieux quand le registre n'est pas modifié ;)

Code : Tout sélectionner

OpenConsole()
b = 0
*a = @b
EnableASM
;DisableDebugger
mov r8,*a
mov rax, 4
imul rax
mov [r8], rax
DisableASM
;EnableDebugger
PrintN(Str(b))
Input()
End
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 »

Voilà la dernière version du cribble pas trop optimisé : toujours pas de masques, ni de pagination. C'est quand même bien plus rapide. Par contre, je ne peux plus aller jusqu'au milliard, j'ai sûrement un dépassement de capacité quelque part. J'ai prévu large pour les buffers, mais ça ne vient pas de là.

Code : Tout sélectionner

;All rights reserved :) All rights reversed :)) Feel aaaaaalright !!!
#Max = 100000000

EnableExplicit

Define u.i, time.i, counter = 0, file
Define *pn.INTEGER, *sieve.INTEGER
Define lastpn.i

OpenConsole()

;-*** Primes lookup ***

*pn = AllocateMemory(#Max*8)
*sieve = AllocateMemory(#Max)
time = ElapsedMilliseconds()

u = 3 ;start after the "2"

EnableASM

mov rcx, u
mov r8, *sieve
mov r9, #Max
mov r10, r9
!cvtsi2sd xmm0, r9
!sqrtsd xmm1, xmm0
!cvtsd2si r9, xmm1
inc r9  ;Sqr(#Max) + 1
!LP:
;If BitNotSet(*sieve, u)
bt [r8], rcx
jc BITSET
;Sieve(*sieve, u)
mov rax, rcx
mul rax
!@@:
bts [r8], rax
add rax, rcx
add rax, rcx
cmp rax, r10
jl @b
!BITSET:
add rcx, 2 ;jump over even numbers
cmp rcx, r9
jl LP
DisableASM

time = ElapsedMilliseconds() - time
PrintN("Time elapsed : " + Str(time) + " ms")
time = ElapsedMilliseconds()

EnableASM
; How many pn ?
mov rcx, u
mov r8, *sieve
mov r9, *pn
mov r10, #Max
;add '1' manually ;)
mov qword [r9], 1
add r9, 8
!LP2:
;If BitSet(*sieve, u)
bt [r8], rcx
jc BITNOTSET
;Store pn number
mov [r9], rcx
add r9, 8
!BITNOTSET:
add rcx, 2
cmp rcx, r10
jl LP2

sub r9, *pn
shr r9, 3
mov counter, r9
DisableASM        

time = ElapsedMilliseconds() - time
PrintN("Time elapsed : " + Str(time) + " ms")
FreeMemory(*sieve)

file = CreateFile(#PB_Any, #PB_Compiler_FilePath + "primes-org.txt")
;>*** add 2 manually
WriteStringN(file, "2, ")
;<***
For u = 1 To counter - 1
  WriteStringN(file, StrU(PeekI(*pn + u * 8)) + ",")
Next u
WriteStringN(file, "Found " + counter + " prime numbers")

lastpn = PeekI(*pn + counter * 8 - 8)
PrintN("Found " + counter + " prime numbers")

PrintN("Press enter to exit...")
Input()
CloseConsole()
End
Ollivier
Messages : 4190
Inscription : ven. 29/juin/2007 17:50
Localisation : Encore ?
Contact :

Re: A propos de nombres premiers

Message par Ollivier »

fweil a écrit :Tu m'as envoyé un code sur lequel j'ai apporté des points de correction et des indications
pour le rendre correct. A toi de faire en sorte qu'il fonctionne déjà bien. En particulier sur
la question des débordements mémoire. Je n'ai pas vu ton retour sur ce point. Je ferai de
l'optimisation en profondeur, sans aucun problème, sur un code qui fonctionne sans
erreur.
Je suis étonné, je ne retrouve pas cette remarque sur les débordements mémoire. Il me semble qu'il se limitait à ce que PB a comme plafond mémoire en 32 bits.

Je vais le poster ici...
Ollivier
Messages : 4190
Inscription : ven. 29/juin/2007 17:50
Localisation : Encore ?
Contact :

Re: A propos de nombres premiers

Message par Ollivier »

Si tu as les repères de temps de ton message faisant état d'une remarque sur ce code, ça m'aiderait un tout petit peu pour te répondre. Je risque, par contre de tarder pour vérifier coerrectement.

Voilà (17/10 21:22)

Code : Tout sélectionner

DisableDebugger

Define.I

Global Nmax = ((1 << 30) + (0) ) - 1 ; Borne maxi Global RacineNmax = Sqr(Nmax)

QCQ = 30 * RacineNmax

Global Dim Multi.B(Nmax + QCQ) ; Création tableau d'états de chaque entier ; 0 = Premier ; 1 = Non premier

; En créant un tableau de "zéros", on considère, au départ que tous les nombres sont premiers

Multi(0) = 1 ; On considère 0 comme non premier 

Multi(1) = 1 ; On considère 1 comme non premier

Global Dim Prime(Nmax >> 4)

Lap0 = ElapsedMilliseconds()
 N = Nmin ; ...par la borne minimum
 ;Goto yyy
 N = 2 
I = 4 
Repeat
 Multi(I) = 1
 Multi(I + 2) = 1
 I + 4
 Until I > Nmax 
N = 3
 I = 9
 Repeat Multi(I) = 1 
I + 6
 Until I > Nmax
 N = 5
 I = 25 ; 5 * 5
 Repeat
 Multi(I) = 1
 Multi(I + 10) = 1 ; 5 * 2
 I + 20 ; 5 * 4 
Until I > Nmax

yyy: 
Macro Pif7(N, A0, A1, A2, A3, A4, A5, A6)

I = N * N ; 7 * 7 
I1 = (A0 * N) 
I2 = I1 + (A1 * N) 
I3 = I2 + (A2 * N) 
I4 = I3 + (A3 * N) 
I5 = I4 + (A4 * N)
 I6 = I5 + (A5 * N) 
I7 = I6 + (A6 * N)
 ID = 30 * N ; 30 * 7
 Repeat 
Multi(I) = 1
 Multi(I + I1) = 1 
Multi(I + I2) = 1 
Multi(I + I3) = 1 
Multi(I + I4) = 1
 Multi(I + I5) = 1
 Multi(I + I6) = 1
 Multi(I + I7) = 1
 I + ID ; 
Until I > Nmax 
EndMacro

Pif7(7, 4, 2, 4, 2, 4, 6, 2) 
Pif7(11, 2, 4, 2, 4, 6, 2, 6) 
Pif7(13, 4, 2, 4, 6, 2, 6, 4)
 Pif7(17, 2, 4, 6, 2, 6, 4, 2) 
Pif7(19, 4, 6, 2, 6, 4, 2, 4) 
Pif7(23, 6, 2, 6, 4, 2, 4, 2) 
Pif7(29, 2, 6, 4, 2, 4, 2, 4) 
Pif7(31, 6, 4, 2, 4, 2, 4, 6)
 Pif7(37, 4, 2, 4, 2, 4, 6, 2) 
Pif7(41, 2, 4, 2, 4, 6, 2, 6) 
Pif7(43, 4, 2, 4, 6, 2, 6, 4)

N = 47 
Repeat 
If Multi(N) = 0 ; La cible indique-t-elle un premier ?
 I = N * N
 Repeat Multi(I) = 1
 I + (2 * N) 
Until I > Nmax
 Else 
nn + 1
 If nn % 100 = 0
 Delay(1) ; Une petite respiration de temps en temps... EndIf
 EndIf 
N + 1
 Until N > RacineNmax 
xxx:
 Lap1 = ElapsedMilliseconds()

For I = 0 To Nmax
 If Multi(I) = 0 
Prime(Nprime) = I
 Nprime + 1
 EndIf 
Next ; 33 554 393 (2 063 689 / (32 Mega) )
 ; 16 777 213 (1 077 871 / (16 Mega) ) 
; 8388593 (564164 / (8 Mega) )

If Nprime <> 2063689
 B$ = "/!\ ALERTE /!\"
 Else
 B$ = "" 
EndIf 
A2$ = Str(Prime(Nprime - 2) ) + " " + Str(Prime(Nprime - 3) )
 A$ = "Dernier nombre premier trouvé " + Str(Prime(Nprime - 1) ) + " nn = " + Str(nn) MessageRequester(B$, "Ok" + Chr(10) + Str(Lap1 - Lap0) + "ms" + Chr(10) + Str(Nprime) + " nombres premiers détectés" + Chr(10) + A$ + Chr(10) + A2$)
Ollivier
Messages : 4190
Inscription : ven. 29/juin/2007 17:50
Localisation : Encore ?
Contact :

Re: A propos de nombres premiers

Message par Ollivier »

@fweil

En tout cas, non : je ne me "trompe pas de client" (je te cite!)

C'est une bel et bien une "compétition". Il n'y a rien à gagner évidemment, comme tu l'as dit.

C'est une "compétition" d'un temps qui commence un peu, quand on veut, et qui termine un peu quand on veut quelque jours après. Cela rejoint ton idée de rassembler les données aux final. Et avoir un lapin devant une troupe de lévriers, ça motive!!!

Je dois avouer apprécier en plus de ta venue, celle de PAPIPP, Djes, SPH, j'apprécie que Starwolf se soit manifesté et ait présenté une version supérieure à la mienne (un peu bâtarde, mais c'était pour t'expliquer cet algo, par un exemple.

En gros, si on architecture cet algo dans un système virtuel suffisamment souple, il y a possibilité d'obtenir une optimisation intéressante.

Je réfléchis à une optimisation qui dépasse la fréquence du processeur si l'on se réfère au balayage total. La limite absolument infranchissable étant le nombre de nombres premiers générés à la fréquence du processeur (en considérant un seul coeur). On en est loin, suffisamment loin pour se permettre d'insignifiants pieds de nez arithmétiques.
Répondre