язык C в Linux: вопросы начального уровня

Вопросы написания собственного программного кода (на любых языках)

Модератор: Olej

Аватара пользователя
Olej
Писатель
Сообщения: 21338
Зарегистрирован: 24 сен 2011, 14:22
Откуда: Харьков
Контактная информация:

Re: язык C в Linux: вопросы начального уровня

Непрочитанное сообщение Olej » 09 янв 2016, 09:33

Katerina писал(а): А скажите пожалуйста какой из этих способов предпочтительнее и почему? Я нашла такой пример. Отличие по времени выполнения(если компилировать без каких либо доп. ключей с учетом особенностей архитектуры процессора) практически на порядок.
Хороший пример...

Начиная с того, что он безграмотный ;-) :
Title Best way to get last char in unicode null terminated string
Потому как показанные там строки ну никак не могут быть Unicode, это сказано для красного словца из непонимания что такое Unicode вообще ... там как раз ASCII строки, char*, а вот если бы там были и именно Unicode строки, то картина перевернулась бы с точностью до наоборот...

А по существу:
- функция strlen(), как и всякая библиотечная функция C, включает в себя какие-то входные проверки, корректность допустимых данных и т.п.
- простое инкрементирование указателя может вполне быть быстрее ... на мизерных интервалах
- а то что "на порядок" ;-) - так это вы спишите на неточность измерений времени! Нельзя в системе корректно мерять временные интервалы с точностью, намного меньше системного тика, который в Linux обычно 1 мсек. ... а в некоторых WIndows может быть и 13 мс. (кстати, в какой это системе вы измеряли?)
- да и все эти соотношения времени выполнения поплывут при изменении уровня оптимизации компилятора ... и неизвестно в какую сторону, и при смене компилятора ...

Пример хорош тем, что он иллюстрирует, что:
- не стоит даже заморачиваться на экономию по-мелочам...
- а на микроинтервалах - это вообще "вилами по воде"...
- экономить надо на вычислительной сложности алгоритмов, вот том O(N) ... которое если O(10^N) - то никакое "улучшение производительности" вас не спасёт.

Аватара пользователя
Olej
Писатель
Сообщения: 21338
Зарегистрирован: 24 сен 2011, 14:22
Откуда: Харьков
Контактная информация:

Re: язык C в Linux: вопросы начального уровня

Непрочитанное сообщение Olej » 09 янв 2016, 09:46

Katerina писал(а):Я нашла такой пример. Отличие по времени выполнения(если компилировать без каких либо доп. ключей с учетом особенностей архитектуры процессора) практически на порядок.
Даже любопытно стало :lol:

Код: Выделить всё

olej@nvidia ~/2016_WORK/in.WORK/strtime $ gcc strtime.c -o strtime
olej@nvidia ~/2016_WORK/in.WORK/strtime $ ./strtime 
Elapsed time t=1.949000e-06 s
@
Elapsed time t=2.320000e-07 s
@
As you can Pointers-usage approach gives better perfomance
olej@nvidia ~/2016_WORK/in.WORK/strtime $ ./strtime 
Elapsed time t=2.769000e-06 s
@
Elapsed time t=4.790000e-07 s
@
As you can Pointers-usage approach gives better perfomance
Это 2 последовательных выполнения одного и того же показанного кода без всяких его изменений.
Разительная разница (в разы) уже может возникать из-за кэширования (попадания) или нет строки...
Но я вас уверяю, что и эти цифры времени - величины взятые практически с потолка (при таких ... микросекундных интервалах времени).

Но стоит элементарно поменять уровень оптимизации компилятора и ...

Код: Выделить всё

olej@nvidia ~/2016_WORK/in.WORK/strtime $ gcc -O3 strtime.c -o strtime
olej@nvidia ~/2016_WORK/in.WORK/strtime $ ./strtime 
Elapsed time t=3.132000e-06 s
@
Elapsed time t=3.520000e-07 s
@
As you can Pointers-usage approach gives better perfomance
И сразу абсолютные величины цифр поплыли ... потому что они мало о чём говорят...
Katerina писал(а): А скажите пожалуйста какой из этих способов предпочтительнее и почему?
Предпочтительнее без всякого сомнения способ функции strlen().
Потому, что он стандартный, POSIX-функция.
Читающие позже люди ваш код с фокусами с указателем - проклянут вас 10 раз на этих 3-х строчках кода! :lol:
Вложения
strtime.c
(2.15 КБ) 318 скачиваний

Katerina
Интересующийся
Сообщения: 3
Зарегистрирован: 09 янв 2016, 06:08
Контактная информация:

Re: язык C в Linux: вопросы начального уровня

