С++ в относительно новых стандартах

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

Модератор: Olej

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

Re: С++ в относительно новых стандартах

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

Задача: заменить алгоритм for_each() STL (т.е. C++11/14/17, конечно ... но все же мы знаем откуда ноги растут? :lol: ) на операторный цикл.
Зачем?
Элементарно, Ватсон:
- есть огромный практический проект...
- который писали много лет (до 10-12) разные люди (числом до 100), многих (большинство) из которых на сегодня и в фирме нет, чтобы спросить...
- проект нужно поддерживать и развивать...
- а по телу проекта раскидано множество for_each() использующих функторы обрабатывающие элементы контейнеров STL...
И всё бы хорошо ... до тех пор, пока не требуются (для диагностики, отладки, расширения функциональности) получить информацию (новые данные) об итогах обработки, причём получить непосредственно из класса функтора, не залезая в само содержимое контейнеров.

Простейшая формулировка (модельной) задачи: есть вектор случайных положительных целых в диапазоне 0...100. Класс-функтор, пробегая вектор, обращает нечётные значения в отрицательные, меняет знак.
Вопрос: узнать по итогу выполнения операции сколько чисел было обращено? (не залезая с подсчётами в сам контейнер ... считаем что он недоступен ... или его элементы имеют весьма сложную структуру чтобы её ковырять).

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

Re: С++ в относительно новых стандартах

Непрочитанное сообщение Olej » 08 янв 2021, 21:52

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;
}
Там 4 альтернативные части, делающие практически то же самое:

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 КБ) 49 скачиваний

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

Re: С++ в относительно новых стандартах

Непрочитанное сообщение Olej » 08 янв 2021, 21:55

Olej писал(а):
08 янв 2021, 21:52
Вот как-то так:
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
Как легко видеть, первые 3 теста отрабатывают успешно и аналогично...
Olej писал(а):
08 янв 2021, 21:52
v4, это попытка выполнить алгоритм for_each() через внешне описанный класс функтора ... попытка неудачная
А вот 4-й - настолько характерен, что нужно сказать в 2 слова отдельно (он у меня 2 часа времени отнял :lol: ):
- можно предполагать, что определённый извне экземпляр class Actor, переданный в функтор, сохранит итоговые результаты выполнения...
- но он (правильно модифицируя переданный ему контейнер) после завершения говорит: "число изменений было 0" :-o
- это потому, что в алгоритм передавался не экземпляр act4, а его копия, которая успешно отработала ...
- а по итогу мы диагностируем исходный оригинал, который естественно нигде не участвовал.
(добавьте в class Actor явный деструктор с cout и убедитесь в этом).

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

Re: С++ в относительно новых стандартах

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

Olej писал(а):
08 янв 2021, 21:55
по итогу мы диагностируем исходный оригинал, который естественно нигде не участвовал.
Конечно, искусственным трюкачеством можно изменить класс функтора так, чтобы все его экземпляры работали с одним единственным счётчиком операций ... Для этого достаточно всего только чуть изменить описание класса 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 КБ) 53 скачивания

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

Re: С++ в относительно новых стандартах

Непрочитанное сообщение Olej » 26 янв 2021, 19:25

Olej писал(а):
08 янв 2021, 21:45
Задача: заменить алгоритм for_each() STL (т.е. C++11/14/17, конечно ... но все же мы знаем откуда ноги растут? ) на операторный цикл.
Зачем?
Ещё более показательный такой, наверное, пример:

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

#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 );
//---------------------------------------------------------
}
Здесь limit случайных чисел в диапазоне [0...range) раскладываются, по величине в steps кластеров.

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

