Manipulation d'expression par templates : la vengeance

Manipulation d'expression par templates : la vengeance - C++ - Programmation

Marsh Posté le 04-09-2003 à 11:39:40    

bon, mon générateur de code arithmétique optimisé Altivec par template marche.
Il me reste neanmoins un gros probleme.
 
Pour une expression du style :
 
R = (a+b)/(a*b);
 
mon générateur produit 2 chargements de la variable a et deux de la variable b :
 

Code :
  1. vector float t1,t2,t3,t4;
  2. t1 =  vec_ld(0,a.begin);
  3. t2 =  vec_ld(0,b.begin);
  4. t3 =  vec_ld(0,a.begin);
  5. t4 =  vec_ld(0,b.begin);
  6. //calcul
  7. vec_madd(  vec_add(t1,t2), vec_re(vec_madd(t3,t4,zero)),zero);


 
au lieu de :
 

Code :
  1. vector char t1,t2;
  2. t1 =  vec_ld(0,a.begin);
  3. t2 =  vec_ld(0,b.begin);
  4. //calcul
  5. vec_madd(  vec_add(t1,t2), vec_re(vec_madd(t1,t2,zero)),zero);


 
Moralité : 50% de perf en - que prevu.
 
Question :
En utilisant une technique d'expressions template ou dérivée, comment arrivez a ne generer qu'un chargement par occurence d'une variable dans les expressions ?
 
Plus geenralement comment 'flagé' des objets comme etant utilisé et ceci au moment de la compilation ?
 
Cf mes aures posts sur mes classes d'expressions pour les details. Je posterais un exemple minimaliste de code plus tard.
 
EIT : arf j'ai acheté un nouveau clavier


Message édité par Joel F le 04-09-2003 à 16:58:47
Reply

Marsh Posté le 04-09-2003 à 11:39:40   

Reply

Marsh Posté le 04-09-2003 à 11:53:58    

Je pense pas que tu puisse le faire dans un cadre général, tu va pouvoir optimiser ça pour des opérateurs particuliers ou des suites d'opérateurs particulières (du style a+b*c a+b+c ...)


---------------
Le Tyran
Reply

Marsh Posté le 04-09-2003 à 12:05:12    

c'est peut etre con, mais moi j'aurais fait
 
