3 votes

Modèle de classe pour l'utilisation de la mémoire alignée sur le cache en C++.

(fournir les informations dont vous avez besoin pour comprendre ma question, c'est beaucoup, mais c'est déjà compressé)

J'essaie d'implémenter un modèle de classe pour allouer et accéder au cache de données aligné. Cela fonctionne très bien, mais l'implémentation de la prise en charge des tableaux pose problème.

Sémantiquement, le code doit fournir ce mappage en mémoire pour un seul élément comme celui-ci :

cache_aligned<element_type>* my_el = 
          new(cache_line_size) cache_aligned<element_type>();
| element | buffer |

l'accès (jusqu'à présent) ressemble à ceci :

*my_el; // returns cache_aligned<element_type>
**my_el; //returns element_type
*my_el->member_of_element();

CEPENDANT, pour un tableau, j'aimerais avoir ceci :

 cache_aligned<element_type>* my_el_array = 
         new(cache_line_size)  cache_aligned<element_type()[N];
 | element 0 | buffer | element 1 | buffer | ... | element (N-1) | buffer |

Pour l'instant, j'ai le code suivant

template <typename T>
class cache_aligned {
    private:
        T instance;
    public:
        cache_aligned()
        {}
        cache_aligned(const T& other)
        :instance(other.instance)
        {}
        static void* operator new (size_t size, uint c_line_size) {
             return c_a_malloc(size, c_line_size);
        }
        static void* operator new[] (size_t size, uint c_line_size) {
             int num_el = (size - sizeof(cache_aligned<T>*) 
                              / sizeof(cache_aligned<T>);
             return c_a_array(sizeof(cache_aligned<T>), num_el, c_line_size);
        }
        static void operator delete (void* ptr) {
             free_c_a(ptr);
        }
        T* operator-> () {
             return &instance;
        }
        T& operator * () {
             return instance;
        }
};

les fonctions cache_aligned_malloc

void* c_a_array(uint size, ulong num_el, uint c_line_size) {
    void* mem = malloc((size + c_line_size) * num_el + sizeof(void*));
    void** ptr = (void**)((long)mem + sizeof(void*));
    ptr[-1] = mem;
    return ptr;
}

void free_c_a(void ptr) {
    free(((void**)ptr)[-1]);
}

Le problème est ici, l'accès aux données devrait fonctionner comme ceci :

my_el_array[i]; // returns cache_aligned<element_type>
*(my_el_array[i]); // returns element_type
my_el_array[i]->member_of_element();

Mes idées pour résoudre ce problème sont :

(1) quelque chose de similaire à ceci, pour surcharger l'opérateur sizeof :

static size_t operator sizeof () {
   return sizeof(cache_aligned<T>) + c_line_size;
}

--> pas possible car la surcharge de l'opérateur sizeof est illégale

(2) quelque chose comme ceci, pour surcharger l'opérateur [] pour le type pointeur :

static T& operator [] (uint index, cache_aligned<T>* ptr) {
    return ptr + ((sizeof(cache_aligned<T>) + c_line_size) * index);
}

--> pas possible en C++, de toute façon

(3) solution totalement triviale

template <typename T> cache_aligned {
    private:
          T instance;
          bool buffer[CACHE_LINE_SIZE]; 
          // CACHE_LINE_SIZE defined as macro
    public:
          // trivial operators and methods ;)
};

--> je ne sais pas si c'est fiable, en fait j'utilise gcc-4.5.1 sous linux ...

(4) Remplacer T instance ; par T* instance_ptr ; dans le modèle de classe et utiliser l'opérateur [] pour calculer la position de l'élément, comme ceci :

| pointeur-à-instance | ----> | élément 0 | tampon | ... | élément (N-1) | tampon |

ce n'est pas la sémantique prévue, puisque l'instance du modèle de classe devient le goulot d'étranglement lors du calcul de l'adresse des éléments.

Merci de m'avoir lu ! Je ne sais pas comment raccourcir le problème. Ce serait formidable si vous pouviez m'aider ! Toute solution de contournement m'aiderait beaucoup.

Je sais que l'alignement est une extension du C++0x. Cependant, dans gcc, il n'est pas encore disponible.

Greetz, sema

1voto

Öö Tiib Points 4755

Lorsque c_line_size est une constante intégrale au moment de la compilation, il est bien sûr préférable de remplir le cache_aligné avec un tableau de chars dépendant de sizeof T.

Vous pouvez également vérifier si 2 T-s tiennent sur une ligne de cache et réduire l'exigence d'alignement en conséquence.

N'attendez pas de miracles d'une telle optimisation. Je pense qu'une performance deux fois supérieure pour certains algorithmes est le plafond que vous pouvez atteindre en évitant les divisions de lignes de cache.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X