Я пытаюсь измерить разницу во времени между двумя сигналами на параллельном порту, но сначала я узнал, насколько точна и точна моя измерительная система (AMD Athlon (tm) 64 X2 Dual Core Processor 5200+ × 2 ) на SUSE 12.1 x64.
Итак, после некоторого чтения я решил использовать clock_gettime(), сначала я получаю значение clock_getres() с помощью этого кода:
а выход был: 1 nano second. И я был так счастлив!
Но вот моя проблема, когда я попытался проверить этот факт с помощью этого другого кода:
это вычисляет время между двумя вызовами clock_gettime, time3 и time4 объявляются, но не используются в этом примере, потому что я делал с ними тесты.
Выход в этом примере колеблется между 978 и 1467 нс. оба числа кратно 489, это заставляет меня думать, что 489 нс - это мое РЕАЛЬНОЕ разрешение. далекого от 1 нс, полученного выше.
Мне действительно нужно разрешение не менее 10ns для моего проекта. Давай! GPS может получить лучшее разрешение, чем ПК?
Ответ 2
Я понимаю, что эта тема давно мертва, но хотела бросить мои выводы. Это длинный ответ, поэтому я поставил здесь короткий ответ, и те, у кого есть терпение, могут пробраться через остальное. Не совсем ответ на вопрос - это 700 нс или 1500 нс в зависимости от того, какой режим используется clock_gettime(). Длительный ответ более сложный.
Для справки, машина, на которой я работал, - это старый ноутбук, которого никто не хотел. Это Acer Aspire 5720Z, работающий под Ubuntu 14.041 LTS.
Аппаратное обеспечение:
RAM: 2.0 GiB//Вот как Ubuntu сообщает об этом в "Системные настройки" → "Подробности"
Процессор: Intel® Pentium (R) Двойной процессор T2330 @1,60 ГГц × 2
Графика: Intel® 965GM x86/MMX/SSE2
Я хотел точно измерить время в предстоящем проекте и как относительный новичок на ПК, вне зависимости от операционной системы, я подумал, что сделаю несколько экспериментов по решению аппаратного обеспечения времени. Я наткнулся на этот вопрос.
Из-за этого вопроса я решил, что clock_gettime() выглядит так, как будто он отвечает моим потребностям. Но мой опыт работы с аппаратными средствами ПК в прошлом оставил меня поджатым, поэтому я начал с нескольких экспериментов, чтобы узнать, что такое фактическое разрешение таймера.
Метод: собирайте последовательные образцы результата из clock_gettime() и просматривайте любые шаблоны в разрешении. Далее следует код.
Результаты немного дольше Резюме:
1. Не совсем результат. Указанная резолюция полей в структуре находится в наносекундах. Результатом вызова функции clock_getres() также является tv_sec 0, tv_nsec 1. Однако предыдущий опыт учил не доверять разрешению только из одной структуры. Это верхний предел точности и реальности, как правило, намного сложнее.
2. Фактическое разрешение результата clock_gettime() на моей машине, с моей программой, с моей операционной системой, в один конкретный день и т.д., Оказывается, составляет 70 наносекунд для режима 0 и 1. 70 нс не так уж плохо, но, к сожалению, это нереально, как мы увидим в следующем пункте. Чтобы усложнить ситуацию, при использовании режимов 2 и 3 разрешение составляет 7 нс.
3. Длительность вызова clock_gettime() больше похожа на 1500 нс для режимов 0 и 1. Мне вообще не имеет смысла требовать разрешение 70 нс на время, если для получения значения требуется 20 раз разрешение.
4. Некоторые режимы clock_gettime() быстрее других. Режимы 2 и 3, очевидно, составляют половину времени настенных часов мод 0 и 1. Режимы 0 и 1 статистически неотличимы друг от друга. Режимы 2 и 3 намного быстрее, чем режимы 0 и 1, причем наиболее оптимальным является режим 3.
Прежде чем продолжить, я лучше определяю режимы: в каком режиме:?:
Режим 0 CLOCK_REALTIME//ссылка: http://linux.die.net/man/3/clock_gettime
Режим 1 CLOCK_MONOTONIC
Режим 2 CLOCK_PROCESS_CPUTIME_ID
Режим 3 CLOCK_THREAD_CPUTIME_ID
Заключение: Для меня нет смысла говорить о разрешении временных интервалов, если разрешение меньше времени, которое функция выполняет для получения временного интервала. Например, если мы используем режим 3, мы знаем, что функция завершается в течение 700 наносекунд в 99% случаев. И мы также знаем, что временной интервал, который мы получим, будет кратным 7 наносекундам. Таким образом, "разрешение" 7 наносекунд, составляет 1/100-й раз, чтобы сделать звонок, чтобы получить время. Я не вижу никакого значения в интервале изменения 7 наносекунд. Существует 3 разных ответа на вопрос о разрешении: 1 нс, 7 или 70 нс и, наконец, 700 или 1500 нс. Я одобряю последнюю цифру.
После того, как все сказано и сделано, если вы хотите измерить производительность какой-либо операции, вам нужно иметь в виду, сколько времени занимает вызов clock_gettime() - это 700 или 1500 нс. Нет смысла пытаться измерить то, что занимает 7 наносекунд, например. Ради аргументов, скажем, вы были готовы жить с 1% -ой ошибкой в своих выводах теста производительности. Если вы используете режим 3 (который, я думаю, буду использовать в моем проекте), вам нужно сказать, что интервал, который вам нужно измерить, должен быть 100 раз 700 наносекунд или 70 микросекунд. В противном случае ваши выводы будут иметь более 1% ошибки. Итак, продолжайте и оценивайте свой код интереса, но если ваше прошедшее время в интересующем коде меньше 70 микросекунд, тогда вам лучше пойти и запрограммировать код интереса достаточно, чтобы интервал был больше как 70 микросекунд или более.
Обоснование этих претензий и некоторых деталей:
Требование 3 в первую очередь. Это достаточно просто. Просто запустите clock_gettime() большое количество раз и запишите результаты в массиве, а затем обработайте результаты. Выполняйте обработку вне цикла, чтобы время между вызовами clock_gettime() было как можно короче.
Что все это значит? См. График. Например, для режима 0 вызов функции clock_gettime() занимает большую часть времени менее 1,5 микросекунд. Вы можете видеть, что режим 0 и режим 1 в основном одинаковы. Однако режимы 2 и 3 очень сильно отличаются от режимов 0 и 1 и немного отличаются друг от друга. Режимы 2 и 3 занимают примерно половину времени настенных часов для clock_gettime() по сравнению с режимами 0 и 1. Также обратите внимание, что режим 0 и 1 немного отличаются друг от друга - в отличие от режимов 2 и 3. Обратите внимание, что режим 0 и 1 отличаются на 70 наносекундов - это число, которое мы вернем в заявку № 2.
Прилагаемый график ограничен диапазоном до 2 микросекунд. В противном случае выбросы в данных предотвращают передачу графика предыдущей точкой. Что-то на графике не ясно, так это то, что выбросы для режимов 0 и 1 намного хуже, чем выбросы для режимов 2 и 3. Другими словами, не только средний и статистический "режим" (значение, которое происходит наиболее) и медиана (т.е. 50-й процентиль) для всех этих режимов различны, поэтому есть максимальные значения и их 99-й процентили.
График прилагается для 100 001 выборки для каждого из четырех режимов. Обратите внимание, что в графических тестах использовалась только маска процессора процессора 0. Независимо от того, пользовался ли я приемом процессора или нет, похоже, не имеет никакого значения для графика.
Претензия 2: Если вы внимательно посмотрите на образцы, собранные при подготовке графика, вы скоро заметите, что разница между различиями (то есть различиями 2-го порядка) относительно постоянна - примерно в 70 наносекунд (fore Modes 0 и 1 at наименее). Чтобы повторить этот эксперимент, собирайте 'n' образцы тактового времени, как и раньше. Затем вычислите различия между каждым образцом. Теперь отсортируйте различия в порядке (например, sort -g), а затем выведите отдельные уникальные отличия (например, uniq -c).
Например:
$ ./Exp03 -l 1001 -m 0 -k | sort -g | awk -f mergeTime2.awk | awk -f percentages.awk | sort -g
1.118e-06 8 8 0.8 0.8 // time,count,cumulative count, count%, cumulative count%
1.188e-06 17 25 1.7 2.5
1.257e-06 9 34 0.9 3.4
1.327e-06 570 604 57 60.4
1.397e-06 301 905 30.1 90.5
1.467e-06 53 958 5.3 95.8
1.537e-06 26 984 2.6 98.4
<snip>
Разница между длительностью в первом столбце часто составляет 7e-8 или 70 наносекунд. Это может стать более ясным путем обработки различий:
$ <as above> | awk -f differences.awk
7e-08
6.9e-08
7e-08
7e-08
7e-08
7e-08
6.9e-08
7e-08
2.1e-07 // 3 lots of 7e-08
<snip>
Обратите внимание, как все различия являются целыми кратными 70 наносекундам? Или, по крайней мере, с ошибкой округления 70 наносекунд.
Этот результат вполне может быть зависимым от аппаратного обеспечения, но на самом деле я не знаю, что ограничивает его до 70 наносекунд. Может быть, есть генератор 14,28 МГц где-то?
Обратите внимание, что на практике я использую гораздо большее количество образцов, таких как 100 000, а не 1000, как указано выше.
Соответствующий код (прилагается):
'Expo03' - это программа, которая называет clock_gettime() как можно быстрее. Обратите внимание, что типичное использование будет выглядеть примерно так:
./Expo03 -l 100001 -m 3
Это вызовет clock_gettime() 100 001 раз, чтобы мы могли вычислить 100 000 различий. Каждый вызов функции clock_gettime() в этом примере будет использовать режим 3.
MergeTime2.awk - полезная команда, которая является прославленной командой "uniq". Проблема заключается в том, что различия 2-го порядка часто встречаются в парах 69 и 1 наносекунды, а не 70 (для режима 0 и 1, по крайней мере), так как я довел вас до сих пор. Поскольку нет разницы в 68 наносекунд или разницы в 2 наносекунды, я объединил эти 69 и 1 наносекундные пары в одно число 70 наносекунд. Почему поведение 69/1 происходит вообще, интересно, но рассматривая их как два отдельных номера, в основном добавляли "шум" к анализу.
Прежде чем вы спросите, я повторил это упражнение, избегая плавающей запятой, и та же проблема все еще возникает. В результате tv_nsec как целое число имеет это поведение 69/1 (или 1/7 и 1/6), поэтому, пожалуйста, не предполагайте, что это артефакт, вызванный вычитанием с плавающей запятой.
Обратите внимание, что я уверен в этом "упрощении" для 70 нс и для небольших целых кратных 70 нс, но этот подход выглядит менее надежным для случая 7 нс, особенно когда вы получаете разности 2-го порядка в 10 раз больше 7 нс разрешение.
percentages.awk и difference.awk прилагаются в случае.
Остановить печать: я не могу опубликовать график, поскольку у меня нет "репутации не менее 10". Извините "это.
Роб Уотсон
21 ноября 2014 г.
Expo03.cpp
/* Like Exp02.cpp except that here I am experimenting with
modes other than CLOCK_REALTIME
RW 20 Nov 2014
*/
/* Added CPU affinity to see if that had any bearing on the results
RW 21 Nov 2014
*/
#include <iostream>
using namespace std;
#include <iomanip>
#include <stdlib.h> // getopts needs both of these
#include <unistd.h>
#include <errno.h> // errno
#include <string.h> // strerror()
#include <assert.h>
// #define MODE CLOCK_REALTIME
// #define MODE CLOCK_MONOTONIC
// #define MODE CLOCK_PROCESS_CPUTIME_ID
// #define MODE CLOCK_THREAD_CPUTIME_ID
int main(int argc, char ** argv)
{
int NumberOf = 1000;
int Mode = 0;
int Verbose = 0;
int c;
// l loops, m mode, h help, v verbose, k masK
int rc;
cpu_set_t mask;
int doMaskOperation = 0;
while ((c = getopt (argc, argv, "l:m:hkv")) != -1)
{
switch (c)
{
case 'l': // ell not one
NumberOf = atoi(optarg);
break;
case 'm':
Mode = atoi(optarg);
break;
case 'h':
cout << "Usage: <command> -l <int> -m <mode>" << endl
<< "where -l represents the number of loops and "
<< "-m represents the mode 0..3 inclusive" << endl
<< "0 is CLOCK_REALTIME" << endl
<< "1 CLOCK_MONOTONIC" << endl
<< "2 CLOCK_PROCESS_CPUTIME_ID" << endl
<< "3 CLOCK_THREAD_CPUTIME_ID" << endl;
break;
case 'v':
Verbose = 1;
break;
case 'k': // masK - sorry! Already using 'm'...
doMaskOperation = 1;
break;
case '?':
cerr << "XXX unimplemented! Sorry..." << endl;
break;
default:
abort();
}
}
if (doMaskOperation)
{
if (Verbose)
{
cout << "Setting CPU mask to CPU 0 only!" << endl;
}
CPU_ZERO(&mask);
CPU_SET(0,&mask);
assert((rc = sched_setaffinity(0,sizeof(mask),&mask))==0);
}
if (Verbose) {
cout << "Verbose: Mode in use: " << Mode << endl;
}
if (Verbose)
{
rc = sched_getaffinity(0,sizeof(mask),&mask);
// cout << "getaffinity rc is " << rc << endl;
// cout << "getaffinity mask is " << mask << endl;
int numOfCPUs = CPU_COUNT(&mask);
cout << "Number of CPU is " << numOfCPUs << endl;
for (int i=0;i<sizeof(mask);++i) // sizeof(mask) is 128 RW 21 Nov 2014
{
if (CPU_ISSET(i,&mask))
{
cout << "CPU " << i << " is set" << endl;
}
//cout << "CPU " << i
// << " is " << (CPU_ISSET(i,&mask) ? "set " : "not set ") << endl;
}
}
clockid_t cpuClockID;
int err = clock_getcpuclockid(0,&cpuClockID);
if (Verbose)
{
cout << "Verbose: clock_getcpuclockid(0) returned err " << err << endl;
cout << "Verbose: clock_getcpuclockid(0) returned cpuClockID "
<< cpuClockID << endl;
}
timespec timeNumber[NumberOf];
for (int i=0;i<NumberOf;++i)
{
err = clock_gettime(Mode, &timeNumber[i]);
if (err != 0) {
int errSave = errno;
cerr << "errno is " << errSave
<< " NumberOf is " << NumberOf << endl;
cerr << strerror(errSave) << endl;
cerr << "Aborting due to this error" << endl;
abort();
}
}
for (int i=0;i<NumberOf-1;++i)
{
cout << timeNumber[i+1].tv_sec - timeNumber[i].tv_sec
+ (timeNumber[i+1].tv_nsec - timeNumber[i].tv_nsec) / 1000000000.
<< endl;
}
return 0;
}
MergeTime2.awk
BEGIN {
PROCINFO["sorted_in"] = "@ind_num_asc"
}
{array[$0]++}
END {
lastX = -1;
first = 1;
for (x in array)
{
if (first) {
first = 0
lastX = x; lastCount = array[x];
} else {
delta = x - lastX;
if (delta < 2e-9) { # this is nasty floating point stuff!!
lastCount += array[x];
lastX = x
} else {
Cumulative += lastCount;
print lastX "\t" lastCount "\t" Cumulative
lastX = x;
lastCount = array[x];
}
}
}
print lastX "\t" lastCount "\t" Cumulative+lastCount
}
percentages.awk
{ # input is $1 a time interval $2 an observed frequency (i.e. count)
# $3 is a cumulative frequency
b[$1]=$2;
c[$1]=$3;
sum=sum+$2
}
END {
for (i in b) print i,b[i],c[i],(b[i]/sum)*100, (c[i]*100/sum);
}
differences.awk
NR==1 {
old=$1;next
}
{
print $1-old;
old=$1
}
NR==1 {
old=$1;next
}
{
print $1-old;
old=$1
}