samedi 2 février 2013

Laisser le temps au temps

Parlons un peu des routines de la libc que sont localtime_r, gmtime_r, mktime, et timegm. Ces fonctions sont utilisées pour convertir l'heure Unix (le nombre de secondes depuis le premier Janvier 1970) en une structure tm qui contient année, mois, jour, heure, minute et seconde, et inversement, en utilisant l'heure locale ou l'UTC.

Il est donc raisonable pour localtime_r et pour mktime d'accéder à la variable d'environnement TZ pour obtenir le fuseau horaire nécessaire aux conversions en temps local. Il est également raisonnable de protéger ladite variable d'environnement par un beau mutex global. Les performances s'écroulent lorsque trop de threads cherchent à faire des conversions de dates (imaginons par exemple un système de log multithreadé).

Ce qui est moins raisonnable, c'est que beaucoup d'implémentations de gmtime_r et timegm (notamment dans la glibc de RHEL5) utilisent les mêmes routines que localtime_r et mktime, et donc tentent d'acquérir le mutex global quand bien même elles n'en ont aucunement besoin, puisqu'il n'y a pas d'ajustement de fuseau horaire à effectuer. Les performances sont meilleures (6x plus rapide sur mon benchmark) car les calculs sont plus simples, mais il y a encore ce mutex qui s'obstine à nous pourrir la vie.

En allant regarder du côté de FreeBSD, on trouve en revanche des implémentations directes, sans mutex, des fonctions sus-citées. Collons ça dans le code, et c'est un facteur 3 qui est gagné.

La conclusion, c'est que pour faire quoi que ce soit d'avancé avec les dates, il n'y a pas trop le choix: il faut écrire sa propre classe DateTime, extraire les données brutes des fuseaux horaires depuis la tz database, faire soi-même les ajustements, et importer du code lock-free pour ses conversions.

Aucun commentaire: