Bonjour Nal,
Désolé, ceci n'est pas un journal gastronomique. La table en question, c'est une table de hachage. Non, pas un billot de boucher (ni un billet de bouchot). Une vraie, écrite en C, avec un index chaîné qui conserve l’ordre d’insertion et sert de base à l’itération et au tri, comme la LinkedHashMap de Pierre Tramo, mais avec des verrous intégrés et beaucoup plus véloce. C'est cette rapidité qui m'a fait penser que ça pourrait t'intéresser.
Comme on le voit sur cette représentation graphique pour décideur pressé, malgré le poids des verrous et les chaînes aux pieds, elle tourne aussi vite que les SwissTables.
1) Dessous de table
Comme elle est sous licence CeCILL, c'est facile :
https://github.com/RaphaelPrevost/ASKL/blob/0.3.9/lib/askl_htable.c
https://github.com/RaphaelPrevost/ASKL/blob/0.3.9/lib/askl_htable.h
https://github.com/RaphaelPrevost/ASKL/blob/0.3.9/lib/arcane/htable.c
2) Je la retourne
On voit mieux les verrous, comme ça.
https://github.com/RaphaelPrevost/ASKL/blob/0.3.9/lib/askl_rwlock.c
https://github.com/RaphaelPrevost/ASKL/blob/0.3.9/lib/askl_rwlock.h
Ils sont hybrides, à la fois atomiques et dieselpthread. Mais pourquoi diable réinventer le verrou, demandera le connaisseur ? Tout simplement parce que je voulais m'amuser à permettre la promotion d'un simple verrou en lecture au grade de verrou en écriture. Ça permet de réaliser des opérations de modification ou de suppression « en passant », lorsqu'on traverse la table avec un itérateur :
/* je veux remplacer le premier bitoniau après une clé par un bidule */
for (it = map_at(ma_table, "clef", 4); it; it = map_next(it)) {
if (strncmp(it->key, "clef", 4)) {
variant bitoniau = map_set_at(it, variant_from_pointer(bidule));
free(variant_to_pointer(bitoniau));
/* très bien, faisons comme ça */
it = map_break(it); break;
}
}
[...]
/* portage en C de la fonction optimisée "supprimeClefFast" (c) 2003 Pierre Tramo */
for (it = map_each(ma_table); it; it = map_next(it)) {
if (! strncmp(it->key, "clef", 4))
map_remove_at(it);
}
Le verrou en lecture est maintenu pendant la durée de vie de l'itérateur, ce qui empêche les fils d'exécution écrivains de nous tirer le tapis sous les pieds - mais ne nous empêche pas de verrouiller subrepticement en écriture, avec la coopération des autres lecteurs, le temps d'une modification ponctuelle. Impossible de faire ça sans se prendre les pieds dans le tapis avec de simples pthread_rwlocks.
3) Contre le mur
Le mur des accès mémoire bien sûr. Globalement, on s'en sort pas trop mal jusqu'à 10 millions de clés/valeurs, et on peut monter jusqu'à un facteur de charge de 90,9% sans fatiguer. Le maintien de l'index chaîné coûte 8 octets par entrée mais procure un grand confort d'utilisation, en permettant de trier dans toutes les positions et d'avoir un itérateur qui glisse tout seul. On paye encore 8 octets pour la structure variant, plus hygiénique que des pointeurs void. Au total, en comptant absolument tout, on dépense 26 octets pour chaque insertion.
4) Par tous les trous
La table utilise la technique du coucou, c'est-à-dire qu'en l'absence de place libre, on fait son trou en éjectant la clé la mieux placée. La clé ainsi outragée est aussitôt réinsérée, avec potentiellement une autre éjection à la clé si aucun emplacement n'est disponible. Ce comportement vicieux permet globalement de maintenir une bonne répartition des clés sur l'ensemble de la table.
5) Je la compile
gcc -D_ENABLE_HASHTABLE \
-O2 -std=c11 -W -Wall -Wpointer-arith -lpthread \
lib/compat/askl_compat_layer.c lib/askl_rwlock.c lib/askl_variant.c \
lib/askl_htable.c \
atable.c -o atable
Bon bah voilà Journal, ça faisait longtemps que je ne t'avais pas écrit comme ça, j'ai même dû me refaire un compte pour l'occasion. Ça fait plaisir de se retrouver autour d'une table, et c'est meilleur quand c'est partagé.
Bon Vendredi,
# #define public
Posté par David Demelier (site web personnel) . Évalué à 4 (+2/-0). Dernière modification le 16 janvier 2026 à 16:56.
Vraiment ?
https://github.com/RaphaelPrevost/ASKL/blob/0.3.9/lib/askl.h#L171
Non mais je veux dire, un dev C qui sait faire du C comme moi. Il s'attend à quoi en voyant une déclaration de fonction qui n'est pas du C comme :
Ça n'apporte rien à part de la confusion. Je peux comprendre le besoin de faire des export DLL, mais alors il faut nommer ça de manière explicite et surtout pas avec un mot clé réservé en C++ (qui détruit donc la compatibilité)
AI is a mental disorder
[^] # Re: #define public
Posté par JaguarWan . Évalué à 2 (+2/-0).
+1, c'est pas faux, j'ai absolument pas pris en compte C++ quand j'ai codé ça. Je vais remplacer
publicetprivatepar autre chose. Dans le contexte,publiccorrespond aux fonctions exportées,privateaux fonctions utilisées au sein de la bibliothèque mais non exportées (__attribute__((visibility("hidden")))).[^] # Re: #define public
Posté par Clément V . Évalué à 2 (+1/-0).
Il faut mettre des majuscules comme ça on sait que c'est pas un mot-clé. Et comme tu as choisi de faire du C, le language sans espace de nom, mets aussi des préfixes partout.
Comme ça en plus du C++, tu prends aussi en compte toutes les autres bibliothèques C/C++ (en tout cas celles qui ne s'appellent pas ASKL).
Envoyer un commentaire
Suivre le flux des commentaires
Note : les commentaires appartiennent à celles et ceux qui les ont postés. Nous n’en sommes pas responsables.