qu'on pourrait peut être définir une constante "un" plutôt que de la recréer à chaque fois (y en a d'autres un peu partout)
que l'opération -= fait un appel à - qui alloue un nouveau nombre pour le résultat de la soustraction
qui écrase ensuite *this (ce qui entraîne une libération)
et qu'on pourrait peut être aller taper directement dans la représentation interne de Bigint pour retrancher 1 à *this, en court-circuitant ces abstractions fort esthétiques, mais coûteuses.
- Edité par michelbillaud 21 janvier 2025 à 21:21:08
Effectivement, les allocations/désallocations sont chronophages.
Pire elles ruinent le multithreading car une allocation, c'est un entonnoir : le système n'alloue que pour un à la fois.
Pour gagner ne vitesse, on pourrait envisager un cache (une mémoire déjà allouée de BigInt par exemple) qui ne seraient pas détruits et récréés mais simplement réutilisés
Peut être aussi que des statistiques d'utilisation montreraient que la plupart des BigInt ne sont pas si grands que ça, et qu'on y gagnerait largement à utiliser une représentation hybride :
un grand entier natif en dessous d'une certaine valeur absolue (sans allocation)
Un tableau dynamique pour les nombres qui rentrent pas
Ça va compliquer, c'est sur. No pain, no gain.
- Edité par michelbillaud 22 janvier 2025 à 9:21:47
Alors BigInt n'est pas mon code (celui que j'ai profilé, qui est sur le lien GitHub).
@lmghs Je ne sais pas de quoi il retourne. Aurais-tu un exemple ?
C'est ce qui est mis en place dans toutes les bibliothèques de calcul matriciel destinées exclusivement au C++ qui sont employées "professionnellement".
L'idée est que operator+(A, A) ne retourne pas un A, mais une expression conduisant au final à la définition d'un arbre à évaluer. Et c'est au moment de la construction ou de l'affectation <edit>d'un A à partir</edit> du dit arbre que l'évaluation est réalisée. On peut esquiver beaucoup d'allocations avec ça.
Ca veut dire qu'on retarderai le calcul que a+ b+c+d au dernier moment, et que la somme sur chacun des "chiffres" en base 2^64 ne se fait plus juste sur 2 opérandes mais sur n.
Après, je ne sais pas à quel point il serait simple de définir les multiplications avec ça. Il va falloir creuser. Et probablement s'inspirer de ce qui est fait dans eigen/blaze/armadillo/...
Sauf que j'ai une erreur de compilation que je ne comprends pas :
In file included from ..\..\testinteger.cpp:32:
D:/tmp/MillsPrimes/Integer.h: In instantiation of 'class IntegerExpression<Integer<unsigned char> >':
D:/tmp/MillsPrimes/Integer.h:592:7: required from 'class Integer<unsigned char>'
D:/tmp/MillsPrimes/Integer.h:2778:33: required from here
D:/tmp/MillsPrimes/Integer.h:57:56: error: invalid use of incomplete type 'class Integer<unsigned char>'
57 | CONSTEXPR std::vector<typename E::Type> const& bits() const
| ^~~~
D:/tmp/MillsPrimes/Integer.h:592:7: note: declaration of 'class Integer<unsigned char>'
592 | class Integer<T, typename std::enable_if<std::is_unsigned<T>::value>::type> : public IntegerExpression<Integer<T> >
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Dans ma classe Integer, il y a plus loin (je ne sais pas si ça a un lien) :
J'ai supprimé les opérateurs directs d'addition et autres entre les classes Integer mais j'obtiens les erreurs suivantes (je ne vois pas ce qui manque pour que la surchage avec IntegerExpression soit bien prise en compte).
..\..\testinteger.cpp: In member function 'void TestInteger::testAddition()':
..\..\testinteger.cpp:51:26: error: ambiguous overload for 'operator+' (operand types are 'Integerll' {aka 'Integer<long long unsigned int>'} and 'Integerll' {aka 'Integer<long long unsigned int>'})
51 | auto const n3(n1 + n2);
| ~~ ^ ~~
| | |
| | Integer<[...]>
| Integer<[...]>
..\..\testinteger.cpp:51:26: note: candidate: 'operator+(long long unsigned int, long long unsigned int)' (built-in)
51 | auto const n3(n1 + n2);
| ~~~^~~~
..\..\testinteger.cpp:51:26: note: candidate: 'operator+(long long unsigned int, long long int)' (built-in)
..\..\testinteger.cpp:51:26: note: candidate: 'operator+(long long unsigned int, long unsigned int)' (built-in)
..\..\testinteger.cpp:51:26: note: candidate: 'operator+(long long unsigned int, long int)' (built-in)
..\..\testinteger.cpp:51:26: note: candidate: 'operator+(long long unsigned int, unsigned int)' (built-in)
..\..\testinteger.cpp:51:26: note: candidate: 'operator+(long long unsigned int, int)' (built-in)
..\..\testinteger.cpp:51:26: note: candidate: 'operator+(long long int, long long unsigned int)' (built-in)
..\..\testinteger.cpp:51:26: note: candidate: 'operator+(long long int, long long int)' (built-in)
..\..\testinteger.cpp:51:26: note: candidate: 'operator+(long long int, long unsigned int)' (built-in)
..\..\testinteger.cpp:51:26: note: candidate: 'operator+(long long int, long int)' (built-in)
..\..\testinteger.cpp:51:26: note: candidate: 'operator+(long long int, unsigned int)' (built-in)
..\..\testinteger.cpp:51:26: note: candidate: 'operator+(long long int, int)' (built-in)
..\..\testinteger.cpp:51:26: note: candidate: 'operator+(long unsigned int, long long unsigned int)' (built-in)
..\..\testinteger.cpp:51:26: note: candidate: 'operator+(long unsigned int, long long int)' (built-in)
..\..\testinteger.cpp:51:26: note: candidate: 'operator+(long unsigned int, long unsigned int)' (built-in)
..\..\testinteger.cpp:51:26: note: candidate: 'operator+(long unsigned int, long int)' (built-in)
..\..\testinteger.cpp:51:26: note: candidate: 'operator+(long unsigned int, unsigned int)' (built-in)
..\..\testinteger.cpp:51:26: note: candidate: 'operator+(long unsigned int, int)' (built-in)
..\..\testinteger.cpp:51:26: note: candidate: 'operator+(long int, long long unsigned int)' (built-in)
..\..\testinteger.cpp:51:26: note: candidate: 'operator+(long int, long long int)' (built-in)
..\..\testinteger.cpp:51:26: note: candidate: 'operator+(long int, long unsigned int)' (built-in)
..\..\testinteger.cpp:51:26: note: candidate: 'operator+(long int, long int)' (built-in)
..\..\testinteger.cpp:51:26: note: candidate: 'operator+(long int, unsigned int)' (built-in)
..\..\testinteger.cpp:51:26: note: candidate: 'operator+(long int, int)' (built-in)
..\..\testinteger.cpp:51:26: note: candidate: 'operator+(unsigned int, long long unsigned int)' (built-in)
..\..\testinteger.cpp:51:26: note: candidate: 'operator+(unsigned int, long long int)' (built-in)
..\..\testinteger.cpp:51:26: note: candidate: 'operator+(unsigned int, long unsigned int)' (built-in)
..\..\testinteger.cpp:51:26: note: candidate: 'operator+(unsigned int, long int)' (built-in)
..\..\testinteger.cpp:51:26: note: candidate: 'operator+(unsigned int, unsigned int)' (built-in)
..\..\testinteger.cpp:51:26: note: candidate: 'operator+(unsigned int, int)' (built-in)
..\..\testinteger.cpp:51:26: note: candidate: 'operator+(int, long long unsigned int)' (built-in)
..\..\testinteger.cpp:51:26: note: candidate: 'operator+(int, long long int)' (built-in)
..\..\testinteger.cpp:51:26: note: candidate: 'operator+(int, long unsigned int)' (built-in)
..\..\testinteger.cpp:51:26: note: candidate: 'operator+(int, long int)' (built-in)
..\..\testinteger.cpp:51:26: note: candidate: 'operator+(int, unsigned int)' (built-in)
..\..\testinteger.cpp:51:26: note: candidate: 'operator+(int, int)' (built-in)
In file included from ..\..\testinteger.cpp:32:
D:/tmp/MillsPrimes/Integer.h:125:20: note: candidate: 'IntegerAdd<E1, E2> operator+(const IntegerExpression<E1>&, const IntegerExpression<E2>&) [with E1 = Integer<long long unsigned int>; E2 = Integer<long long unsigned int>]'
125 | IntegerAdd<E1, E2> operator+(IntegerExpression<E1> const& u, IntegerExpression<E2> const& v)
| ^~~~~~~~
In file included from ..\..\testinteger.cpp:32:
D:/tmp/MillsPrimes/Integer.h:2935:22: note: candidate: 'constexpr Integer<T> operator+(Integer<T>, const S&) [with T = long long unsigned int; S = Integer<long long unsigned int>]'
2935 | CONSTEXPR Integer<T> operator+(Integer<T> lhs, S const& rhs)
| ^~~~~~~~
D:/tmp/MillsPrimes/Integer.h:2941:22: note: candidate: 'constexpr Integer<T> operator+(const S&, Integer<T>) [with T = long long unsigned int; S = Integer<long long unsigned int>]'
2941 | CONSTEXPR Integer<T> operator+(S const& lhs, Integer<T> rhs)
| ^~~~~~~~
EDIT3 : Bon, j'ai réussi à avancer un peu mais je croule sous les erreurs, pas simple .
EDIT4 : Mine de rien, c'est assez velu à faire cette histoire . J'ai une erreur de compilation que j'ai du mal à lever sur la ligne suivante.
auto const mid((L + R) >> 1);
L'erreur est :
In file included from ..\..\testinteger.cpp:32:
D:/tmp/MillsPrimes/Integer.h: In instantiation of 'constexpr decltype(auto) operator>>(const IntegerExpression<E1>&, const S&) [with T = IntegerAdd<Integer<long long unsigned int>, Integer<long long unsigned int> >; S = int; std::enable_if_t<(! is_base_of_v<S, T>)>* <anonymous> = 0]':
D:/tmp/MillsPrimes/Integer.h:4127:32: required from 'constexpr std::pair<Integer<T>, Integer<T> > computeQrBurnikelZiegler(const Integer<T>&, const Integer<T>&) [with T = long long unsigned int]'
..\..\testinteger.cpp:160:53: required from here
D:/tmp/MillsPrimes/Integer.h:3207:19: error: no matching function for call to 'IntegerAdd<Integer<long long unsigned int>, Integer<long long unsigned int> >::IntegerAdd(const int&)'
3207 | return lhs >> T(rhs);
Comment dois-je faire pour récupérer le bon type Integer<U> à la place de T quand j'ai une expression template (je ne sais pas si c'est très clair comme ça) ? J'ai essayé de mettre dans IntegerExpression :
mais je me heurte au fait que la classe Integer n'est pas encore complètement définie à ce moment... J'ai besoin d'une astuce.
EDIT5 : J'ai réussi à m'en sortir mais j'ai à nouveau une erreur de compilation qui est la suivante :
D:/tmp/MillsPrimes/Integer.h:245:26: error: use of 'constexpr decltype(auto) IntegerMul<E1, E2>::bits() const [with E1 = Integer<long long unsigned int>; E2 = Integer<long long unsigned int>]' before deduction of 'auto'
245 | return w.bits();
| ~~~~~~^~
sur le code suivant :
CONSTEXPR decltype(auto) bits() const
{
auto w(u_);
w += v_;
return w.bits();
}
Une idée ?
EDIT6 : Ben y'a pas à dire, mais c'est chaud patate mine de rien cette affaire d'expression template... Il faut que je m'assure que tous les types Integer<> soient tous de même type pour faire les calculs sur les mêmes template ou bien convertir tout ça en char et faire les calculs dessus. Du coup, je vais peut-être abandonner l'idée que Integer soit une classe template.
- Edité par P'tit Ju 25 janvier 2025 à 19:23:44
Le premier et meilleur outil de l'Homme reste encore et toujours son cerveau.
Pour information, mon code pour tester qu'un nombre de 1024 bits est premier dure environ 5 secondes.
Ha oui mais le problème c'est quand il faut générer un nombre premier aléatoirement. Il y a pas mal de nombres à tester avant de tomber sur le bon et ça me prend trop de temps pour ça que j'utilise une lib qui le fait à ma place, j'avais essayé de le faire moi même pour le fun mais ce n'est pas aussi performant.
Je reviens sur l'histoire d'utiliser des expressions template (le code sur la branche expression-template du GitHub est à jour) où je continue à galérer. J'ai l'erreur de compilation suivante et je ne comprends pas le problème. Pour information, j'ai limité la taille de profondeur des templates à 25 (-ftemplate-depth=25), j'ai le même problème avec 900 (valeur par défaut) mais ça met plus de temps et c'est encore moins compréhensible. Pourriez-vous m'aider s'il vous plaît ?
In file included from D:/Programmes/Qt/Tools/mingw1120_64/lib/gcc/x86_64-w64-mingw32/11.2.0/include/c++/bits/move.h:57,
from D:/Programmes/Qt/Tools/mingw1120_64/lib/gcc/x86_64-w64-mingw32/11.2.0/include/c++/bits/atomic_base.h:38,
from D:/Programmes/Qt/Tools/mingw1120_64/lib/gcc/x86_64-w64-mingw32/11.2.0/include/c++/atomic:41,
from ..\..\Integer.cpp:1:
D:/Programmes/Qt/Tools/mingw1120_64/lib/gcc/x86_64-w64-mingw32/11.2.0/include/c++/type_traits: In instantiation of 'struct std::__and_<std::is_array<IntegerRShift<IntegerRShift<IntegerRShift<IntegerRShift<IntegerRShift<IntegerRShift<IntegerMod<Integer, Integer>, Integer>, Integer>, Integer>, Integer>, Integer>, Integer> >, std::__not_<std::extent<IntegerRShift<IntegerRShift<IntegerRShift<IntegerRShift<IntegerRShift<IntegerRShift<IntegerMod<Integer, Integer>, Integer>, Integer>, Integer>, Integer>, Integer>, Integer>, 0> > >':
D:/Programmes/Qt/Tools/mingw1120_64/lib/gcc/x86_64-w64-mingw32/11.2.0/include/c++/type_traits:798:12: required from 'struct std::__is_array_unknown_bounds<IntegerRShift<IntegerRShift<IntegerRShift<IntegerRShift<IntegerRShift<IntegerRShift<IntegerMod<Integer, Integer>, Integer>, Integer>, Integer>, Integer>, Integer>, Integer> >'
D:/Programmes/Qt/Tools/mingw1120_64/lib/gcc/x86_64-w64-mingw32/11.2.0/include/c++/type_traits:115:12: required from 'struct std::__or_<std::is_void<IntegerRShift<IntegerRShift<IntegerRShift<IntegerRShift<IntegerRShift<IntegerRShift<IntegerMod<Integer, Integer>, Integer>, Integer>, Integer>, Integer>, Integer>, Integer> >, std::__is_array_unknown_bounds<IntegerRShift<IntegerRShift<IntegerRShift<IntegerRShift<IntegerRShift<IntegerRShift<IntegerMod<Integer, Integer>, Integer>, Integer>, Integer>, Integer>, Integer>, Integer> > >'
D:/Programmes/Qt/Tools/mingw1120_64/lib/gcc/x86_64-w64-mingw32/11.2.0/include/c++/type_traits:120:12: required from 'struct std::__or_<std::is_reference<IntegerRShift<IntegerRShift<IntegerRShift<IntegerRShift<IntegerRShift<IntegerRShift<IntegerMod<Integer, Integer>, Integer>, Integer>, Integer>, Integer>, Integer>, Integer> >, std::is_function<IntegerRShift<IntegerRShift<IntegerRShift<IntegerRShift<IntegerRShift<IntegerRShift<IntegerMod<Integer, Integer>, Integer>, Integer>, Integer>, Integer>, Integer>, Integer> >, std::is_void<IntegerRShift<IntegerRShift<IntegerRShift<IntegerRShift<IntegerRShift<IntegerRShift<IntegerMod<Integer, Integer>, Integer>, Integer>, Integer>, Integer>, Integer>, Integer> >, std::__is_array_unknown_bounds<IntegerRShift<IntegerRShift<IntegerRShift<IntegerRShift<IntegerRShift<IntegerRShift<IntegerMod<Integer, Integer>, Integer>, Integer>, Integer>, Integer>, Integer>, Integer> > >'
D:/Programmes/Qt/Tools/mingw1120_64/lib/gcc/x86_64-w64-mingw32/11.2.0/include/c++/type_traits:211:13: required by substitution of 'template<class _TypeIdentity, class _NestedType> constexpr typename std::__or_<std::is_reference<_NestedType>, std::is_function<_NestedType>, std::is_void<_NestedType>, std::__is_array_unknown_bounds<_NestedType> >::type std::__is_complete_or_unbounded(_TypeIdentity) [with _TypeIdentity = std::__type_identity<IntegerRShift<IntegerRShift<IntegerRShift<IntegerRShift<IntegerRShift<IntegerRShift<IntegerMod<Integer, Integer>, Integer>, Integer>, Integer>, Integer>, Integer>, Integer> >; _NestedType = IntegerRShift<IntegerRShift<IntegerRShift<IntegerRShift<IntegerRShift<IntegerRShift<IntegerMod<Integer, Integer>, Integer>, Integer>, Integer>, Integer>, Integer>, Integer>]'
D:/Programmes/Qt/Tools/mingw1120_64/lib/gcc/x86_64-w64-mingw32/11.2.0/include/c++/type_traits:691:52: [ skipping 9 instantiation contexts, use -ftemplate-backtrace-limit=0 to disable ]
..\..\Integer.h:734:81: required from 'constexpr std::vector<long long unsigned int> IntegerAnd<E1, E2>::bits() const [with E1 = IntegerMul<IntegerMod<Integer, Integer>, Integer>; E2 = Integer]'
..\..\Integer.h:1608:45: required from 'constexpr std::vector<long long unsigned int> IntegerMod<E1, E2>::bits() const [with E1 = IntegerMul<IntegerMod<Integer, Integer>, Integer>; E2 = Integer]'
..\..\Integer.h:54:53: required from 'constexpr std::vector<long long unsigned int> IntegerExpression<E>::bits() const [with E = IntegerMod<IntegerMul<IntegerMod<Integer, Integer>, Integer>, Integer>]'
..\..\Integer.h:74:13: required from 'constexpr bool IntegerExpression<E>::operator!() const [with E = IntegerMod<IntegerMul<IntegerMod<Integer, Integer>, Integer>, Integer>]'
..\..\Integer.h:69:21: required from 'constexpr IntegerExpression<E>::operator bool() const [with E = IntegerMod<IntegerMul<IntegerMod<Integer, Integer>, Integer>, Integer>]'
..\..\Integer.h:1487:52: required from 'constexpr bool IntegerMul<E1, E2>::isInfinity() const [with E1 = IntegerMod<IntegerMul<IntegerMod<Integer, Integer>, Integer>, Integer>; E2 = Integer]'
..\..\Integer.h:1065:74: required from 'constexpr bool IntegerAdd<E1, E2>::isNan() const [with E1 = Integer; E2 = IntegerMul<IntegerMod<IntegerMul<IntegerMod<Integer, Integer>, Integer>, Integer>, Integer>]'
..\..\Integer.h:1556:25: required from 'constexpr bool IntegerDiv<E1, E2>::isNan() const [with E1 = IntegerAdd<Integer, IntegerMul<IntegerMod<IntegerMul<IntegerMod<Integer, Integer>, Integer>, Integer>, Integer> >; E2 = Integer]'
..\..\Integer.h:59:54: required from 'constexpr bool IntegerExpression<E>::isNan() const [with E = IntegerDiv<IntegerAdd<Integer, IntegerMul<IntegerMod<IntegerMul<IntegerMod<Integer, Integer>, Integer>, Integer>, Integer> >, Integer>]'
..\..\Integer.h:161:27: required from 'constexpr bool IntegerExpression<E>::operator<(const IntegerExpression<T>&) const [with T = Integer; E = IntegerDiv<IntegerAdd<Integer, IntegerMul<IntegerMod<IntegerMul<IntegerMod<Integer, Integer>, Integer>, Integer>, Integer> >, Integer>]'
..\..\Integer.cpp:736:19: required from here
D:/Programmes/Qt/Tools/mingw1120_64/lib/gcc/x86_64-w64-mingw32/11.2.0/include/c++/type_traits:138:12: fatal error: template instantiation depth exceeds maximum of 25 (use '-ftemplate-depth=' to increase the maximum)
138 | struct __and_<_B1, _B2>
| ^~~~~~~~~~~~~~~~
compilation terminated.
mingw32-make[1]: *** [Makefile.Release:683: release/Integer.o] Error 1
mingw32-make[1]: *** Waiting for unfinished jobs....
In file included from D:/Programmes/Qt/6.7.0/mingw_64/include/QtCore/qglobal.h:13,
from D:/Programmes/Qt/6.7.0/mingw_64/include/QtTest/qttestglobal.h:7,
from D:/Programmes/Qt/6.7.0/mingw_64/include/QtTest/qtest.h:12,
from D:/Programmes/Qt/6.7.0/mingw_64/include/QtTest/QTest:1,
from ..\..\testinteger.cpp:1:
D:/Programmes/Qt/Tools/mingw1120_64/lib/gcc/x86_64-w64-mingw32/11.2.0/include/c++/type_traits: In instantiation of 'struct std::__and_<std::is_array<IntegerRShift<IntegerRShift<IntegerRShift<IntegerRShift<IntegerRShift<IntegerRShift<IntegerMod<Integer, Integer>, Integer>, Integer>, Integer>, Integer>, Integer>, Integer> >, std::__not_<std::extent<IntegerRShift<IntegerRShift<IntegerRShift<IntegerRShift<IntegerRShift<IntegerRShift<IntegerMod<Integer, Integer>, Integer>, Integer>, Integer>, Integer>, Integer>, Integer>, 0> > >':
D:/Programmes/Qt/Tools/mingw1120_64/lib/gcc/x86_64-w64-mingw32/11.2.0/include/c++/type_traits:798:12: required from 'struct std::__is_array_unknown_bounds<IntegerRShift<IntegerRShift<IntegerRShift<IntegerRShift<IntegerRShift<IntegerRShift<IntegerMod<Integer, Integer>, Integer>, Integer>, Integer>, Integer>, Integer>, Integer> >'
D:/Programmes/Qt/Tools/mingw1120_64/lib/gcc/x86_64-w64-mingw32/11.2.0/include/c++/type_traits:115:12: required from 'struct std::__or_<std::is_void<IntegerRShift<IntegerRShift<IntegerRShift<IntegerRShift<IntegerRShift<IntegerRShift<IntegerMod<Integer, Integer>, Integer>, Integer>, Integer>, Integer>, Integer>, Integer> >, std::__is_array_unknown_bounds<IntegerRShift<IntegerRShift<IntegerRShift<IntegerRShift<IntegerRShift<IntegerRShift<IntegerMod<Integer, Integer>, Integer>, Integer>, Integer>, Integer>, Integer>, Integer> > >'
D:/Programmes/Qt/Tools/mingw1120_64/lib/gcc/x86_64-w64-mingw32/11.2.0/include/c++/type_traits:120:12: required from 'struct std::__or_<std::is_reference<IntegerRShift<IntegerRShift<IntegerRShift<IntegerRShift<IntegerRShift<IntegerRShift<IntegerMod<Integer, Integer>, Integer>, Integer>, Integer>, Integer>, Integer>, Integer> >, std::is_function<IntegerRShift<IntegerRShift<IntegerRShift<IntegerRShift<IntegerRShift<IntegerRShift<IntegerMod<Integer, Integer>, Integer>, Integer>, Integer>, Integer>, Integer>, Integer> >, std::is_void<IntegerRShift<IntegerRShift<IntegerRShift<IntegerRShift<IntegerRShift<IntegerRShift<IntegerMod<Integer, Integer>, Integer>, Integer>, Integer>, Integer>, Integer>, Integer> >, std::__is_array_unknown_bounds<IntegerRShift<IntegerRShift<IntegerRShift<IntegerRShift<IntegerRShift<IntegerRShift<IntegerMod<Integer, Integer>, Integer>, Integer>, Integer>, Integer>, Integer>, Integer> > >'
D:/Programmes/Qt/Tools/mingw1120_64/lib/gcc/x86_64-w64-mingw32/11.2.0/include/c++/type_traits:211:13: required by substitution of 'template<class _TypeIdentity, class _NestedType> constexpr typename std::__or_<std::is_reference<_NestedType>, std::is_function<_NestedType>, std::is_void<_NestedType>, std::__is_array_unknown_bounds<_NestedType> >::type std::__is_complete_or_unbounded(_TypeIdentity) [with _TypeIdentity = std::__type_identity<IntegerRShift<IntegerRShift<IntegerRShift<IntegerRShift<IntegerRShift<IntegerRShift<IntegerMod<Integer, Integer>, Integer>, Integer>, Integer>, Integer>, Integer>, Integer> >; _NestedType = IntegerRShift<IntegerRShift<IntegerRShift<IntegerRShift<IntegerRShift<IntegerRShift<IntegerMod<Integer, Integer>, Integer>, Integer>, Integer>, Integer>, Integer>, Integer>]'
D:/Programmes/Qt/Tools/mingw1120_64/lib/gcc/x86_64-w64-mingw32/11.2.0/include/c++/type_traits:691:52: [ skipping 9 instantiation contexts, use -ftemplate-backtrace-limit=0 to disable ]
..\..\Integer.h:734:81: required from 'constexpr std::vector<long long unsigned int> IntegerAnd<E1, E2>::bits() const [with E1 = IntegerMul<IntegerMod<Integer, Integer>, Integer>; E2 = Integer]'
..\..\Integer.h:1608:45: required from 'constexpr std::vector<long long unsigned int> IntegerMod<E1, E2>::bits() const [with E1 = IntegerMul<IntegerMod<Integer, Integer>, Integer>; E2 = Integer]'
..\..\Integer.h:54:53: required from 'constexpr std::vector<long long unsigned int> IntegerExpression<E>::bits() const [with E = IntegerMod<IntegerMul<IntegerMod<Integer, Integer>, Integer>, Integer>]'
..\..\Integer.h:74:13: required from 'constexpr bool IntegerExpression<E>::operator!() const [with E = IntegerMod<IntegerMul<IntegerMod<Integer, Integer>, Integer>, Integer>]'
..\..\Integer.h:69:21: required from 'constexpr IntegerExpression<E>::operator bool() const [with E = IntegerMod<IntegerMul<IntegerMod<Integer, Integer>, Integer>, Integer>]'
..\..\Integer.h:1487:52: required from 'constexpr bool IntegerMul<E1, E2>::isInfinity() const [with E1 = IntegerMod<IntegerMul<IntegerMod<Integer, Integer>, Integer>, Integer>; E2 = Integer]'
..\..\Integer.h:1065:74: required from 'constexpr bool IntegerAdd<E1, E2>::isNan() const [with E1 = Integer; E2 = IntegerMul<IntegerMod<IntegerMul<IntegerMod<Integer, Integer>, Integer>, Integer>, Integer>]'
..\..\Integer.h:1556:25: required from 'constexpr bool IntegerDiv<E1, E2>::isNan() const [with E1 = IntegerAdd<Integer, IntegerMul<IntegerMod<IntegerMul<IntegerMod<Integer, Integer>, Integer>, Integer>, Integer> >; E2 = Integer]'
..\..\Integer.h:59:54: required from 'constexpr bool IntegerExpression<E>::isNan() const [with E = IntegerDiv<IntegerAdd<Integer, IntegerMul<IntegerMod<IntegerMul<IntegerMod<Integer, Integer>, Integer>, Integer>, Integer> >, Integer>]'
..\..\Integer.h:161:27: required from 'constexpr bool IntegerExpression<E>::operator<(const IntegerExpression<T>&) const [with T = Integer; E = IntegerDiv<IntegerAdd<Integer, IntegerMul<IntegerMod<IntegerMul<IntegerMod<Integer, Integer>, Integer>, Integer>, Integer> >, Integer>]'
..\..\testinteger.cpp:276:19: required from here
D:/Programmes/Qt/Tools/mingw1120_64/lib/gcc/x86_64-w64-mingw32/11.2.0/include/c++/type_traits:138:12: fatal error: template instantiation depth exceeds maximum of 25 (use '-ftemplate-depth=' to increase the maximum)
138 | struct __and_<_B1, _B2>
| ^~~~~~~~~~~~~~~~
Le premier et meilleur outil de l'Homme reste encore et toujours son cerveau.
Salut! Le message d'erreur n'est pas complet il te dit que l'erreur vient lors de l'instanciation d'un paramètre template dans std::__and_ et qu'il manque quelque chose qui est requis à la ligne 734 et la ligne 734 dans ton fichier c'est cette ligne-ci : (Pourtant je ne vois pas d'utilisation de template à cette ligne.)
threads.emplace_back(threadFunc, start, end);
Es tu sûr d'avoir bien spécifié les paramètres template qui doivent être spécifié explicitement comme ceci : (Je ne suis plus très sûr de la syntaxe mais normalement ça doit se faire comme ceci)
Pas besoin de faire < + ==. Aussi operator==(other) <=> *this == other
Sinon ton erreur vient d'appel récursif, par exemple IntegerMul qui fait ((u_ << shift) * r).bits(); et donc une nouvelle construction de IntegerMul de manière récursive. Ce n'est probablement pas le seul endroit.
Mais dans le cas général il y a une mauvaise compréhension des expressions templates. Le but est de réduire les constructions intermédiaires en réutilisant les buffers. Là, tout ce que tu fais est dévaluer plus tard l'expression et de les évaluer plusieurs fois ! Par exemple isPositive() qui peut faire appel à < qui utilise 6 fois la fonction bits() qui elle-même fait plein d'opération lourde.
Un exemple de réduction est le remplacement les constructions de la forme a + b + c + d qui vont faire 3 boucles et 2 buffers intermédiaires:
Par quelque chose qui fait une seule boucle et sans buffer intermédiaire.
result = loop(a[i] + b[i] + c[i] + d[i])
Et dans les cas complexe réutiliser le buffer intermédiaire pour réduire les allocations.
Au passage, le code est invalide, les itérateurs de début et fin ne sont pas le même vector et il y a plusieurs vector intermédiaires totalement inutiles:
foo(bits().begin(), bits().end()) -> 2 itérateurs qui n'ont rien à voir entre eux puisque pointant sur 2 std::vector différents.
- Edité par jo_link_noir 27 janvier 2025 à 23:27:32
Comme je n'ai pas d'utilisation de l'opérateur[] dans ma classe, je comprends que l'usage des expressions template n'est peut-être pas adapté. Dans le cas contraire, j'ai du mal à voir comment modifier mon code pour arriver au comportement voulu...
Le premier et meilleur outil de l'Homme reste encore et toujours son cerveau.
Lorsque tu vas faire `BigInt nouvelleinstance = expression`, c'est là qu'est l'opérateur []. Invisible. Ce n'est pas un opérateur externe et visible. C'est un opérateur dont l'appel est interne. Celui où tu boucles sur les digits 64bits qui constituent tes bigints
Heu non, avec un Integer.h de presque 2000 lignes, c'est pas minimaliste... Faut lire tout ça pour comprendre ce qui est écrit, deviner quelle était l'intention, et trouver pourquoi elle n'est pas atteinte, du point de vue du compilateur.
Ceci dit, les messages d'erreurs provoqués par les templates de C++, honnêtement j'ai pas envie de m'y mettre et ça me fait fuir C++. La demande d'un cas d'erreur minimal, c'était pour simplifier la vie de ceux qui ont le courage de s'y coller.
- Edité par michelbillaud 28 janvier 2025 à 11:11:22
@lmghs D'accord mais du coup je ne vois pas concrètement ce que je dois faire pour ma classe Integer (version template ou non), avec un exemple sur un opérateur par exemple (le plus simple pour la compréhension of course, peut-être &).
EDIT : Dans mes recherches, j'ai vu qu'il y avait boost::proto mais je ne sais pas si je peux en avoir usage dans mon cas et si oui, comment. J'ai essayé d'ajouter ça à la suite de ma classe Integer (depuis le code de la branche main) :
Et les opérateurs semblent bien définis mais pas certains usages, comme par exemple pour l'opérateur ! à convertir en bool :
In file included from ..\..\..\expression-template2\testinteger.cpp:32:
..\..\..\expression-template2\Integer.h: In instantiation of 'constexpr std::string Integer<T, typename std::enable_if<std::is_unsigned<_Tp>::value>::type>::toString(size_t) const [with T = unsigned char; typename std::enable_if<std::is_unsigned<_Tp>::value>::type = void; std::string = std::__cxx11::basic_string<char>; size_t = long long unsigned int]':
..\..\..\expression-template2\Integer.h:2335:20: required from here
..\..\..\expression-template2\Integer.h:1276:25: error: could not convert 'operator!<Integer<unsigned char>&>(number)' from 'const type' {aka 'const boost::proto::exprns_::expr<boost::proto::tagns_::tag::logical_not, boost::proto::argsns_::list1<boost::proto::exprns_::expr<boost::proto::tagns_::tag::terminal, boost::proto::argsns_::term<Integer<unsigned char>&>, 0> >, 1>'} to 'bool'
1276 | if (!number)
| ^~~~~~~
| |
| const type {aka const boost::proto::exprns_::expr<boost::proto::tagns_::tag::logical_not, boost::proto::argsns_::list1<boost::proto::exprns_::expr<boost::proto::tagns_::tag::terminal, boost::proto::argsns_::term<Integer<unsigned char>&>, 0> >, 1>}
Quelques tuyaux à me suggérer ?
EDIT2 : J'ai cherché à faire fonctionner ce bout de code mais sans succès...
#include <iostream>
#include <vector>
#include <boost/proto/proto.hpp>
template<typename T>
struct is_terminal : boost::mpl::false_
{
};
template<>
struct is_terminal<std::vector<int> > : boost::mpl::true_
{
};
BOOST_PROTO_DEFINE_OPERATORS(is_terminal, boost::proto::default_domain)
int main()
{
std::vector<int> const a{0, 1, 2, 3};
std::vector<int> const b{4, 5, 6, 7};
std::vector<int> const c{0, 0, 0, 0};
auto const exprAdd{a + b};
std::vector<int> const d{exprAdd}; //je ne sais pas quoi ajouter pour que ça fonctionne
return 0;
}
- Edité par P'tit Ju 2 février 2025 à 11:53:06
Le premier et meilleur outil de l'Homme reste encore et toujours son cerveau.
@lmghs D'accord mais du coup je ne vois pas concrètement ce que je dois faire pour ma classe Integer (version template ou non), avec un exemple sur un opérateur par exemple (le plus simple pour la compréhension of course, peut-être &).
Salut, Après avoir un peu plus cherché, je ne pense pas que ce que je t'ai proposé puisse marcher. Quand les additions se font membre à membre, OK. Sauf que là, il y a des retenues à propager.
On en revient à des opérations optimisées dans l'idée de ce qui est fait par la lib de GNU. Addition, multiplication, multiplication + addition, etc. On peut même profiter des instrinics de gcc et clang pour additionner des unsigned long long et savoir s'il y a une retenue.
Maintenant, pour la place des ET dans tout ça... ben je dirai comme ce qui est fait avec boost.multiprecision (qui correspond à ce que tu cherches à faire): profiter des ET pour reconnaitre certaines opérations en éliminer un max de temporaires.
Ok, si j'ai bien compris, j'abandonne les expressions template.
Du coup, j'en reviens au profilage. L'implémentation de mon test de primalité est d'abord un test de division (algorithme d'Euclide), puis un test de Miller-Rabin. La fonction mpz_probab_prime_p de GMP a une étape en plus entre les deux qui est un test de Baillie-PSW. J'ai regardé le code C et c'est un peu touffu à comprendre.
Est-ce que vous auriez des idées pour faire aussi bien que GMP (sans prétention :-P) ? Ou du moins des idées pour améliorer ce qui met le plus de temps ?
- Edité par P'tit Ju 4 février 2025 à 13:05:07
Le premier et meilleur outil de l'Homme reste encore et toujours son cerveau.
Il faut minimiser le nombre de Integer construit, car les vector interne finissent par coûter cher à construire/détruire et à copier lorsque les tailles changent.
Par exemple, dans reduction, le bloc:
if (x < n)
return x;
else
return x - n;
Peut être transformé en
if (n < x)
x -= n;
return x;
Pour éviter la création d'un Integer résultant de x-n.
Idem pour x = (x + y) % m; qui devient x += y; x %= m. Etc.
En fait, c'est sur ce genre de chose que les expressions template sont pas mal puisqu'elles peuvent faire ces transformations automatiquement. Mais comme détecter la réutilisation d'un identifiant est délicat, je préfère une approche à base d'expression externe. Un truc genre auto expr = ("x"_c = ("x"_c + "y"_c) % "m"_c); expr.compute("x"_c = x, "y"_c = y, "m"_c = m). Mais c'est beaucoup de boulot pour quelque chose qui ici se fait assez facilement à la main.
Après, il y a la réutilisation pure et simple des temporaires. Si on regarde modulo, il y un Integer x(1); construit en début de fonction, peuplé via différente opération qui vont faire des allocations puis supprimé au prochain tour de boucle du parent. Les allocations faîtes dans x pourraient être évitées en réutilisant la variable pour que les prochains tours de boucle ne le fassent plus. Cela veut dire conserver la variable comme un "buffer" et la fonction pourrait faire un x = 1 plutôt qu'une reconstruction complète (en supposant un operator= qui prend un entier, car il peut y avoir une construction cachée si les constructeurs ne sont pas marqués explicit).
Un framework qui définit des classes mathématiques autour de structures à taille dynamique... il est impossible d'avoir des bonnes perfs dès que l'on va enchainer les opérateurs car cela va déclencher des allocations et libérations de vecteurs à tour de bras.
Les ET permettent d'analyser les expressions et de réduire les besoins en allocs & cie, de façon automagique -- ce que fait boost.multiprecision (je l'ai découvert après). Là où avec GMP je soupçonne qu'on va le faire en réfléchissant, et donc limiter les dégâts. Mais je me trompe peut-être.
Si on regarde le code généré (g++ -O3), on voit que les objets ne sont même pas créés - les fonctions encore moins appelées -, et que la valeur 666 est directement calculée par la compilation
Maintenant, stockes le T dans un vector<uint64_t>, enchaine quelques additions, je doute que les constructeurs disparaissent, même avec autorisation implicite d'inliner ce qui est définit dans la même unité de traduction comme ici.
Recueil de code C et C++ http://fvirtman.free.fr/recueil/index.html