более быстрый эквивалент gettimeofday - PullRequest
24 голосов
/ 28 июня 2011

Пытаясь создать приложение, чувствительное к времени ожидания, которое должно отправлять сотни сообщений в секунду, каждое из которых имеет поле времени, мы хотели рассмотреть возможность оптимизации gettimeofday.Первая мысль была rdtsc на основе оптимизации.Какие-нибудь мысли ?Любые другие указатели?Требуемая точность возвращаемого значения времени указывается в миллисекундах, но это не имеет большого значения, если значение иногда не синхронизируется с приемником в течение 1-2 миллисекунд.Попытка сделать лучше, чем 62 наносекунды gettimeofday занимает

Ответы [ 5 ]

51 голосов
/ 27 октября 2012

POSIX Clocks

Я написал эталон для источников тактовых импульсов POSIX:

  • время (с) => 3 цикла
  • ftime (мс) => 54циклы
  • gettimeofday (us) => 42 цикла
  • clock_gettime (нс) => 9 циклов (CLOCK_MONOTONIC_COARSE)
  • clock_gettime (нс) => 9 циклов (CLOCK_REALTIME_COARSE)
  • clock_gettime (нс) => 42 цикла (CLOCK_MONOTONIC)
  • clock_gettime (нс) => 42 цикла (CLOCK_REALTIME)
  • clock_gettime (нс) => 173 цикла (CLOCK_MONOTONIC_RAW)
  • clock_gettime (нс) => 179 циклов (CLOCK_BOOTTIME)
  • clock_gettime (нс) => 349 циклов (CLOCK_THREAD_CPUTIME_ID)
  • clock_gettime (нс) => 370 циклов (CLOCK_PE_CROC)
  • rdtsc (циклы) => 24 цикла

Эти цифры взяты из процессора Intel Core i7-4771 @ 3,50 ГГц в Linux 4.0.Эти измерения были выполнены с использованием регистра TSC и запуска каждого тактового метода тысячи раз и с минимальным значением стоимости.

Вы захотите провести тестирование на машинах, на которых вы собираетесь работать, хотя и то, как они реализованы, различается.от аппаратного обеспечения и версии ядра.Код можно найти здесь .Для подсчета циклов используется регистр TSC, который находится в том же репо ( tsc.h ).

TSC

Доступ к TSC (счетчик меток времени процессора)) самый точный и дешевый способ оценивать вещи.Как правило, это то, что ядро ​​использует само.Это также довольно просто на современных чипах Intel, поскольку TSC синхронизируется между ядрами и не зависит от масштабирования частоты.Таким образом, он обеспечивает простой, глобальный источник времени.Вы можете увидеть пример его использования здесь с пошаговым описанием кода сборки здесь .

Основная проблема с этим (кроме переносимости) заключается в том, чтоне кажется хорошим способом перехода от циклов к наносекундам.Документы Intel, насколько я могу найти, утверждают, что TSC работает на фиксированной частоте, но эта частота может отличаться от заявленной частоты процессоров.Похоже, Intel не предоставляет надежного способа выяснить частоту TSC.Ядро Linux, похоже, решает эту проблему, проверяя, сколько циклов TSC происходит между двумя аппаратными таймерами (см. здесь ).

Memcached

Memcached не выполняет метод кэширования.Это может быть просто для того, чтобы обеспечить более предсказуемую производительность на разных платформах или улучшить масштабирование с использованием нескольких ядер.Это также может быть бесполезной оптимизацией.

45 голосов
/ 28 июня 2011

Вы действительно тестировали и обнаружили, что gettimeofday является недопустимо медленным?

При скорости 100 сообщений в секунду у вас есть 10 мс процессорного времени на сообщение.Если у вас несколько ядер, при условии, что оно может быть полностью распараллелено, вы можете легко увеличить его в 4-6 раз - это 40-60 мс на сообщение!Стоимость gettimeofday вряд ли будет где-то около 10 мс - я подозреваю, что она больше 1-10 микросекунд (в моей системе микробенчмаркинг дает примерно 1 микросекунду за вызов - попробуйте сами ),Ваши усилия по оптимизации были бы лучше потрачены в другом месте.

Хотя использование TSC является разумной идеей, в современном Linux уже есть основанное на TSC пользовательское пространство gettimeofday *1009* - где это возможно, vdso будет использоватьреализация gettimeofday, которая применяет смещение (чтение из общего сегмента памяти пользователя-ядра) к значению rdtsc, таким образом вычисляя время суток без входа в ядро.Тем не менее, в некоторых моделях процессоров TSC не синхронизируется между разными ядрами или разными пакетами, и это может привести к отключению.Если вам нужна высокопроизводительная синхронизация, вы можете сначала подумать о том, чтобы найти модель процессора с синхронизированным TSC.

Тем не менее, если вы готовы пожертвовать значительным разрешением (ваше время будет толькобудьте точны до последнего тика, т. е. он может быть отключен на десятки миллисекунд), вы можете использовать CLOCK_MONOTONIC_COARSE или CLOCK_REALTIME_COARSE с clock_gettime .Это также реализовано с помощью vdso и гарантированно не вызывает ядро ​​(для последних ядер и glibc).

4 голосов
/ 28 июня 2011

Как говорит bdonian, если вы отправляете всего несколько сотен сообщений в секунду, gettimeofday будет достаточно быстрым.

Однако, если вы отправляете миллионы сообщений в секунду, это можетбыть другим (но вы все равно должны измерить , что это узкое место).В этом случае вы можете рассмотреть что-то вроде этого:

  • имеет глобальную переменную, которая дает текущую временную метку с желаемой точностью
  • имеет выделенный фоновый поток, который ничего не делает, кромеобновите временную метку (если временная метка должна обновляться каждые T единиц времени, затем поток спит в некоторой доле T, а затем обновляет временную метку; при необходимости используйте функции реального времени)
  • все остальные потоки (или основной процесс, если вы не используете потоки в противном случае) просто читает глобальную переменную

Язык C не гарантирует, что вы можете прочитать значение метки времени, если оно больше sig_atomic_t.Вы можете использовать блокировку, чтобы справиться с этим, но блокировка тяжелая.Вместо этого вы можете использовать типизированную переменную volatile sig_atomic_t для индексации массива временных меток: фоновый поток обновляет следующий элемент в массиве, а затем обновляет индекс.Другие потоки читают индекс, а затем читают массив: они могут получить крошечную устаревшую временную метку (но они получат правильную в следующий раз), но они не сталкиваются с проблемой, когда они читают временную метку вв то же время он обновляется и получает несколько байтов старого значения и часть нового значения.

Но все это сильно излишне для сотен сообщений в секунду.

1 голос
/ 20 марта 2014

Ниже приведен тест.Я вижу около 30нс.printTime () из rashad Как получить текущее время и дату в 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 с для 100 000 000 вызовов или 30 нс;

2014-03-20.09:23:35
2014-03-20.09:23:38
2014-03-20.09:23:38
2014-03-20.09:23:41
0 голосов
/ 28 июня 2011

Вам нужна точность в миллисекундах? Если нет, вы можете просто использовать time() и иметь дело с меткой времени Unix.

...