Непрочитанное сообщение Katerina » 09 янв 2016, 22:34

Спасибо за ответ. Но, всё-таки, абсолютные величины может и поплыли, но (10ка)порядок сохранился. Мне приходит в голову что для корректного сравнения нужно просто увеличить число вызовов функции скажем до 10^6. и сравнить уже ни микро а мили секунды. И что-то мне подсказывает что разница будет существенна. Насчёт читабельности, согласна на 100%;)

Аватара пользователя
Olej
Писатель
Сообщения: 21338
Зарегистрирован: 24 сен 2011, 14:22
Откуда: Харьков
Контактная информация:

Re: язык C в Linux: вопросы начального уровня

Непрочитанное сообщение Olej » 10 янв 2016, 01:05

Katerina писал(а):Но, всё-таки, абсолютные величины может и поплыли, но (10ка)порядок сохранился. Мне приходит в голову что для корректного сравнения нужно просто увеличить число вызовов функции скажем до 10^6. и сравнить уже ни микро а мили секунды. И что-то мне подсказывает что разница будет существенна.
Разница будет, конечно. Потому что сравнение изначально поставлено некорректно:

Код: Выделить всё

char *get_last_char(char *str) {
        if (*str) {
                while (*str) ++str;
                --str;
        } else {
                str = 0;
        }
        return str;
}
Здесь - ничего, кроме простого инкремента указателя.

Код: Выделить всё

char * str_get_last_char(char str[])
{
    return str + strlen(str) - 1;
}
Здесь - 1-н дополнительный вызов функции + операция сложения.

Это просто некорректное сравнение.

Аватара пользователя
Olej
Писатель
Сообщения: 21338
Зарегистрирован: 24 сен 2011, 14:22
Откуда: Харьков
Контактная информация:

Re: язык C в Linux: вопросы начального уровня

Непрочитанное сообщение Olej » 10 янв 2016, 01:12

Katerina писал(а):Но, всё-таки, абсолютные величины может и поплыли, но (10ка)порядок сохранился. Мне приходит в голову что для корректного сравнения нужно просто увеличить число вызовов функции скажем до 10^6. и сравнить уже ни микро а мили секунды.
Меня заинтересовало их измерение временных интервалов (я такому не верю ;-) ).
И я на базе их приложения (чтобы не затронуть изменениями) сделал такое для сравнения:

Код: Выделить всё

#include "rdtsc.c"

// Use clock_gettime in linux
void get_monotonic_time(struct timespec *ts){
    clock_gettime(CLOCK_MONOTONIC, ts);
}

double get_elapsed_time(struct timespec *before, struct timespec *after){
    double diff_s  = after->tv_sec - before->tv_sec;
    double diff_ns = after->tv_nsec - before->tv_nsec;
    return diff_s + diff_ns*1e-9;
}

void loop( unsigned long long n ) {
   do;
   while( n-- != 0 );
}

int main( int argc, char *argv[] ) {
   if( 1 == argc ) {
      printf( "ошибка!\n"); return 1;
   }
   unsigned long long delay = atoll( argv[ 1 ] );

   struct timespec before, after;
   get_monotonic_time( &before );
   loop( delay );
   get_monotonic_time( &after );
   printf( "задержка %e сек.\n", get_elapsed_time( &before, &after) );

   time_t t1, t2;
   unsigned long long cf = 0, cs = 0, procf;
   time( &t1 );
   while( t1 == time( &t2 ) ) cf  = rdtsc(); // начало очередной секунды
   while( t2 == time( &t1 ) ) cs  = rdtsc(); // завершение этой секунды
   procf = cs - cf;
   printf( "тактовая частота процессора %.3f Ghz\n",
           (double)( procf ) / 1.E9 );
   cf  = rdtsc();
   loop( delay );
   cs  = rdtsc();
   printf( "задержка %e сек.\n", (double)( cs - cf ) / procf );
}
Где включаемый rdtsc.c:

Код: Выделить всё

static unsigned long long rdtsc( void ) {    // ассемблерные вставки  
   unsigned long long int x = 0;
#ifdef __i386__
   asm volatile ( "rdtsc" : "=A" (x) );      // команда RDTSC
#else
   asm volatile ( "rdtsc" );
   asm volatile ( "" : "=a" (x) );           // команда RDTSC
#endif
   return x;
}

void tproc( void ) {                         // частота процессора
   time_t t1, t2;
   unsigned long long cf = 0, cs = 0;
   printf( "GCC: инлайновые ассемблерные вставки\n" );
   time( &t1 );
   while( t1 == time( &t2 ) ) cf  = rdtsc(); // начало очередной секунды
   while( t2 == time( &t1 ) ) cs  = rdtsc(); // завершение этой секунды
   printf( "тактовая частота процессора %.3f Ghz\n",
           (double)( cs - cf ) / 1.E9 );
}
Только не компилируйте это с оптимизацией!

