Les codes fantastiques : un zéro pointé

Magazine
Marque
GNU/Linux Magazine
Numéro
271
Mois de parution
septembre 2024
Spécialité(s)


Résumé

Continuons cette série sur les codes fantastiques avec la découverte d’une implémentation audacieuse de strlen.


Body

Au XXIe siècle, quand on parle de code vectorisé, on veut souvent dire « code qui utilise un jeu d’instructions vectorielles », comme SSE ou AVX, afin d’exécuter plusieurs opérations en une seule instruction. Par exemple, en C, on peut charger dans des registres vectoriels les données issues de tableaux de flottants, puis en faire le produit, en utilisant l’intrinsèque :

__m128 z = _mm_mul_ps(x, y);

Chaque registre vectoriel contenant 4 nombres flottants en format simple précision, on aura alors fait 4 opérations en 1 seule instruction. Et il existe une multitude de ces fonctions. Il y a néanmoins un inconvénient : il faut commencer par charger des données depuis la mémoire vers des registres vectoriels, et cela a un coût qu’il faut ensuite amortir en faisant plusieurs opérations vectorielles sur ces registres.

Dans le cas qui nous intéresse, strlen, on veut rechercher un zéro le plus rapidement possible. On pourrait utiliser une instruction SSE pour cela (p. ex. _mm_cmpeq_epi8 disponible depuis SSE2, ou la bien plus évoluée _mm_cmpistri depuis SSE4.2), mais cette fois, on ne va pas utiliser de registre vectoriel : on va plutôt considérer qu’un uint32_t, ou un mot, est une sorte de registre vectoriel contenant 4 octets. L’idée étant alors de parcourir les octets 4 par 4, c’est-à-dire un mot à la fois, ce qu’un processeur sait faire de manière efficace. La boucle principale de notre implémentation serait alors :

do
    word = *++word_ptr;
while (!haszero (word));

qui s’exécute 4 fois plus vite que la boucle naïve sur des octets, à condition d’avoir un test rapide pour has_zero. Et pour cela, on peut consulter la magnifique page de Sean Eron Anderson, Bit Twiddling Hacks, https://graphics.stanford.edu/~seander/bithacks.html, qui nous propose :

#define haszero(v) (((v) - 0x01010101UL) & ~(v) & 0x80808080UL)

soit tout juste 4 opérations arithmétiques pour tester chaque octet du mot considéré. Notons au passage que cela s’étend facilement à des mots de 8 octets.

Une fois sorti de la boucle, il faudra tout de même trouver quel est l’octet positionné à zéro, il se trouve qu’un appel à __builtin_ctz (ctz = Count Trailing Zero) sur la sortie de haszero suffit.

Cette implémentation est plus difficile à maintenir que la version naïve, et on peut se demander si cela vaut vraiment la peine. Sur une fonction aussi usitée que strlen, les développeurs de la glibc ont tranché pour vous, la réponse est oui, c’est une de leurs implémentations qui a inspiré cet article, vous pouvez la retrouver sur https://sourceware.org/git/?p=glibc.git;a=blob;f=string/strlen.c. Et de manière plus générale, l’astuce consistant à utiliser des registres scalaires comme des registres vectoriels est amusante, et offre une belle occasion de se plonger dans les bit twiddling hacks !



Article rédigé par

Par le(s) même(s) auteur(s)

C sans frontières : une extension de C avec vérification de bornes

Magazine
Marque
MISC
Numéro
143
Mois de parution
janvier 2026
Spécialité(s)
Résumé

D’aucuns penseraient que le langage C est un langage simple. Après tout le fameux Kernighan & Ritchie de 1978 (a.k.a. White Book) ne fait que 200 pages, bien peu pour un langage qui a servi de base au noyau Linux, au compilateur GCC et à l’interpréteur de référence de Python, CPython. Et pourtant s’il y a bien une notion parmi toutes celles introduites dans the White Book qui met au défi ingénieurs en sécurité, chercheurs en compilation et développeurs en tout genre, c’est la notion de pointeur, et cette terrible question : le déréférencement de mon pointeur donne-t-il lieu à un comportement indéfini ?Des ingénieurs de chez Apple ont trouvé un chemin pragmatique et intéressant à cette question, et ils l’ont mis à disposition dans un fork de clang à travers une option, -fbounds-safety, et un fichier d’en-tête, <ptrcheck.h>. Examinons leur approche.

C++ : contrôlez votre espérance de vie

Magazine
Marque
MISC
Numéro
141
Mois de parution
septembre 2025
Spécialité(s)
Résumé

Le langage C est un magnifique outil pédagogique pour enseigner le concept de mémoire, puisqu’il laisse la main au développeur pour la gérer. Mais on le sait, cet attrait n’en est pas un quand on parle de sûreté d’exécution, puisque ce langage est connu pour ne pas aider le développeur pour détecter les usages illégaux liés à la mémoire. Le langage Rust a attaqué le problème en rendant explicite le concept de durée de vie. Le langage Safe C++ tente quant à lui d’introduire ce concept en C++. Il y a plus de vingt ans, splint proposait déjà un concept moins ambitieux pour C. Et maintenant certains fous essaient de le porter vers C++, à travers des extensions de Clang. Quelles sont donc toutes ces approches ?

Les listes de lecture

Python niveau débutant

9 article(s) - ajoutée le 01/07/2020
Vous désirez apprendre le langage Python, mais ne savez pas trop par où commencer ? Cette liste de lecture vous permettra de faire vos premiers pas en découvrant l'écosystème de Python et en écrivant de petits scripts.

Au pays des algorithmes

11 article(s) - ajoutée le 01/07/2020
La base de tout programme effectuant une tâche un tant soit peu complexe est un algorithme, une méthode permettant de manipuler des données pour obtenir un résultat attendu. Dans cette liste, vous pourrez découvrir quelques spécimens d'algorithmes.

Analyse de données en Python

10 article(s) - ajoutée le 01/07/2020
À quoi bon se targuer de posséder des pétaoctets de données si l'on est incapable d'analyser ces dernières ? Cette liste vous aidera à "faire parler" vos données.
Plus de listes de lecture