Я пытаюсь измерить разницу во времени между двумя сигналами на параллельном порту, но сначала я узнал, насколько точна и точна моя измерительная система (двухъядерный процессор AMD Athlon ™ 64 X2 5200+ × 2) на SUSE 12,1 х64.
Поэтому после некоторого чтения я решил использовать clock_gettime (), сначала я получаю значение clock_getres (), используя этот код:
/*
* This program prints out the clock resolution.
*/
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
int main( void )
{
struct timespec res;
if ( clock_getres( CLOCK_REALTIME, &res) == -1 ) {
perror( "clock get resolution" );
return EXIT_FAILURE;
}
printf( "Resolution is %ld nano seconds.\n",
res.tv_nsec);
return EXIT_SUCCESS;
}
и было: 1 нано секунда. И я был так счастлив !!
Но вот моя проблема, когда я попытался проверить этот факт с помощью этого другого кода:
#include <iostream>
#include <time.h>
using namespace std;
timespec diff(timespec start, timespec end);
int main()
{
timespec time1, time2, time3,time4;
int temp;
time3.tv_sec=0;
time4.tv_nsec=000000001L;
clock_gettime(CLOCK_REALTIME, &time1);
NULL;
clock_gettime(CLOCK_REALTIME, &time2);
cout<<diff(time1,time2).tv_sec<<":"<<diff(time1,time2).tv_nsec<<endl;
return 0;
}
timespec diff(timespec start, timespec end)
{
timespec temp;
if ((end.tv_nsec-start.tv_nsec)<0) {
temp.tv_sec = end.tv_sec-start.tv_sec-1;
temp.tv_nsec = 1000000000+end.tv_nsec-start.tv_nsec;
} else {
temp.tv_sec = end.tv_sec-start.tv_sec;
temp.tv_nsec = end.tv_nsec-start.tv_nsec;
}
return temp;
}
этот вычисляет время между двумя вызовами clock_gettime, time3 и time4 объявлены, но не используются в этом примере, потому что я делал тесты с ними.
Выход в этом примере колеблется между 978 и 1467 нс. оба числа кратны 489, это заставляет меня думать, что 489 нс — мое РЕАЛЬНОЕ разрешение. далеко от 1 нс, полученных выше.
Мой вопрос: есть ли способ получить лучшие результаты? я что-то пропустил?
Мне действительно нужно разрешение по крайней мере 10 нс для моего проекта. Давай! GPS может получить лучшее разрешение, чем ПК?
Насколько я знаю, Linux работает на ПК как правило, не сможет дать вам точность таймера в диапазоне наносекунд. Это происходит главным образом из-за типа планировщика задач / процессов, используемого в ядре. Это как результат ядра, так и оборудования.
Если вам нужно время с разрешением наносекунды, я боюсь, что вам не повезло. Однако вы должны быть в состоянии получить микросекундное разрешение, которое должно быть достаточно для большинства сценариев, в том числе для приложения с параллельным портом.
Если вам требуется синхронизация в диапазоне наносекунд, чтобы быть точной с точностью до наносекунды, вам, скорее всего, понадобится специальное аппаратное решение; с действительно точный генератор (для сравнения, базовая тактовая частота большинства процессоров x86 находится в диапазоне мегагерц перед множителями)
Наконец, если вы хотите заменить функциональность осциллографа на вашем компьютере, он просто не будет работать за пределами относительно низкочастотных сигналов. Вы были бы намного лучше инвестировать в сферу — даже простой, портативный, подключаемый к компьютеру для отображения данных.
Я понимаю, что эта тема давно умерла, но хотел добавить свои выводы. Это длинный ответ, поэтому я поместил короткий ответ, и те, у кого есть терпение, могут пройтись по остальным. Не совсем ответ на вопрос составляет 700 нс или 1500 нс, в зависимости от того, какой режим clock_gettime () вы использовали. Длинный ответ намного сложнее.
Для справки, машина, на которой я работал, — это старый ноутбук, который никто не хотел. Это Acer Aspire 5720Z под управлением Ubuntu 14.041 LTS.
Аппаратное обеспечение:
RAM: 2.0 GiB // Вот как Ubuntu сообщает об этом в «Системных настройках» → «Подробно»
Процессор: двухъядерный процессор Intel® Pentium® 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 () на моей машине, с моей программой, с моей операционной системой, в один конкретный день и т. Д. В режиме 0 и 1. получается 70 наносекунд. 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 наносекунд (по крайней мере, в режимах 0 и 1). Чтобы повторить этот эксперимент, соберите ‘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 нс.
Проценты.awk и различия.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 masKint 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's 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
}
RDTSCP
на вашем AMD Athlon 64 X2 даст вам счетчик меток времени с разрешением зависит от ваших часов. Однако точность отличается от разрешения, вам нужно заблокировать сходство потоков и отключить прерывания (см. Маршрутизацию IRQ).
Это влечет за собой переход на ассемблер или для разработчиков Windows, использующих MSVC 2008 instrinsics.
RedHat с RHEL5 представили прокладки пользовательского пространства, которые заменяют gettimeofday
с высоким разрешением RDTSCP
звонки:
Кроме того, проверьте ваше оборудование, AMD 5200 имеет тактовую частоту 2,6 ГГц с интервалом 0,4 нс и стоимость gettimeofday
с RDTSCP
221 цикл, что в лучшем случае равно 88 нс.