(x+y)/(u*v) renvoie une Expression+/*. cette expression contient des références
 
après tu as
 
uneFonction(Expression+/* e) // contructeur, conversion, membre
{
  if(&e.x == &e.u and &e.y==&e.v)
  {
    version spécialisée
  }
   else
   {
     version générique
   }
}
 
après je pense pas que tu puisse avoir quelque chose au moment de la compilation

Reply

Marsh Posté le 04-09-2003 à 13:19:04    

ok ... bonc je vais etre obligé de me taper xxx fonction spar cas particuliers.
c'est ce que je commencait a craindre ...

Reply

Marsh Posté le 04-09-2003 à 13:20:52    

Joel F a écrit :

ok ... bonc je vais etre obligé de me taper xxx fonction spar cas particuliers.
c'est ce que je commencait a craindre ...  


 
Ben ouaip, après tu peux peut être te limiter aux cas les plus fréquants, c à toi de voir


---------------
Le Tyran
Reply

Marsh Posté le 04-09-2003 à 13:30:07    

la solution que j'ai actuellement pass pr des variables muettes :
 

Code :
  1. eve::var<1> x;
  2. eve::var<2> y;
  3. r = apply( (x+y)/(x*y),a,b);


 
ca marche mais c moche :/
donc retour a la case parser/lexer avant compilation ...

Reply

Marsh Posté le 04-09-2003 à 13:38:21    

et un truc du genre
(la syntaxe c peut être pas ça, ça fait 3 mois que j'ai pas touché un template, j'ai des absences  [:ddr555] )

Code :
  1. template<boolean test> toto f(const titi &, const titi &,);
  2. template<const titi &a, const titi &b> toto g()
  3. {
  4.    return f<&a == &b>(a,b);
  5. }


 
Tu spécialise f de façon a pas charger 2 fois la même var en mémoire. Ca pourrait pas marcher? J'ai rien pour tester ici et je sais même pas si ça peut s'adapter à ton cas d'ailleur  [:ddr555]


---------------
Le Tyran
Reply

Marsh Posté le 04-09-2003 à 13:48:22    

Une piste. Y a des techniques assez intéressantes à découvrir dans la doc de Spirit. Par exemple sur l'utilisation de foncteurs conjugués avec le parsing: http://www.boost.org/libs/spirit/doc/functional.html


---------------
NOUVEAU! Le guide de l'édition en version ebook : http://marcautret.free.fr/autret/150q-ebook/
Reply

Marsh Posté le 04-09-2003 à 13:49:31    

ACut a écrit :

Une piste. Y a des techniques assez intéressantes à découvrir dans la doc de Spirit. Par exemple sur l'utilisation de foncteurs conjugués avec le parsing: http://www.boost.org/libs/spirit/doc/functional.html

je lui ai déjà conseillé y a longtemps, mais il s'entête

Reply

Marsh Posté le 04-09-2003 à 13:52:45    

Leto >  [:xp1700]

Reply

Marsh Posté le 04-09-2003 à 13:52:45   

Reply

Marsh Posté le 04-09-2003 à 13:57:16    

Joel F > sinon, si tu veux eviter les tests comme ça, tu peux tenter des bidouiller des wrappers pour référence, tu te fais une fonction qui fait fusionner les alias que tu appelles à chaque fois qu'il faut. soit tu la fais binaire et tu l'appelles (n-1) fois (gaffe à l'implémentation) ou alors avec une liste template ( à coup de cons) (ou de boost::tuple)


Message édité par Taz le 04-09-2003 à 15:14:11
Reply

Marsh Posté le 04-09-2003 à 14:00:01    

Taz a écrit :

Leto >  [:xp1700]  


 
Ca veut dire quoi ça, je me suis complétement planté ou c super génial? Non par ce que avec toi je me méfie, et vu que je suis absolument pas sûr de ce que j'ai écrit... :whistle:


Message édité par LetoII le 04-09-2003 à 14:00:24

---------------
Le Tyran
Reply

Marsh Posté le 04-09-2003 à 15:11:12    

Joel F ?

Reply

Marsh Posté le 04-09-2003 à 15:20:34    


 
Soit il bosse, soit il est rentré chez lui :D


---------------
Le Tyran
Reply

Marsh Posté le 04-09-2003 à 16:08:59    

...soit il est mort.
C'est une hypothèse qu'on formule rarement mais il est déjà arrivé qu'elle se vérifie.


---------------
NOUVEAU! Le guide de l'édition en version ebook : http://marcautret.free.fr/autret/150q-ebook/
Reply

Marsh Posté le 04-09-2003 à 16:12:36    

ACut a écrit :

...soit il est mort.
C'est une hypothèse qu'on formule rarement mais il est déjà arrivé qu'elle se vérifie.


 
Ouai mais là ça serait con il m'a pas encore mis sur son testament  :whistle:


---------------
Le Tyran
Reply

Marsh Posté le 04-09-2003 à 16:27:21    

Je suis pas mort, j'etais en reunion :p
 
@Leto : je crois aps qu ca marche &a c connu que a l'execution pas a la compilation.
 
@Taz : hmmm des precisions ?? la je comprends pas tout

Reply

Marsh Posté le 04-09-2003 à 16:29:22    

Taz a écrit :

je lui ai déjà conseillé y a longtemps, mais il s'entête


 
 :??:  :??:  
 
je veux bien mais ou dois je mettre mon code altivec si je recod epas mes foncteurs a la main ??

Reply

Marsh Posté le 04-09-2003 à 16:30:09    

sur ? moi je te dis juste, que le fastidieux, c'est de taper du code à chaque fois pour vérifier qui est qui. si tu wrappes tes références et que dans chaque fonction tu unique ( tuple <shared_ptr <Reference> > > ) ou un truc du genre, t'as moyen de trouver un traitement générique pour éliminer les alias.

Reply

Marsh Posté le 04-09-2003 à 16:33:02    

Taz a écrit :

sur ? moi je te dis juste, que le fastidieux, c'est de taper du code à chaque fois pour vérifier qui est qui. si tu wrappes tes références et que dans chaque fonction tu unique ( tuple <shared_ptr <Reference> > > ) ou un truc du genre, t'as moyen de trouver un traitement générique pour éliminer les alias.


 
Justement sur ces wrappers de references ... en quoi ca va m'aider

Reply

Marsh Posté le 04-09-2003 à 16:40:34    

ben comment tu veux faire ? tu peux pas affecter des références entre elles ?

Reply

Marsh Posté le 04-09-2003 à 16:41:06    

Joel F a écrit :

Je suis pas mort, j'etais en reunion :p
 
@Leto : je crois aps qu ca marche &a c connu que a l'execution pas a la compilation.
 


 
Ben tu remplace par un opérateur == de ton cru  :whistle:


---------------
Le Tyran
Reply

Marsh Posté le 04-09-2003 à 16:58:28    

Taz a écrit :

ben comment tu veux faire ? tu peux pas affecter des références entre elles ?


 
y a un quiproquo la non :D  [:taimp]  
??  :??:

Reply

Marsh Posté le 04-09-2003 à 17:07:25    

template<typename T>
class refWrapper
{
  struct ref
  {
    T &r;
 
    ref(T &i)
      : r(i)
    {}
 
  private:
    ref& operator=(const ref & );
    ref(const ref & );
 
  };
 
  boost::shared_ptr<ref> rp;
 
 
public:
 
  explicit refWrapper(T &r)
    : rp( new ref(r) )
  {}
 
  refWrapper(const refWrapper &other)
    : rp( other.rp )
  {}
 
  refWrapper& operator=(const refWrapper &other)
  {
    if(this != &other)
      {
 this->rp = other.rp;
      }
 
    return *this;
  }
 
  T& operator*()  
  {
    return this->rp->r;
  }
 
};
tu fais une daube comme ça
 
 
et apres, vu que tes références sont vers des objets de même type, tu dois avoir un moyen sympa de faire la chose
 
void f( truc &a, truc &b)
{
  refWrapper aa(a);
  refWrapper bb(b);
 
  if(aa == bb) // tiens je l'ai pas ecris celui la
  {
    aa = bb;
  }
 
  call(*aa, *bb);
}
 
après tu peux faire un truc avec template, 1 par classe n-aire de fonction avec un tuple de refWrapper de longueur N, et tu bricoles les tests, comme ça à chaque fois
 
 
void fxwrapper( arg0, arg2, arg3, n-1)
{
  tuple < ... > t(arg0, ..., argn-1)
  unique(t);
  fx(*t.get<0)>, ... t.get<n-1>());
}

 
mais peut etre je divague
mais peut etre je divague


Message édité par Taz le 04-09-2003 à 17:17:19
Reply

Marsh Posté le 04-09-2003 à 17:15:36    

je crois que je me suis complètement égaré dans mes pensées là. surtout sur la fin ...

Reply

Marsh Posté le 04-09-2003 à 17:19:22    

Taz a écrit :

je crois que je me suis complètement égaré dans mes pensées là. surtout sur la fin ...

en fait j'en reviens au meme point qu'au début: soit des trucs anonymes, soit tu te cognes des spécialisation pour les cas favorables
 
edit: bon, j'ai fait nimp comme jamais, mais au moins j'ai réfléchi


Message édité par Taz le 04-09-2003 à 17:20:35
Reply

Marsh Posté le 04-09-2003 à 17:29:21    

bon merci quand même ...
ca fait 1 mois qu eje suis le nez dedans et je m'en sort ps ...
je crois que je vais reprendre le probleme calmement plu stard ..
 
j'ai autrement de quoi m'occupé d'ici la

Reply

Marsh Posté le 04-09-2003 à 17:30:42    

de toutes façons, y aura du code à dupliquer en fonction de l'usage, le tout c'est de trouver la meilleur manière générique pour que l'appel soit élégant et que ça soit pas chiant à écrire

Reply

Marsh Posté le 04-09-2003 à 17:34:15    

attends, le problème il est bien là
 
t1 =  vec_ld(0,a.begin);
t2 =  vec_ld(0,b.begin);
t3 =  vec_ld(0,a.begin);
t4 =  vec_ld(0,b.begin);  
 
 
masi transformer en  
 
t1 =  vec_ld(0,a.begin);
t2 =  vec_ld(0,b.begin);
t3 = t1
t4 = t2
 
 
ça serait bon?

Reply

Marsh Posté le 04-09-2003 à 17:46:32    

oui c les vec_ld qui coutent cher.

Reply

Marsh Posté le 04-09-2003 à 18:04:32    

spécialiser un template ca veut bien dire lui ajouter des membres qui ne fonctionnent que pour un type particulier ?
 
par exemple au lieu d'écrire
 

Code :
  1. template<class T>
  2. montemplate<T>::membre(T var)
  3. {
  4. //
  5. }


 
écrire

Code :
  1. template<class T>
  2. montemplate<T>::membre(int var)
  3. {
  4. //
  5. }


 
?


Message édité par blackgoddess le 04-09-2003 à 18:05:29

---------------
-( BlackGoddess )-
Reply

Marsh Posté le 04-09-2003 à 18:05:30    

Presque :
 

Code :
  1. template<class T>
  2. template::membre(T var)
  3. {
  4. //
  5. }


 
écrire

Code :
  1. template<>
  2. template::membre<int>(int var)
  3. {
  4. //
  5. }

 
 
est la bonne syntaxe
 
(et le rapport avec la choucroute  :??: )


Message édité par Joel F le 04-09-2003 à 18:06:03
Reply

Marsh Posté le 04-09-2003 à 18:06:26    

d'accord, merci :)


---------------
-( BlackGoddess )-
Reply

Marsh Posté le 04-09-2003 à 18:07:23    

bin dsl, je m'ecarte du sujet, taz a parlé de spécialisation, je comprenais pas, je demande ici plutot que d'ouvrir un nouveau topic :p


---------------
-( BlackGoddess )-
Reply

Marsh Posté le 04-09-2003 à 18:12:35    

Code :
  1. #include <iostream>
  2. #include <boost/shared_ptr.hpp>
  3. #include <boost/tuple/tuple.hpp>
  4. struct Vector
  5. {};
  6. struct LoadedVectorHandle
  7. {
  8.   Vector *v;
  9.   LoadedVectorHandle(Vector *vv = 0)
  10.     : v(vv)
  11.   {}
  12. };
  13. LoadedVectorHandle load(Vector &v)
  14. {
  15.   return LoadedVectorHandle(&v);
  16. };
  17. class UniqueLoader
  18. {
  19.   struct Shared
  20.   {
  21.     Vector &v;
  22.     LoadedVectorHandle lvh;
  23.     bool loaded;
  24.     Shared(Vector &vv)
  25.       : v(vv), loaded(false)
  26.     {}
  27.   };
  28.   boost::shared_ptr< Shared> rep;
  29. public:
  30.   UniqueLoader(Vector &v)
  31.     : rep( new Shared(v) )
  32.   {}
  33.   UniqueLoader(const UniqueLoader &other)
  34.     : rep( other.rep )
  35.   {}
  36.   UniqueLoader& operator=(const UniqueLoader &other)
  37.   {
  38.     if(this != &other)
  39.       {
  40. this->rep = other.rep;
  41.       }
  42.     return *this;
  43.   }
  44.   LoadedVectorHandle load()
  45.   {
  46.     if(!this->rep->loaded)
  47.       {
  48. this->rep->lvh = ::load(this->rep->v);
  49. this->rep->loaded=true;
  50. std::cout << "loaded " << static_cast<void*>(&this->rep->v) << '\n';
  51.       }
  52.     return this->rep->lvh;
  53.   }
  54.   Vector* operator*()
  55.   {
  56.     return &this->rep->v;
  57.   }
  58. };
  59. void compute(LoadedVectorHandle a, LoadedVectorHandle b, LoadedVectorHandle c)
  60. {
  61.     std::cout << "compute "
  62.     << static_cast<void*>(a.v) << ' '
  63.     << static_cast<void*>(b.v) << ' '
  64.     << static_cast<void*>(c.v) << '\n';
  65. }
  66. void computeWrapper(Vector &a, Vector &b, Vector &c)
  67. {
  68.   std::cout << "computeWrapper "
  69.     << static_cast<void*>(&a) << ' '
  70.     << static_cast<void*>(&b) << ' '
  71.     << static_cast<void*>(&c) << '\n';
  72.   boost::tuple< UniqueLoader, UniqueLoader, UniqueLoader > args(a, b, c);
  73.   if( *args.get<0>() == *args.get<1>() )
  74.     {
  75.       args.get<0>() = args.get<1>();
  76.     }
  77.   if( *args.get<0>() == *args.get<2>() )
  78.     {
  79.       args.get<0>() = args.get<2>();
  80.     }
  81.   if( *args.get<1>() == *args.get<2>() )
  82.     {
  83.       args.get<1>() = args.get<2>();
  84.     }
  85.   compute( args.get<0>().load(), args.get<1>().load(), args.get<2>().load() );
  86.   std::cout << std::endl;
  87. }
  88. int main()
  89. {
  90.   Vector a, b, c;
  91.   computeWrapper(a, b, c);
  92.   computeWrapper(a, a, b);
  93.   computeWrapper(a, a, a);
  94. }


Message édité par Taz le 04-09-2003 à 18:32:53
Reply

Marsh Posté le 04-09-2003 à 18:14:52    

hmm je vois la chose ...
 
qss expiclations qd même :p

Reply

Marsh Posté le 04-09-2003 à 18:25:19    

mis a jour . regarde l'affichage. l'idée est bete. tes différents paramtères peuvent en fait aliaser des variables communes. dans mon truc, un Vector est transformé en LVH une fois chargé avec load, le but étant de ne pas charger inutilement 2 fois le meme Vector. je refrabrique cette aliasing avec des UniqueLoader à base de shared_ptr qui partage un Vector, un lvh et un drapeau pour savoir si load a été appelé. ensuite j'ai mis tout ça dans mon tuple (je sais pas pourquoi, mais je pense qu'il doit y avoir un moyen de généraliser peut etre, et puis je peux appeler les constructeurs avec, mais bon, on pourrait faire avec des variables ou autres, j'y reviens), je farfouille mon tuple pour recréer les alias, c'est à dire, faire les affectations pour que les UL partagent les implémentations quand nécessaire.
 
ensuite, reste plus qu'a UL::load qui load s'il le faut et renvoie un LVH
 
l'affichage est concluant
 
 
pour les tuples, je pense que ya moyen de métaprogrammer tout ça, de manière d'exprimer l'opération d'alias sur un tuple de N éléments en fonctions de l'opération sur un tuple de N-1 éléments

Reply

Marsh Posté le 04-09-2003 à 19:04:38    

Code :
  1. static inline void Unique2(boost::tuple<UniqueLoader, UniqueLoader> t)
  2.   {
  3.     if( *t.get<0>() == *t.get<1>() )
  4.       {
  5. t.get<0>() = t.get<1>();
  6.       }
  7.   }
  8.   static inline void Unique3(boost::tuple<UniqueLoader, UniqueLoader, UniqueLoader> t)
  9.   {
  10.     Unique2( boost::make_tuple(t.get<0>(), t.get<1>()) );
  11.     Unique2( boost::make_tuple(t.get<0>(), t.get<2>()) );
  12.     Unique2( boost::make_tuple(t.get<1>(), t.get<2>()) );
  13.   }
  14.   static inline void Unique4(boost::tuple<UniqueLoader, UniqueLoader, UniqueLoader, UniqueLoader> t)
  15.   {
  16.     Unique3( boost::make_tuple(t.get<0>(), t.get<1>(), t.get<2>()) );
  17.     Unique3( boost::make_tuple(t.get<0>(), t.get<1>(), t.get<3>()) );
  18.     Unique3( boost::make_tuple(t.get<0>(), t.get<2>(), t.get<3>()) );
  19.     Unique3( boost::make_tuple(t.get<1>(), t.get<2>(), t.get<3>()) );
  20.   }


 

Code :
  1. boost::tuple< UniqueLoader, UniqueLoader, UniqueLoader > args(a, b, c);
  2.   UniqueLoader::Unique3(args);


 
ça marche (pas testé le 4, mais l'idée est là)  
les UniqueLoader supporte parfaitement l'affecation, donc y a pas de problèmes si tout ce passe par copie, puisque les implémentations sont partagées. j'ai pas fait à coup de référence et de boost::tie, par ce que c'est plus long à écrire. attention avec boost::tuple, y a des surprises, voir la doc
 
on doit pouvoir faire par template pour avoir des jolis numéros <3>, mais gaffe encore à la manipulation des tuple, il faudra donc faire
 
template<typename Tuple, unsigned N> etc


Message édité par Taz le 04-09-2003 à 19:07:28
Reply

Marsh Posté le 04-09-2003 à 19:43:38    

[:taimp] nickel !!!
 
Evidemment moi, j'ai besoin de gerer tout les UniqueLoader pour 1 a N argument et remplacer les load par mes vec_ld !!!
 
Je teste tous ca et je te dit si ca marche :p
 
 :jap: big merci

Reply

Marsh Posté le 04-09-2003 à 19:51:04    

Bon :
 
CA MARCHE !
 
J'ai testé pour deux, trois et quatre arguments et le gain est de bien 35%. Je suis pas a 50% de plus bicose les tests et le fait que j'ai pas MPT les tuples mais c deja encourageant :D
 
MERCI !!!!

Reply

Marsh Posté le    

Reply

Sujets relatifs:

Leave a Replay

Make sure you enter the(*)required information where indicate.HTML code is not allowed