С++ в относительно новых стандартах
Модератор: Olej
- Olej
- Писатель
- Сообщения: 21338
- Зарегистрирован: 24 сен 2011, 14:22
- Откуда: Харьков
- Контактная информация:
Re: С++ в относительно новых стандартах
Задача: заменить алгоритм for_each() STL (т.е. C++11/14/17, конечно ... но все же мы знаем откуда ноги растут? ) на операторный цикл.
Зачем?
Элементарно, Ватсон:
- есть огромный практический проект...
- который писали много лет (до 10-12) разные люди (числом до 100), многих (большинство) из которых на сегодня и в фирме нет, чтобы спросить...
- проект нужно поддерживать и развивать...
- а по телу проекта раскидано множество for_each() использующих функторы обрабатывающие элементы контейнеров STL...
И всё бы хорошо ... до тех пор, пока не требуются (для диагностики, отладки, расширения функциональности) получить информацию (новые данные) об итогах обработки, причём получить непосредственно из класса функтора, не залезая в само содержимое контейнеров.
Простейшая формулировка (модельной) задачи: есть вектор случайных положительных целых в диапазоне 0...100. Класс-функтор, пробегая вектор, обращает нечётные значения в отрицательные, меняет знак.
Вопрос: узнать по итогу выполнения операции сколько чисел было обращено? (не залезая с подсчётами в сам контейнер ... считаем что он недоступен ... или его элементы имеют весьма сложную структуру чтобы её ковырять).
Зачем?
Элементарно, Ватсон:
- есть огромный практический проект...
- который писали много лет (до 10-12) разные люди (числом до 100), многих (большинство) из которых на сегодня и в фирме нет, чтобы спросить...
- проект нужно поддерживать и развивать...
- а по телу проекта раскидано множество for_each() использующих функторы обрабатывающие элементы контейнеров STL...
И всё бы хорошо ... до тех пор, пока не требуются (для диагностики, отладки, расширения функциональности) получить информацию (новые данные) об итогах обработки, причём получить непосредственно из класса функтора, не залезая в само содержимое контейнеров.
Простейшая формулировка (модельной) задачи: есть вектор случайных положительных целых в диапазоне 0...100. Класс-функтор, пробегая вектор, обращает нечётные значения в отрицательные, меняет знак.
Вопрос: узнать по итогу выполнения операции сколько чисел было обращено? (не залезая с подсчётами в сам контейнер ... считаем что он недоступен ... или его элементы имеют весьма сложную структуру чтобы её ковырять).
- Olej
- Писатель
- Сообщения: 21338
- Зарегистрирован: 24 сен 2011, 14:22
- Откуда: Харьков
- Контактная информация:
Re: С++ в относительно новых стандартах
Вот как-то так:Olej писал(а): ↑08 янв 2021, 21:45Простейшая формулировка (модельной) задачи: есть вектор случайных положительных целых в диапазоне 0...100. Класс-функтор, пробегая вектор, обращает нечётные значения в отрицательные, меняет знак.
Вопрос: узнать по итогу выполнения операции сколько чисел было обращено? (не залезая с подсчётами в сам контейнер ... считаем что он недоступен ... или его элементы имеют весьма сложную структуру чтобы её ковырять).
Код: Выделить всё
#include <iostream> // std::cout
#include <functional> // std::bind
#include <random>
#include <vector>
#include <algorithm>
using namespace std;
class Actor
{
public:
Actor() : counter(0) {}
void operator()( int& i ) {
if (i%2)
{
i = -i;
counter += 1;
}
}
unsigned inline changed( void ) { return counter; }
private:
unsigned counter;
};
inline ostream& operator <<( ostream& out, const vector<int> & obj )
{
out << "< ";
for( auto& p: obj )
out << p << " ";
return out << ">";
}
int main( int argc, char *argv[] )
{
size_t limit = argc > 1 ? atol( argv[1] ) : 100; // vectors length
std::default_random_engine generator; // construct a trivial random generator engine
std::uniform_int_distribution<int> distribution(1,100); // https://www.cplusplus.com/reference/random/
auto dice = std::bind ( distribution, generator ); // function for generates number in the range 1..100
vector<int> v0( limit );
for ( auto& u: v0 )
u = dice();
if (limit < 50) cout << v0 << endl;
cout << "vector full size " << v0.size() << endl;
//---------------------------------------------------------
vector<int> v1 = v0;
for_each ( v1.begin(), v1.end(), Actor() );
if (limit < 50) cout << v1 << endl;
int changed = accumulate( v1.begin(), v1.end(), 0,
[](int acc, const int next)->int{ return acc + ( next < 0 ? 1 : 0 );} );
cout << "'for_each' - was changed " << changed << " elements" << endl;
//---------------------------------------------------------
vector<int> v2 = v0;
Actor act2;
for( int& i : v2 )
act2( i );
if (limit < 50) cout << v2 << endl;
cout << "'for' - was changed " << act2.changed() << " elements" << endl;
//---------------------------------------------------------
vector<int> v3 = v0;
Actor act3;
for ( auto i = v1.begin(); i != v1.end(); i++ )
act3( *i );
if (limit < 50) cout << v3 << endl;
cout << "'for iterator' - was changed " << act3.changed() << " elements" << endl;
//---------------------------------------------------------
vector<int> v4 = v0;
Actor act4;
for_each( v4.begin(), v4.end(), act4 );
if (limit < 50) cout << v4 << endl;
cout << "'functor' - was changed " << act4.changed() << " elements" << endl;
}
1 - v1, исходный алгоритм for_each() как он написан 10 лет назад...
Для проверки (только) подсчитывается число отрицательных (модифицированных) элементов, тспользуя обобщённый численный алгоритм STL accumulate, в качестве его исполнительной функции (чтобы не раздувать код определениями внешних функций) является лямбда-функция 2-х переменных, аккумулирующая отрицательные значения (всё это, кому вдруг интересно, описано в моей книжке, прикреплённой к заглавному сообщению на 1-й странице этой темы - AllSTL_14a.odt).
2- v2, цикл в стиле стандарта C++ 11/14.
3 - v3, более традиционный цикл записанный через итераторы.
4. - v4, это попытка выполнить алгоритм for_each() через внешне описанный класс функтора ... попытка неудачная, о чём ниже...
- Вложения
-
- func2for.cc
- (2.36 КБ) 52 скачивания
- Olej
- Писатель
- Сообщения: 21338
- Зарегистрирован: 24 сен 2011, 14:22
- Откуда: Харьков
- Контактная информация:
Re: С++ в относительно новых стандартах
Makefile традиционный:
Код: Выделить всё
TASK = func2for
CXX += -Wall -std=c++11 -O3
all: $(TASK)
%: %.cc
$(CXX) $< -o $@
Код: Выделить всё
olej@nvidia:~/2021_WORK/Zodiac_Systems/OWN_TEST.codes/func2for$ ./func2for 30
< 1 14 76 46 54 22 5 68 68 94 39 52 84 4 6 53 68 1 39 7 42 69 59 94 85 53 10 66 42 71 >
vector full size 30
< -1 14 76 46 54 22 -5 68 68 94 -39 52 84 4 6 -53 68 -1 -39 -7 42 -69 -59 94 -85 -53 10 66 42 -71 >
'for_each' - was changed 12 elements
< -1 14 76 46 54 22 -5 68 68 94 -39 52 84 4 6 -53 68 -1 -39 -7 42 -69 -59 94 -85 -53 10 66 42 -71 >
'for' - was changed 12 elements
< 1 14 76 46 54 22 5 68 68 94 39 52 84 4 6 53 68 1 39 7 42 69 59 94 85 53 10 66 42 71 >
'for iterator' - was changed 12 elements
< -1 14 76 46 54 22 -5 68 68 94 -39 52 84 4 6 -53 68 -1 -39 -7 42 -69 -59 94 -85 -53 10 66 42 -71 >
'functor' - was changed 0 elements
А вот 4-й - настолько характерен, что нужно сказать в 2 слова отдельно (он у меня 2 часа времени отнял ):
- можно предполагать, что определённый извне экземпляр class Actor, переданный в функтор, сохранит итоговые результаты выполнения...
- но он (правильно модифицируя переданный ему контейнер) после завершения говорит: "число изменений было 0"
- это потому, что в алгоритм передавался не экземпляр act4, а его копия, которая успешно отработала ...
- а по итогу мы диагностируем исходный оригинал, который естественно нигде не участвовал.
(добавьте в class Actor явный деструктор с cout и убедитесь в этом).
- Olej
- Писатель
- Сообщения: 21338
- Зарегистрирован: 24 сен 2011, 14:22
- Откуда: Харьков
- Контактная информация:
Re: С++ в относительно новых стандартах
Конечно, искусственным трюкачеством можно изменить класс функтора так, чтобы все его экземпляры работали с одним единственным счётчиком операций ... Для этого достаточно всего только чуть изменить описание класса Actor, и ничего больше не меняем:
Код: Выделить всё
class Actor
{
public:
Actor()
{
counter = 0;
}
void operator()( int& i ) {
if (i%2)
{
i = -i;
counter += 1;
}
}
unsigned inline changed( void ) { return counter; }
private:
static unsigned counter;
};
unsigned Actor::counter = 0;
Код: Выделить всё
olej@nvidia:~/2021_WORK/Zodiac_Systems/OWN_TEST.codes/func2for$ ./func2forS 30
< 1 14 76 46 54 22 5 68 68 94 39 52 84 4 6 53 68 1 39 7 42 69 59 94 85 53 10 66 42 71 >
vector full size 30
< -1 14 76 46 54 22 -5 68 68 94 -39 52 84 4 6 -53 68 -1 -39 -7 42 -69 -59 94 -85 -53 10 66 42 -71 >
'for_each' - was changed 12 elements
< -1 14 76 46 54 22 -5 68 68 94 -39 52 84 4 6 -53 68 -1 -39 -7 42 -69 -59 94 -85 -53 10 66 42 -71 >
'for' - was changed 12 elements
< 1 14 76 46 54 22 5 68 68 94 39 52 84 4 6 53 68 1 39 7 42 69 59 94 85 53 10 66 42 71 >
'for iterator' - was changed 12 elements
< -1 14 76 46 54 22 -5 68 68 94 -39 52 84 4 6 -53 68 -1 -39 -7 42 -69 -59 94 -85 -53 10 66 42 -71 >
'functor' - was changed 12 elements
- Вложения
-
- func2forS.cc
- (2.4 КБ) 56 скачиваний
- Olej
- Писатель
- Сообщения: 21338
- Зарегистрирован: 24 сен 2011, 14:22
- Откуда: Харьков
- Контактная информация:
Re: С++ в относительно новых стандартах
Ещё более показательный такой, наверное, пример:
Код: Выделить всё
#include <iostream> // std::cout
#include <functional> // std::bind
#include <random>
#include <vector>
#include <cstring>
#include <algorithm>
using namespace std;
class Histograf
{
public:
Histograf( unsigned step) : step(step), counter(0) {}
unsigned operator()(int val, bool last = false)
{
counter++;
if (last)
cout << "last element in the series: " << val << endl;
return val / step;
}
unsigned inline changed( void ) { return counter; }
private:
unsigned step;
unsigned counter;
};
size_t steps; // number of clasters
void show( unsigned change, unsigned arr[] )
{
cout << "there was " << change << " calls : ";
for (size_t i = 0; i < steps; i++)
cout << "| " << arr[ i ] << " ";
cout << " |" << endl << endl;
}
int main( int argc, char *argv[] )
{
size_t range = 100; // [0 ... range) - range of value
if (argc > 1)
{
if (0 == strcmp( argv[1], "-h" ) )
{
cout << "usage: " << argv[0] << " [<range>] [steps] [[+/-]<length>]" << endl;
return 0;
}
else if (atol( argv[1] ) > 0)
range = atol( argv[1] );
}
steps = (argc > 2 && atol( argv[2] ) > 0 ) ? atol( argv[2] ) : 4;
long limit = 100; // number of digits in series
limit = (argc > 3 && atol( argv[3] ) != 0 ) ? atol( argv[3] ) : 100;
bool print = ( limit < 0 );
limit = abs( limit );
cout << "size of sequence: " << limit << endl;
std::default_random_engine generator; // construct a trivial random generator engine
std::uniform_int_distribution<int> distribution( 0, range - 1 ); // https://www.cplusplus.com/reference/random/
auto dice = std::bind ( distribution, generator ); // function for generates number in the range 0..range
dice(); // first alwais 0
vector<int> v( limit );
for ( auto &u: v )
u = dice();
if (print) { //debuging output
cout << "< ";
for( auto& i: v ) cout << i << " ";
cout << ">" << endl << endl;
}
unsigned hist[ steps ]; // Varianle-Length Array (ISO/IEC 9899:1999)
//---------------------------------------------------------
for (size_t i = 0; i < steps; i++ ) hist[ i ] = 0;
Histograf hi1( range / steps );
for ( int& i : v )
hist[ hi1( i ) ]++;
show( hi1.changed(), hist );
//---------------------------------------------------------
for (size_t i = 0; i < steps; i++ ) hist[ i ] = 0;
Histograf hi2( range / steps );
for ( auto i = v.begin(); i != v.end(); i++ )
hist[ hi2( *i, i + 1 == v.end() ) ]++;
show( hi2.changed(), hist );
//---------------------------------------------------------
for (size_t i = 0; i < steps; i++ ) hist[ i ] = 0;
Histograf hi3( range / steps );
for_each( v.begin(), v.end(), hi3 );
show( hi3.changed(), hist );
//---------------------------------------------------------
}
Код: Выделить всё
[olej@xenix func2for]$ ./func2for2 100 10 15
size of sequence: 15
there was 15 calls : | 3 | 1 | 1 | 1 | 1 | 3 | 2 | 1 | 1 | 1 |
last element in the series: 52
there was 15 calls : | 3 | 1 | 1 | 1 | 1 | 3 | 2 | 1 | 1 | 1 |
there was 0 calls : | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
- во 2-м случае, кроме полностью возможностей предыдущего, мы можем передать внутрь функтора дополнительную информацию о конкретно обрабатываемом элементе, например, его порядковый номер, или то что он последний, как в показанном примере;
- в 3-м варианте - всеми любимый алгоритм for_each() - мы не можем ни то ни другое: при выполнении внутри цикла используется копия объявленного объекта.
- Вложения
-
- func2for.tgz
- (39.1 КБ) 56 скачиваний
- Olej
- Писатель
- Сообщения: 21338
- Зарегистрирован: 24 сен 2011, 14:22
- Откуда: Харьков
- Контактная информация:
Re: С++ в относительно новых стандартах
Код: Выделить всё
Variable-Length Arrays
Локальные массивы переменной длины разрешены в языке C с 1999 г. (стандарт C99, он же ISO/IEC 9899:1999).
Но в C++ VLA легализовали только со стандарта C++14 (в GCC с C++11).
О чём довольно много понаписано:
GCC и Variable-Length Arrays
МАССИВЫ ПЕРЕМЕННОЙ ДЛИНЫ В C И C ++25 сентября 2013
...
Где-то читал, что следующий Стандарт C++14, возможно, позволит выполнять такой трюк. Но настоящий говорит, что размер массива должен быть constant expression. Это еще раз уменьшает совместимость с C99, где VLAs давно доступны.
01.01.2020
...
Но стандарт C ++ (до C ++ 11 ) не поддерживает массивы переменного размера. Стандарт C ++ 11 упоминает размер массива как константное выражение. См. (См. 8.3.4 на стр. 179 из N3337 ). Таким образом, приведенная выше программа не может быть допустимой программой C ++. Программа может работать в компиляторе GCC, потому что компилятор GCC предоставляет расширение для их поддержки.
В качестве примечания, в последнем C ++ 14 (см. 8.3.4 на стр. 184 из N3690 ) размер массива упоминается как простое выражение (не константное выражение).
- Olej
- Писатель
- Сообщения: 21338
- Зарегистрирован: 24 сен 2011, 14:22
- Откуда: Харьков
- Контактная информация:
Re: С++ в относительно новых стандартах
Выглядит это, например, так:
Код: Выделить всё
#include <iostream> // std::cout
#include <functional> // std::bind
#include <random>
using namespace std;
int main( int argc, char *argv[] ) {
if (argc != 2) {
cout << "usage: " << argv[0] << "length" << endl;
return 1;
}
unsigned num = atol( argv[1] ) > 0 ?
atol( argv[1] ) : 0;
if (0 == num ) {
cout << "wrong number" << endl;
return 2;
}
int arr_num[ num ]; // Variable-Length Arrays (ISO/IEC 9899:1999)
std::default_random_engine generator; // construct a trivial random generator engine
std::uniform_int_distribution<int> distribution(-100,100); // https://www.cplusplus.com/reference/random/
auto dice = std::bind ( distribution, generator ); // function for generates number in the range
dice();
for ( auto &a: arr_num ) a = dice();
size_t num2 = sizeof(arr_num) / sizeof(arr_num[0]);
for (size_t n = 0; n < num2; n++)
cout << (0 == n ? "< " : " ") << arr_num[ n ] << (n == num2 - 1 ? " >" : " " );
cout << endl;
bool arr_log[ num2 ];
for (size_t n = 0; n < num2; n++)
arr_log[ n ] = arr_num[ n ] > 0;
for (size_t n = 0; n < num2; n++)
cout << (0 == n ? "< " : " ") << (arr_log[ n ] > 0 ? "+" : "-" ) << (n == num2 - 1 ? " >" : " " );
cout << endl;
}
P.S. Вспомните, что вас всегда учили, что в C/C++ массивы могут иметь размер ... сначала, годов с 70-х до конца 90-х - это было требование константного размера ... а после - статического размера, вычисляемого и определённого на этапе компиляции!
Если компиляцию делать с опцией -pedantic, то компилятор до сиз пор ругается независимо от указываемого ему стандарта:
Код: Выделить всё
[olej@xenix VLA]$ make
g++ -Wall -pedantic -std=c++11 vla.cc -o vla
vla.cc: В функции «int main(int, char**)»:
vla.cc:20:6: предупреждение: ISO C++ запрещает массив переменного размера «arr_num» [-Wvla]
20 | int arr_num[ num ]; // Variable-Length Arrays (ISO/IEC 9899:1999)
| ^~~~~~~
vla.cc:30:7: предупреждение: ISO C++ запрещает массив переменного размера «arr_log» [-Wvla]
30 | bool arr_log[ num2 ];
| ^~~~~~~
Код: Выделить всё
[olej@xenix VLA]$ make
g++ -Wall -pedantic -std=c++14 vla.cc -o vla
vla.cc: В функции «int main(int, char**)»:
vla.cc:19:6: предупреждение: ISO C++ запрещает массив переменного размера «arr_num» [-Wvla]
19 | int arr_num[ num ]; // Variable-Length Arrays (ISO/IEC 9899:1999)
| ^~~~~~~
vla.cc:29:7: предупреждение: ISO C++ запрещает массив переменного размера «arr_log» [-Wvla]
29 | bool arr_log[ num2 ];
| ^~~~~~~
Код: Выделить всё
[olej@xenix VLA]$ make
g++ -Wall -pedantic -std=c++17 vla.cc -o vla
vla.cc: В функции «int main(int, char**)»:
vla.cc:19:6: предупреждение: ISO C++ запрещает массив переменного размера «arr_num» [-Wvla]
19 | int arr_num[ num ]; // Variable-Length Arrays (ISO/IEC 9899:1999)
| ^~~~~~~
vla.cc:29:7: предупреждение: ISO C++ запрещает массив переменного размера «arr_log» [-Wvla]
29 | bool arr_log[ num2 ];
| ^~~~~~~
Код: Выделить всё
[olej@xenix VLA]$ ./vla 25
< -74 51 -8 7 -56 -91 36 36 87 -23 4 67 -94 -90 6 34 -99 -23 -87 -17 38 18 87 70 5 >
< - + - + - - + + + - + + - - + + - - - - + + + + + >
- Вложения
-
- VLA.tgz
- (1.21 КБ) 53 скачивания
- Olej
- Писатель
- Сообщения: 21338
- Зарегистрирован: 24 сен 2011, 14:22
- Откуда: Харьков
- Контактная информация:
Re: С++ в относительно новых стандартах
Понадобилось ввести короткие задержки (миллисекундного диапазона) во избежание гонок в многопоточном проекте...
Что там у нас есть в C++? ... такого что использует POSIX API (язык C, Linux), и что не использует....
Точнее даже, мне нужна не просто (только) функция аналог sleep() только миллисекундного диапазона, а 2 функции - пассивного и активного (подобно тому как работает spin-блокировка) ожидания!
Потому что они могут влиять на поведение многопоточных приложений радикально разным образом.
P.S. О чём детально на примерах разобрано моей книге в эл. виде: Параллелизм, конкурентность, многопроцессорность в Linux
Что там у нас есть в C++? ... такого что использует POSIX API (язык C, Linux), и что не использует....
Точнее даже, мне нужна не просто (только) функция аналог sleep() только миллисекундного диапазона, а 2 функции - пассивного и активного (подобно тому как работает spin-блокировка) ожидания!
Потому что они могут влиять на поведение многопоточных приложений радикально разным образом.
P.S. О чём детально на примерах разобрано моей книге в эл. виде: Параллелизм, конкурентность, многопроцессорность в Linux
- Olej
- Писатель
- Сообщения: 21338
- Зарегистрирован: 24 сен 2011, 14:22
- Откуда: Харьков
- Контактная информация:
Re: С++ в относительно новых стандартах
Итак, варианты пассивной задержки (с вытеснением потока):
Код: Выделить всё
#include <iostream> // std::cout
#include <vector>
#include <chrono>
#include <thread>
#include <math.h>
using namespace std;
const char* wait_p1( unsigned ms )
{
std::this_thread::sleep_for(std::chrono::milliseconds(ms));
// using namespace std::chrono_literals; // ns, us, ms, s, h, etc.
// sleep_for(10ns);
return __FUNCTION__;
}
#include <sys/time.h>
const char* wait_p2( unsigned ms )
{
/* struct timespec {
time_t tv_sec; // seconds
long tv_nsec; // nanoseconds (0...999999999)
};
*/
struct timespec pause = { 0, 0 };
unsigned long nsec = ms * 1000000L;
pause.tv_sec = nsec / 1000000000L;
pause.tv_nsec = nsec % 1000000000L;
nanosleep( &pause, NULL );
return __FUNCTION__;
}
#include <unistd.h>
const char* wait_p3( unsigned ms )
{
usleep( ms * 1000L );
return __FUNCTION__;
}
/* struct timeval {
time_t tv_sec; // seconds
suseconds_t tv_usec; // microseconds
};
*/
int main( int argc, char *argv[] ) {
if (argc != 2) {
cout << "usage: " << argv[0] << " <delay (sec.)>" << endl;
return 1;
}
if ( !(atof( argv[1] ) > 0.0 ) ) {
cout << "wrong number" << endl;
return 2;
}
unsigned mdelay = std::floor( atof( argv[1] ) * 1000. );
cout << "the requested delay is " << mdelay << " milliseconds" << endl;
typedef const char* (*wait_func_t)( unsigned );
vector<wait_func_t> variants = { wait_p1, wait_p2, wait_p3 };
for( auto &wf : variants )
{
struct timeval tb, tf;
gettimeofday( &tb, NULL ); // временная метка начала
const char *title = wf( mdelay );
gettimeofday( &tf, NULL ); // временная метка конца
timersub( &tf, &tb, &tf );
cout << title << " : delay was "
<< double( tf.tv_sec * 1000000L + tf.tv_usec ) / 1000.
<< " milliseconds" << endl;
}
}
Код: Выделить всё
g++ -Wall -pedantic -std=c++11 waitp.cc -o waitp
Код: Выделить всё
olej@nvme:~/2021/OWN_TEST.codes/wait$ ./waitp .1
the requested delay is 100 milliseconds
wait_p1 : delay was 100.128 milliseconds
wait_p2 : delay was 100.117 milliseconds
wait_p3 : delay was 100.129 milliseconds
olej@nvme:~/2021/OWN_TEST.codes/wait$ ./waitp .01
the requested delay is 10 milliseconds
wait_p1 : delay was 10.296 milliseconds
wait_p2 : delay was 10.112 milliseconds
wait_p3 : delay was 10.137 milliseconds
olej@nvme:~/2021/OWN_TEST.codes/wait$ ./waitp .5
the requested delay is 500 milliseconds
wait_p1 : delay was 500.127 milliseconds
wait_p2 : delay was 500.113 milliseconds
wait_p3 : delay was 500.112 milliseconds
- Вложения
-
- waitp.cc
- (1.8 КБ) 54 скачивания
- Olej
- Писатель
- Сообщения: 21338
- Зарегистрирован: 24 сен 2011, 14:22
- Откуда: Харьков
- Контактная информация:
Re: С++ в относительно новых стандартах
С вариантом активной паузы, не возвращая управление системе (ядру), без перераспределения потоков - здесь всё хуже...
Здесь нужно а). калибровать по числу выполнений тестовой функции, б). при вызове задержки вычислить по калиброванному значению число требуемых вызовов, в). осуществить нужное число вызовов в цикле.
А поскольку я это пишу для C++ а не для C, то желательно всё это нутро упаковать в класс...
Тут пока у меня 1 вариант:
Код: Выделить всё
#include <iostream> // std::cout
#include <vector>
#include <sys/time.h>
#include <math.h>
using namespace std;
class ActivDelay
{
public:
ActivDelay() {
c2m = cycle2msec();
}
void delay( unsigned ms ) { // активная задержка в миллисекундах
for (ulong i = 0; i < ms; i++ )
for (ulong j = 0; j < c2m; j++ )
one_cycle();
}
private:
ulong c2m; // откалиброванное число вызовов рабочей функции на 1 миллисекунду
static void one_cycle( void ) // функция одного цикла активной работы
{
int i, n = 0;
for( i = 0; i < 50; i++ ) // цикл произвольной активной работы
n = ( n + 37 ) % 31;
}
static const ulong cycle2msec( void ) // калибровка числа вызовов за msec.
{
ulong n = 0;
struct timeval tb, tf;
do gettimeofday( &tb, NULL ); // начальная отметка
while ( 0 != tb.tv_usec );
do
{
one_cycle();
n++;
gettimeofday( &tf, NULL ); // текущая отметка
} while ( tf.tv_usec <= 100000L );
n /= 100;
return n;
}
};
static ActivDelay delay;
const char* wait_a1( unsigned ms )
{
delay.delay( ms );
return __FUNCTION__;
}
int main( int argc, char *argv[] ) {
if (argc != 2) {
cout << "usage: " << argv[0] << " <delay (sec.)>" << endl;
return 1;
}
if ( !(atof( argv[1] ) > 0.0 ) ) {
cout << "wrong number" << endl;
return 2;
}
unsigned mdelay = std::floor( atof( argv[1] ) * 1000. );
cout << "the requested delay is " << mdelay << " milliseconds" << endl;
typedef const char* (*wait_func_t)( unsigned );
vector<wait_func_t> variants = { wait_a1 };
for( auto &wf : variants )
{
struct timeval tb, tf;
gettimeofday( &tb, NULL ); // временная метка начала
const char *title = wf( mdelay );
gettimeofday( &tf, NULL ); // временная метка конца
timersub( &tf, &tb, &tf );
cout << title << " : delay was "
<< double( tf.tv_sec * 1000000L + tf.tv_usec ) / 1000.
<< " milliseconds" << endl;
}
}
Именно поэтому объект (имплементацию) нельзя помещать внутрь (локально) функции паузы wait_a1() - иначе размещение и инициализация объекта ActivDelay будет входить в результат каждой паузы ... (я на это час времени потерял ).
Здесь, как и должно ожидать, не такая устойчивость результатов, но достаточная (тут могут вмешиваться и эффекты кэширования процессора, и смена частоты работы процессора и мн. другое):
Код: Выделить всё
olej@nvme:~/2021/OWN_TEST.codes/wait$ ./waita .1
the requested delay is 100 milliseconds
wait_a1 : delay was 90.336 milliseconds
olej@nvme:~/2021/OWN_TEST.codes/wait$ ./waita .2
the requested delay is 200 milliseconds
wait_a1 : delay was 175.153 milliseconds
olej@nvme:~/2021/OWN_TEST.codes/wait$ ./waita .3
the requested delay is 300 milliseconds
wait_a1 : delay was 269.103 milliseconds
olej@nvme:~/2021/OWN_TEST.codes/wait$ ./waita .5
the requested delay is 500 milliseconds
wait_a1 : delay was 449.711 milliseconds
olej@nvme:~/2021/OWN_TEST.codes/wait$ ./waita 1
the requested delay is 1000 milliseconds
wait_a1 : delay was 900.953 milliseconds
- Вложения
-
- waita.cc
- (2.2 КБ) 49 скачиваний
Кто сейчас на конференции
Сейчас этот форум просматривают: нет зарегистрированных пользователей и 0 гостей