Grâce à quelques très utile stackOverflow utilisateurs à Peu se tourner: qui bit est défini?, J'ai construit ma fonction (posté à la fin de la question).
Toutes les suggestions -- même les petites suggestions seraient appréciées. J'espère qu'il va faire de mon mieux, mais au moins il devrait m'apprendre quelque chose. :)
Vue d'ensemble
Cette fonction sera appelée à au moins 10à 13 fois, et éventuellement aussi souvent que 10à 15. C'est, ce code sera exécuté par mois en toute probabilité, de sorte que toute la performance conseils seraient utiles.
Cette fonction comptes pour 72-77% du programme du temps, basé sur le profilage et une douzaine de courses dans différentes configurations (optimisation de certains paramètres qui ne sont pas pertinentes ici).
Au moment où la fonction s'exécute dans une moyenne de 50 horloges. Je ne sais pas combien cela peut être amélioré, mais je serais ravis de les voir courir dans 30.
La Clé De L'Observation
Si, à un certain point dans le calcul, vous pouvez dire que la valeur qui sera retournée sera petite (valeur exacte négociable -- dire, en dessous d'un million de) , vous pouvez abandonner tôt. Je ne suis intéressé dans de grandes valeurs.
C'est de cette façon j'ai l'espoir de sauver le plus de temps, plutôt que par d'autres micro-optimisations (si ce sont bien sûr les bienvenus aussi!).
Information Sur Le Rendement
- smallprimes est un tableau de bits (64 bits); la moyenne est d'environ 8 bits sera réglé, mais il pourrait être aussi peu que 0 ou 12.
- q sera généralement différente de zéro. (Notez que la fonction se termine tôt si q et smallprimes sont à zéro.)
- r et s sera souvent 0. Si q est égal à zéro, r et s sera trop; si r est égal à zéro, s sera trop.
- Comme le commentaire dit à la fin, nu est généralement de 1 à la fin, j'ai donc efficace d'un cas particulier pour elle.
- Les calculs ci-dessous le cas particulier peut apparaître pour des risques de débordement, mais par le biais de modélisation appropriées j'ai prouvé que, pour mon entrée, cela ne se fera pas -- ne vous inquiétez pas au sujet de cette affaire.
- Fonctions qui ne sont pas définis ici (ugcd, minuu, étoiles, etc.) ont déjà été optimisés; aucun temps pour s'exécuter. pr est un petit tableau (tous en L1). Aussi, toutes les fonctions appelées ici sont des fonctions pures.
- Mais si vous vous souciez vraiment... ugcd est le pgcd, minuu est le minimum, vals-les-bains est le nombre de fuite binaire 0, __builtin_ffs est l'emplacement le plus à gauche binaire 1, la star est de (n-1) >> vals(n-1), pr est un tableau de nombres premiers de 2 à 313.
- Les calculs sont en cours sur un Phenom II x4 920, même si des optimisations pour le i7 ou Woodcrest sont encore de l'intérêt (si je reçois de temps de calcul sur les autres nœuds).
- Je serais heureux de répondre à toutes les questions que vous avez au sujet de la fonction ou de ses mandants.
Ce qu'il fait réellement
Ajouté en réponse à une demande. Vous n'avez pas besoin de lire cette partie.
L'entrée est un nombre impair n, avec 1 < n < 4282250400097. Les autres entrées de fournir la factorisation du nombre dans ce sens:
smallprimes&1 est défini si le nombre est divisible par 3, smallprimes&2 est défini si le nombre est divisible par 5, smallprimes&4 est défini si le nombre est divisible par 7, smallprimes&8 est défini si le nombre est divisible par 11, etc. jusqu'à le bit le plus significatif qui représente 313. Un nombre divisible par le carré d'un nombre premier n'est pas représenté de manière différente d'un nombre divisible par juste ce nombre. (En fait, les multiples de places peut être mis au rebut; dans l'étape de prétraitement dans une autre fonction des multiples de carrés de nombres premiers <= lim ont smallprimes et q définie sur 0, de sorte qu'ils seront supprimées, où la valeur optimale de la mfr est déterminé par l'expérience.)
q, r et s représentent les plus grands facteurs de la le nombre. Tout en restant le facteur (qui peut être supérieure à la racine carrée du nombre, ou si s est non nulle peut-être même moins) peuvent être obtenus en divisant les facteurs de n.
Une fois que tous les facteurs sont récupérés de cette façon, le nombre de bases, 1 <= b < n, dans laquelle n est un fort pseudoprime sont comptées à l'aide d'une formule mathématique mieux expliqué par le code.
Des améliorations jusqu'à présent
- Poussé le début de la sortie de test. De toute évidence, cela permet d'économiser du travail, donc j'ai fait le changement.
- Les fonctions appropriées sont déjà en ligne, alors
__attribute__ ((inline))
ne fait rien. Curieusement, le marquage de la fonction principalebases
et de certaines aides avec__attribute ((hot))
nuire à la performance de près de 2% et je ne peux pas comprendre pourquoi (mais c'est reproductible avec plus de 20 tests). Donc je n'ai pas d'effectuer le changement. De même,__attribute__ ((const))
, au mieux, n'a pas aidé. J'ai été plus que légèrement surpris par cette.
Code
ulong bases(ulong smallprimes, ulong n, ulong q, ulong r, ulong s)
{
if (!smallprimes & !q)
return 0;
ulong f = __builtin_popcountll(smallprimes) + (q > 1) + (r > 1) + (s > 1);
ulong nu = 0xFFFF; // "Infinity" for the purpose of minimum
ulong nn = star(n);
ulong prod = 1;
while (smallprimes) {
ulong bit = smallprimes & (-smallprimes);
ulong p = pr[__builtin_ffsll(bit)];
nu = minuu(nu, vals(p - 1));
prod *= ugcd(nn, star(p));
n /= p;
while (n % p == 0)
n /= p;
smallprimes ^= bit;
}
if (q) {
nu = minuu(nu, vals(q - 1));
prod *= ugcd(nn, star(q));
n /= q;
while (n % q == 0)
n /= q;
} else {
goto BASES_END;
}
if (r) {
nu = minuu(nu, vals(r - 1));
prod *= ugcd(nn, star(r));
n /= r;
while (n % r == 0)
n /= r;
} else {
goto BASES_END;
}
if (s) {
nu = minuu(nu, vals(s - 1));
prod *= ugcd(nn, star(s));
n /= s;
while (n % s == 0)
n /= s;
}
BASES_END:
if (n > 1) {
nu = minuu(nu, vals(n - 1));
prod *= ugcd(nn, star(n));
f++;
}
// This happens ~88% of the time in my tests, so special-case it.
if (nu == 1)
return prod << 1;
ulong tmp = f * nu;
long fac = 1 << tmp;
fac = (fac - 1) / ((1 << f) - 1) + 1;
return fac * prod;
}