équivalent plus rapide de gettimeofday
En essayant de construire une application très sensible à la latence, qui doit envoyer 100s de messages par seconde, chaque message ayant le champ time, nous voulions envisager d'optimiser gettimeofday.
La première pensée était l'optimisation basée sur rdtsc
. Toutes les pensées ? Toute autre pointeurs ?
L'exactitude requise de la valeur de temps renvoyée est en millisecondes, mais ce n'est pas une grosse affaire si la valeur est parfois désynchronisée avec le récepteur pendant 1-2 millisecondes.
Essayer de faire mieux que les 62 nanosecondes gettimeofday prend
5 réponses
Avez-vous réellement comparé et trouvé gettimeofday
trop lent?
À raison de 100 messages par seconde, vous avez 10ms de temps CPU par message. Si vous avez plusieurs cœurs, en supposant qu'il peut être entièrement parallélisé, vous pouvez facilement augmenter cela de 4-6x-c'est 40-60ms par message! Le coût de gettimeofday est peu susceptible d'être proche de 10ms-je soupçonne que c'est plus comme 1-10 microsecondes (sur mon système, microbenchmarking il donne environ 1 microseconde par appel - essayez-le pour vous-même). Vos efforts d'optimisation seraient mieux dépensés ailleurs.
Bien que l'utilisation du TSC soit une idée raisonnable, Linux moderne a déjà un gettimeofday basé sur userspace TSC-si possible, le vdso utilisera une implémentation de gettimeofday qui applique un décalage (lu à partir d'un segment de mémoire utilisateur-noyau partagé) à la valeur de rdtsc
, calculant ainsi l'Heure de la journée sans entrer Cependant, certains modèles de CPU n'ont pas de TSC synchronisé entre différents cœurs ou différents paquets, et cela peut donc finir par être désactivé. Si vous voulez une synchronisation haute performance, vous pouvez d'abord envisager de trouver un modèle de CPU qui a un TSC synchronisé.
Cela dit, si vous êtes prêt à sacrifier une quantité significative de résolution (votre timing ne sera précis qu'à la dernière coche, ce qui signifie qu'il pourrait être désactivé de dizaines de millisecondes), vous pouvez utiliser CLOCK_MONOTONIC_COARSE ou CLOCK_REALTIME_COARSE avec clock_gettime. C'est également implémenté avec le vdso, et garanti de ne pas appeler dans le noyau (pour les noyaux récents et la glibc).
Horloges POSIX
J'ai écrit un benchmark pour les sources D'horloge POSIX:
- temps (s) => 3 cycles
- ftime (ms) = > 54 cycles
- gettimeofday (us) = > 42 cycles
- clock_gettime (ns) = > 9 cycles (CLOCK_MONOTONIC_COARSE)
- clock_gettime (ns) = > 9 cycles (CLOCK_REALTIME_COARSE)
- clock_gettime (ns) = > 42 cycles (CLOCK_MONOTONIC)
- clock_gettime (ns) = > 42 cycles (CLOCK_REALTIME)
- clock_gettime (ns) = > 173 cycles (CLOCK_MONOTONIC_RAW)
- clock_gettime (ns) = > 179 cycles (CLOCK_BOOTTIME)
- clock_gettime (ns) = > 349 cycles (CLOCK_THREAD_CPUTIME_ID)
- clock_gettime (ns) = > 370 cycles (CLOCK_PROCESS_CPUTIME_ID)
- rdtsc (cycles) = > 24 cycles
Ces chiffres proviennent D'un processeur Intel Core i7-4771 à 3,50 GHz sous Linux 4.0. Ces mesures ont été prises en utilisant le registre TSC et en exécutant chaque méthode d'horloge des milliers de fois et en prenant le coût minimum valeur.
Vous voudrez tester sur les machines que vous avez l'intention d'exécuter, car leur implémentation varie selon le matériel et la version du noyau. Le code peut être trouvé ici. Il s'appuie sur le registre TSC pour le comptage des cycles, qui se trouve dans le même repo (tsc.h ).
TSC
L'accès au TSC (compteur d'horodatage du processeur) est le moyen le plus précis et le moins cher de chronométrer les choses. Généralement, c'est ce que le noyau utilise lui-même. Il est aussi tout à fait straight-forward sur les puces Intel modernes comme le TSC est synchronisé entre les cœurs et non affecté par la mise à l'échelle de fréquence. Il fournit donc une source de temps simple et globale. Vous pouvez voir un exemple d'utilisation ici, avec une procédure de l'assemblée du code ici.
Le principal problème avec ceci (autre que la portabilité) est qu'il ne semble pas y avoir un bon moyen de passer des cycles aux nanosecondes. Les documents Intel autant que je peux trouver indiquent que le TSC fonctionne à une fréquence fixe, mais que cette fréquence peut différer de la fréquence indiquée par les processeurs. Intel ne semble pas fournir un moyen fiable de comprendre la fréquence TSC. Le noyau Linux semble résoudre ce problème en testant combien de cycles TSC se produisent entre deux minuteries matérielles (voir ici).
Memcached
Memcached dérange de faire la méthode cache. Il peut s'agir simplement de s'assurer que les performances sont plus prévisibles sur toutes les plates-formes, ou de mieux évoluer avec plusieurs cœurs. Il peut également ne pas être un la peine d'optimisation.
Comme le dit bdonian, si vous n'envoyez que quelques centaines de messages par seconde, gettimeofday
sera assez rapide.
Cependant, si vous envoyiez des millions de messages par seconde, cela pourrait être différent (mais vous devriez quand même mesurer que c'est un goulot d'étranglement). Dans ce cas, vous voudrez peut-être considérer quelque chose comme ceci:
- avoir une variable globale, donnant l'horodatage actuel dans la précision souhaitée
- avoir un thread d'arrière-plan dédié qui rien d'autre que de mettre à jour l'horodatage (si l'horodatage doit être mis à jour toutes les T unités de temps, alors que le thread dort une fraction de T, puis met à jour L'horodatage; utilisez des fonctionnalités en temps réel si vous en avez besoin)
- tous les autres threads (ou le processus principal, si vous n'utilisez pas de threads autrement) lit simplement la variable globale
Le langage C ne garantit pas que vous pouvez lire la valeur d'horodatage si elle est supérieure à sig_atomic_t
. Vous pouvez utiliser le verrouillage pour faire face à cela, mais le verrouillage est lourd. Au lieu de cela, vous pouvez utiliser une variable typée volatile sig_atomic_t
pour indexer un tableau d'horodatages: le thread d'arrière-plan met à jour l'élément suivant du tableau, puis met à jour l'index. Les autres threads lisent l'index, puis lisent le tableau: ils peuvent obtenir un timestamp un peu obsolète (mais ils obtiennent le bon la prochaine fois), mais ils ne rencontrent pas le problème où ils lisent l'horodatage en même temps qu'il est mis à jour, et obtiennent quelques octets de l'ancienne valeur et certains des nouveaux valeur.
Mais tout cela est beaucoup exagéré pour seulement des centaines de messages par seconde.
Ci-dessous est une référence. Je vois environ 30ns. printTime () de rashad Comment obtenir l'heure et la date actuelles en C++?
#include <string>
#include <iostream>
#include <sys/time.h>
using namespace std;
void printTime(time_t now)
{
struct tm tstruct;
char buf[80];
tstruct = *localtime(&now);
strftime(buf, sizeof(buf), "%Y-%m-%d.%X", &tstruct);
cout << buf << endl;
}
int main()
{
timeval tv;
time_t tm;
gettimeofday(&tv,NULL);
printTime((time_t)tv.tv_sec);
for(int i=0; i<100000000; i++)
gettimeofday(&tv,NULL);
gettimeofday(&tv,NULL);
printTime((time_t)tv.tv_sec);
printTime(time(NULL));
for(int i=0; i<100000000; i++)
tm=time(NULL);
printTime(time(NULL));
return 0;
}
3 sec pour 100 000 000 appels ou 30ns;
2014-03-20.09:23:35
2014-03-20.09:23:38
2014-03-20.09:23:38
2014-03-20.09:23:41
Avez-vous besoin de la précision de la milliseconde? Sinon, vous pouvez simplement utiliser time()
et gérer l'horodatage unix.