[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  |
- в 1-м варианте мы имеем возможность возвратить итоговое значение из функтора вовне и накапливать его во внешней переменной;
- во 2-м случае, кроме полностью возможностей предыдущего, мы можем передать внутрь функтора дополнительную информацию о конкретно обрабатываемом элементе, например, его порядковый номер, или то что он последний, как в показанном примере;
- в 3-м варианте - всеми любимый алгоритм for_each() - мы не можем ни то ни другое: при выполнении внутри цикла используется копия объявленного объекта.
Вложения
func2for.tgz
(39.1 КБ) 53 скачивания

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

Re: С++ в относительно новых стандартах

Непрочитанное сообщение Olej » 26 янв 2021, 20:21

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

Variable-Length Arrays
- VLA
Локальные массивы переменной длины разрешены в языке C с 1999 г. (стандарт C99, он же ISO/IEC 9899:1999).
Но в C++ VLA легализовали только со стандарта C++14 (в GCC с C++11).
О чём довольно много понаписано:
GCC и Variable-Length Arrays
25 сентября 2013
...
Где-то читал, что следующий Стандарт C++14, возможно, позволит выполнять такой трюк. Но настоящий говорит, что размер массива должен быть constant expression. Это еще раз уменьшает совместимость с C99, где VLAs давно доступны.
МАССИВЫ ПЕРЕМЕННОЙ ДЛИНЫ В C И C ++
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: С++ в относительно новых стандартах

Непрочитанное сообщение Olej » 26 янв 2021, 20:29

Olej писал(а):
26 янв 2021, 20:21
Но в C++ VLA легализовали только со стандарта C++14 (в GCC с C++11).
Выглядит это, например, так:

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

#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;
}
Здесь создаётся не только числовой массив arr_num под размер вводимый пользователем при запуске программы (который потом заполняется случайными числами в диапазоне [-100...+100]), но затем и основывающийся на его (arr_num) размере - логически массив arr_log.
P.S. Вспомните, что вас всегда учили, что в C/C++ массивы могут иметь размер ... сначала, годов с 70-х до конца 90-х - это было требование константного размера ... а после - статического размера, вычисляемого и определённого на этапе компиляции! :-o

Если компиляцию делать с опцией -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 КБ) 50 скачиваний

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

Re: С++ в относительно новых стандартах

Непрочитанное сообщение Olej » 26 янв 2021, 20:36

Понадобилось ввести короткие задержки (миллисекундного диапазона) во избежание гонок в многопоточном проекте...
Что там у нас есть в C++? ... такого что использует POSIX API (язык C, Linux), и что не использует....

Точнее даже, мне нужна не просто (только) функция аналог sleep() только миллисекундного диапазона, а 2 функции - пассивного и активного (подобно тому как работает spin-блокировка) ожидания!
Потому что они могут влиять на поведение многопоточных приложений радикально разным образом.

P.S. О чём детально на примерах разобрано моей книге в эл. виде: Параллелизм, конкурентность, многопроцессорность в Linux
среда, 24 сентября 2014 г.
Номер последней редакции: 05
Объём (страниц): 97
Размер файла текста: 185153
Размер архива кодов: 314749
Дата размещения: 23 сентябра 2014

Скачать эту редакцию текст и архив примеров к нему можно:
Google: текст и архив

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

Re: С++ в относительно новых стандартах

Непрочитанное сообщение Olej » 27 янв 2021, 02:57

Olej писал(а):
26 янв 2021, 20:36
Точнее даже, мне нужна не просто (только) функция аналог sleep() только миллисекундного диапазона, а 2 функции - пассивного и активного (подобно тому как работает spin-блокировка) ожидания!
Итак, варианты пассивной задержки (с вытеснением потока):

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

#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
Это только несколько ... там ещё из boost есть интересные варианты, я ещё, может, дополню ... но они все показывают и так высокую повторяемость:

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

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 КБ) 50 скачиваний

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

Re: С++ в относительно новых стандартах

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

Olej писал(а):
27 янв 2021, 02:57
Итак, варианты пассивной задержки (с вытеснением потока):
С вариантом активной паузы, не возвращая управление системе (ядру), без перераспределения потоков - здесь всё хуже...
Здесь нужно а). калибровать по числу выполнений тестовой функции, б). при вызове задержки вычислить по калиброванному значению число требуемых вызовов, в). осуществить нужное число вызовов в цикле.

А поскольку я это пишу для 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 будет входить в результат каждой паузы ... (я на это час времени потерял :-o ).
Здесь, как и должно ожидать, не такая устойчивость результатов, но достаточная (тут могут вмешиваться и эффекты кэширования процессора, и смена частоты работы процессора и мн. другое):

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

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 КБ) 46 скачиваний

Ответить

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

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

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