Код: Выделить всё

olej@nvidia ~/2016_WORK/in.WORK/strtime $ gcc -O0 cmptim.c -o cmptim
И имеем:

Код: Выделить всё

olej@nvidia ~/2016_WORK/in.WORK/strtime $ ./cmptim 10000
задержка 3.949400e-05 сек.
тактовая частота процессора 3.069 Ghz
задержка 2.799485e-05 сек.
olej@nvidia ~/2016_WORK/in.WORK/strtime $ ./cmptim 1000
задержка 3.421000e-06 сек.
тактовая частота процессора 3.069 Ghz
задержка 2.731473e-06 сек.
olej@nvidia ~/2016_WORK/in.WORK/strtime $ ./cmptim 100
задержка 5.140000e-07 сек.
тактовая частота процессора 3.069 Ghz
задержка 4.495977e-07 сек.
olej@nvidia ~/2016_WORK/in.WORK/strtime $ ./cmptim 10
задержка 4.980000e-07 сек.
тактовая частота процессора 3.069 Ghz
задержка 1.423728e-07 сек.
olej@nvidia ~/2016_WORK/in.WORK/strtime $ ./cmptim 2
задержка 4.650000e-07 сек.
тактовая частота процессора 3.069 Ghz
задержка 3.746645e-08 сек.
olej@nvidia ~/2016_WORK/in.WORK/strtime $ ./strtime
Elapsed time t=2.341000e-06 s
@
Elapsed time t=2.440000e-07 s
@
As you can Pointers-usage approach gives better perfomance
На малых интервалах их способ измерения измеряет просто "цифры с потолка"... всё те же ~4e-07 ... на все случаи жизни. :lol:

Аватара пользователя
Olej
Писатель
Сообщения: 21338
Зарегистрирован: 24 сен 2011, 14:22
Откуда: Харьков
Контактная информация:

Re: язык C в Linux: вопросы начального уровня

Непрочитанное сообщение Olej » 10 янв 2016, 10:45

Olej писал(а): На малых интервалах их способ измерения измеряет просто "цифры с потолка"... всё те же ~4e-07 ... на все случаи жизни. :lol:
Вообще, привычка доверять измерению времени выполнения, делая зарубки начала и конца, в многозадачной операционной системе порочна!
Она идёт ещё со времён MS-DOS, когда в системе крутилась одна единственная задача и её никто не мог оттеснять.

Но вот вам сравнение 2-х способов замера времени, показанные в предыдущем сообщении:

Код: Выделить всё

olej@nvidia ~/2016_WORK/in.WORK/strtime $ ./cmptim 1000000
задержка 4.775500e-03 сек.
тактовая частота процессора 3.069 Ghz
задержка 2.725302e-03 сек.
olej@nvidia ~/2016_WORK/in.WORK/strtime $ sudo nice -10 ./cmptim 1000000
задержка 2.752458e-03 сек.
тактовая частота процессора 3.069 Ghz
задержка 2.687955e-03 сек.
Что произошло?
В 1-м случае "их" способ (системный, через API) оказывается >100% "хуже", но просто потому, что задачу вытесняют соседи...
А во 2-м ... цифры программного измерения и независимого аппаратного счётчика почти совпадают ... но для этого:
- задаче нужен повышенный приоритет в системе (nice -9) ...
- для этого примитивнейшая задача должна запускаться от root ...
- и это при том, что при O(1) планировании задач (вытеснений) в Linux, у них то и приоритетов в полном смысле слова нет, а есть только ... "повышенное время захвата процессора" :lol:

Аватара пользователя
Olej
Писатель
Сообщения: 21338
Зарегистрирован: 24 сен 2011, 14:22
Откуда: Харьков
Контактная информация:

Re: язык C в Linux: вопросы начального уровня

Непрочитанное сообщение Olej » 10 янв 2016, 10:54

Olej писал(а):- и это при том, что при O(1) планировании задач (вытеснений) в Linux, у них то и приоритетов в полном смысле слова нет, а есть только ... "повышенное время захвата процессора" :lol:
Хотя да! :lol: - можно ещё воспользоваться realtime-режимом выполнения задачи и её realtime-приоритетом (опять же, с root и всеми удовольствиями):

Код: Выделить всё

