Я уже вся заждалась, измаялась и даже немного всплакнула.
Не так давно попалась в руки одна ошибка. Проявлялась она в том, что некое приложение, общающееся с коллегой через TCP/IP соединение, жаловалось на то, что пересылка пакетов по сети занимает около получаса, и что, вообще, заставлять девушку ждать более 300 миллисекунд – неприлично. Впрочем, судя по тому, что приложение работало как ни в чем не бывало, – замечание про полчаса ожидания было легким преувеличением. Для полноты картины добавлю, что то приложение было в процессе переноса на другую платформу с целью «чиста позапускать» (и посмотреть, как оно там работает).
Кусок кода, выводящий сообщение нашелся быстро. Однако на код, вычисляющий задержку, пришлось немного помедитировать. Как следует помедитировав отладчиком, удалось выделить алгоритм вычисления задержки, который выглядел примерно вот так:
- Клиент посылает текущее время вместе с пакетом:
message->timestamp = (DWORD)QueryPerformanceCounter(); send(message); - Сервер прибавляет свое время при приеме пакета и отнимает – при посылке:
message->timestamp += (DWORD)QueryPerformanceCounter(); … message->timestamp -= (DWORD)QueryPerformanceCounter(); send(message); - Клиент сравнивает текущее время с полученным в пакете:
DWORD delta = (DWORD)QueryPerformanceCounter() - message->timestamp; double latency = (double)delta / QueryPerformanceFrequency();
Фактически клиент меряет время, прошедшее между отправкой и приемом пакета, за вычетом времени на обработку пакета сервером. Трюк с использованием младших 4-х байт счетчика хотя и не очень красив, но работает при условии что, один DWORD покрывает интервал заведомо больший, чем максимальная задержка пакета.
Причина ошибки оказалась в том, что частота таймера высокого разрешения оказалась различной на порядки на клиенте и сервере. Допущение, что все машины используют одинаковую частоту таймера высокого разрешения было верным до тех пор, пока приложение не попало в сильно отличное от стандартного окружение.
очень смешно что в майкрософте не знают архитектуру ПК
Бедные, мы бедные… И что же сподвигло вас, уважаемый, на такой глобальный вывод?
Странно это, кто-то использовал QueryPerformanceFrequency но при этом не догадывался, что оно может быть разное?
Да, это довольно спорное решение. Хотя это приложение запускется в дата центре, на фактически идентичном железе.
А какие преимущества есть у интерфейса в стиле QPC/QPF перед позиксными clock_gettime/clock_getres?
Если вызывающая сторона все равно будет преобразовывать результат во время, почему нельзя отдавать его в виде временных единиц, а не абстрактных тиков?
Скорее всего – так убирается одно лишнее преобразование тиков таймера в секунды/наносекунды в недрах QueryPerformanceCounter. Лишнее – так как потом переводить все равно переводить тики/секунды в часовые пояса, дни и часы.
У Криса Касперски была одна статья именно про таймеры в ПК и их точность, а так же какие АПИ используют какие таймеры на материнке и какие из них точнее.
Если нужно, то могу дать ссылку.