olej@nvidia ~/2016_WORK/in.WORK/strtime $ sudo chrt -f 10 ./cmptim 1000
задержка 2.967000e-06 сек.
тактовая частота процессора 3.069 Ghz
задержка 2.907396e-06 сек.
olej@nvidia ~/2016_WORK/in.WORK/strtime $ sudo chrt -f 10 ./cmptim 100
задержка 5.180000e-07 сек.
тактовая частота процессора 3.069 Ghz
задержка 3.257959e-07 сек.
olej@nvidia ~/2016_WORK/in.WORK/strtime $ sudo chrt -f 10 ./cmptim 10
задержка 2.780000e-07 сек.
тактовая частота процессора 3.069 Ghz
задержка 1.423722e-07 сек.
И убедиться при этом, что любые замеры программным способом временных интервалов <1ms (1e-6, интервал системного тика) - это фикция и самообман: ... смотрим на цифры и любуемся ;-) , а что за ними стоит (и стоит ли что-то?) - не задумываемся! :cry:

Аватара пользователя
Olej
Писатель
Сообщения: 21338
Зарегистрирован: 24 сен 2011, 14:22
Откуда: Харьков
Контактная информация:

Re: язык C в Linux: вопросы начального уровня

Непрочитанное сообщение Olej » 10 янв 2016, 11:22

Olej писал(а):И убедиться при этом, что любые замеры программным способом временных интервалов <1ms (1e-6, интервал системного тика) - это фикция и самообман: ... смотрим на цифры и любуемся ;-) , а что за ними стоит (и стоит ли что-то?) - не задумываемся! :cry:
А вот что показывает аппаратный счётчик процессора RDTSC, если его ещё откалибровать на интервал 2-х следующих непосредственно друг за другом ("нет операции") вызовов rdtsc(), типа:

Код: Выделить всё

#define NUMB 10
unsigned calibr( int rep ) {
   uint32_t n, m, sum = 0;
   n = m = ( rep <= 0 ? NUMB : rep );
   while( n-- ) {
      uint64_t cf, cs;
      cf = rdtsc();
      cs = rdtsc();
      sum += (uint32_t)( cs - cf );
   }
   return sum / m;
}
Вот такие цифры:

Код: Выделить всё

olej@nvidia ~/2016_WORK/in.WORK/strtime $ sudo chrt -f 10 ./cmptim 100
задержка 5.730000e-07 сек.
тактовая частота процессора 3.069 Ghz
задержка 5.733994e-07 сек.
olej@nvidia ~/2016_WORK/in.WORK/strtime $ sudo chrt -f 10 ./cmptim 10
задержка 4.680000e-07 сек.
тактовая частота процессора 3.069 Ghz
задержка 7.493289e-08 сек.
olej@nvidia ~/2016_WORK/in.WORK/strtime $ sudo chrt -f 10 ./cmptim 3
задержка 4.940000e-07 сек.
тактовая частота процессора 3.069 Ghz
задержка 2.052510e-08 сек.
olej@nvidia ~/2016_WORK/in.WORK/strtime $ sudo chrt -f 10 ./cmptim 1
задержка 3.300000e-07 сек.
тактовая частота процессора 3.069 Ghz
задержка 1.694135e-08 сек.
Легко (пропорционально числу циклов) засекаются интервалы из наносекундного диапазона.
И на интервалах порядка 1/2 миллисекунды программный и аппаратный результаты практически сходятся.

Katerina
Интересующийся
Сообщения: 3
Зарегистрирован: 09 янв 2016, 06:08
Контактная информация:

Re: язык C в Linux: вопросы начального уровня

Непрочитанное сообщение Katerina » 10 янв 2016, 22:12

Спасибо, исчерпывающе:). Напишу им письмо;)

Аватара пользователя
Olej
Писатель
Сообщения: 21338
Зарегистрирован: 24 сен 2011, 14:22
Откуда: Харьков
Контактная информация:

Re: язык C в Linux: вопросы начального уровня

Непрочитанное сообщение Olej » 30 мар 2016, 11:56

Olej писал(а): Изменения C99 тоже заслуживают отдельного рассмотрения:
- они плохо описаны в "штатной" литературе по C (коротая, в лучшем случае, описывает C89);
- они все между собой не равнозначны по значению своему;
- они описаны в стандартах и статьях, но описаны сами изменения (списком), а не примеры применения самых существенных дополнений.

Сам стандарт C99 смотрим:

- глава C99 перевода : Полный справочник по C.
Черновик стандарта C11 (2011 года): Programming languages — C
N1570 Committee Draft — April 12, 2011
ISO/IEC 9899:201x
INTERNATIONAL STANDARD
©ISO/IEC
ISO/IEC 9899:201x

Ответить

Вернуться в «Программирование»

Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей и 14 гостей