Ползущий массив (сравнение скоростей)
-
- professor
- Сообщения: 3408
- Зарегистрирован: 31 июл 2011, 23:05
- Награды: 2
- Версия LabVIEW: 12-18
- Благодарил (а): 49 раз
- Поблагодарили: 176 раз
- Контактная информация:
Re: Ползущий массив (сравнение скоростей)
Остапа несло...
Не могу понять, почему индексированный while практически не уступает по скорости for-у.
Предположил, что в первом тесте цикл сразу знает количество итераций, повторил тест с имитацией неизвестности - результат тот же, скорость не существенно ниже, чем у For...
У кого-нибудь есть идеи объяснения этого?
Не могу понять, почему индексированный while практически не уступает по скорости for-у.
Предположил, что в первом тесте цикл сразу знает количество итераций, повторил тест с имитацией неизвестности - результат тот же, скорость не существенно ниже, чем у For...
У кого-нибудь есть идеи объяснения этого?
-
- leader
- Сообщения: 526
- Зарегистрирован: 28 фев 2010, 18:04
- Версия LabVIEW: LV2018
- Благодарил (а): 10 раз
- Поблагодарили: 18 раз
- Контактная информация:
Re: Ползущий массив (сравнение скоростей)
В определении начала кольца допустил неточность. Следует делать так:
- Вложения
-
- ДвойнойМассив.vi
- (10.83 КБ) 55 скачиваний
-
Jakob Brontfeyn
- expert
- Сообщения: 1729
- Зарегистрирован: 28 фев 2008, 11:01
- Награды: 6
- Благодарил (а): 1 раз
- Контактная информация:
Re: Ползущий массив (сравнение скоростей)
Я для сложных экспериментов в лабораториях, чтобы долго не заморачиваться,
применяю метод с использованием Chathistory-массива
очень удобно, потому что, если надо, можно получить сразу несколько
синхронно ползучих массивов в одном чате. При этом сам чат-индикатор на панели,
в погоне за скоростью, есть возможность скрыть.
В тестовом примере сделал так, что массив из 10000 элементов
ползет 10000 раз, то бишь полностью обновляется.
Как получается, вам судить, смотрите два примера,
для одного и для трех ползучих массивов.
применяю метод с использованием Chathistory-массива
очень удобно, потому что, если надо, можно получить сразу несколько
синхронно ползучих массивов в одном чате. При этом сам чат-индикатор на панели,
в погоне за скоростью, есть возможность скрыть.
В тестовом примере сделал так, что массив из 10000 элементов
ползет 10000 раз, то бишь полностью обновляется.
Как получается, вам судить, смотрите два примера,
для одного и для трех ползучих массивов.
- Вложения
-
- polzet_odin_omassiv.vi
- (35.97 КБ) 53 скачивания
-
- polzet_tri_massiva.vi
- (45.02 КБ) 60 скачиваний
-
- professor
- Сообщения: 3408
- Зарегистрирован: 31 июл 2011, 23:05
- Награды: 2
- Версия LabVIEW: 12-18
- Благодарил (а): 49 раз
- Поблагодарили: 176 раз
- Контактная информация:
Re: Ползущий массив (сравнение скоростей)
вариант интересный, но скоростью совершенно не блещет. Во-первых, обращение к свойству элемента - очень медленная процедура, да ещё и на UI завязанная. А во-вторых, для получения фрагмента данных каждый раз придётся вытаскивать всю историю, чтобы потом кусок вырезатьJakob Brontfeyn писал(а): ↑13 дек 2021, 07:53 применяю метод с использованием Chathistory-массива
очень удобно, потому что, если надо, можно получить сразу несколько
синхронно ползучих массивов в одном чате.
-
- professor
- Сообщения: 3408
- Зарегистрирован: 31 июл 2011, 23:05
- Награды: 2
- Версия LabVIEW: 12-18
- Благодарил (а): 49 раз
- Поблагодарили: 176 раз
- Контактная информация:
Re: Ползущий массив (сравнение скоростей)
Всё не давал покоя этот метод, решил его тоже протестировать. Тестировал отдельно, потому что очевиден проигрыш по скорости. Но вот удобство выборки соблазняло.Chupakabra писал(а): ↑10 дек 2021, 15:27 Есть еще такая штука In-Memory SQLite. По скорости ничего не скажу, нужно пробовать. Но можно временные ряды хранить в памяти и извлекать со всеми плюшками sql.
Главное опасение было - прожорливость метода - будет ли SQLite нормально освобождать память при удалении старых данных.
Тест: буфер на 10 часов, каждую секунду в базу добавляется новое значение, тут же читается 1 час истории из середины. И раз в 10 минут удаляется всё, что старше 10 часов.
Результаты порадовали. Явной потери памяти не наблюдается, в скорости вполне приличные - 10-12 мс на итерацию (запись + выборка). Полусекундные всплески - это удаление истории. График занятой памяти Исходный код: Итого: при не строго тактированных данных метод вполне оправдан простотой выборки по времени.
-
- professor
- Сообщения: 3408
- Зарегистрирован: 31 июл 2011, 23:05
- Награды: 2
- Версия LabVIEW: 12-18
- Благодарил (а): 49 раз
- Поблагодарили: 176 раз
- Контактная информация:
Re: Ползущий массив (сравнение скоростей)
Озадачился вопросом утечек памяти.
Короткие (по часу) тесты не очень убедительны, прогнал 10-часовые.
Тест гонял на виртуальной машине, которая работает на основной машине, чтобы работать не мешало, а тесту никто память не засорял. Результаты опять же не однозначные. Графики - скользящее среднее 50 шт
1) поворот-замена. Тут память скорее не утекает 2) удалить-вставить
тут непонятно, что за всплеск во второй половине, но в целом скорее утечек нет 3) удалить-прибавить (build array)
Вполне убедительно, что память не исчезает 4) split-build
Тоже непонятный всплеск, но два плато.
Короткие (по часу) тесты не очень убедительны, прогнал 10-часовые.
Тест гонял на виртуальной машине, которая работает на основной машине, чтобы работать не мешало, а тесту никто память не засорял. Результаты опять же не однозначные. Графики - скользящее среднее 50 шт
1) поворот-замена. Тут память скорее не утекает 2) удалить-вставить
тут непонятно, что за всплеск во второй половине, но в целом скорее утечек нет 3) удалить-прибавить (build array)
Вполне убедительно, что память не исчезает 4) split-build
Тоже непонятный всплеск, но два плато.
-
- professor
- Сообщения: 3408
- Зарегистрирован: 31 июл 2011, 23:05
- Награды: 2
- Версия LabVIEW: 12-18
- Благодарил (а): 49 раз
- Поблагодарили: 176 раз
- Контактная информация:
Re: Ползущий массив (сравнение скоростей)
Итого.
Надоело гонять тесты, пора паузу взять
rotate+replace и delete-build самые быстрые по скорости, и они же не вызывают опасений в утечках памяти. Но второй потребляет память как будто равномернее.
Пожалуй, на этом пока всё.
Надоело гонять тесты, пора паузу взять
rotate+replace и delete-build самые быстрые по скорости, и они же не вызывают опасений в утечках памяти. Но второй потребляет память как будто равномернее.
Пожалуй, на этом пока всё.
-
Jakob Brontfeyn
- expert
- Сообщения: 1729
- Зарегистрирован: 28 фев 2008, 11:01
- Награды: 6
- Благодарил (а): 1 раз
- Контактная информация:
Re: Ползущий массив (сравнение скоростей)
Зачем же "кусок" вырезать, задайте длинну истории чата равной длинне "куска".Artem.spb писал(а): ↑13 дек 2021, 14:05вариант интересный, но скоростью совершенно не блещет. Во-первых, обращение к свойству элемента - очень медленная процедура, да ещё и на UI завязанная. А во-вторых, для получения фрагмента данных каждый раз придётся вытаскивать всю историю, чтобы потом кусок вырезатьJakob Brontfeyn писал(а): ↑13 дек 2021, 07:53 применяю метод с использованием Chathistory-массива
очень удобно, потому что, если надо, можно получить сразу несколько
синхронно ползучих массивов в одном чате.
-
- professor
- Сообщения: 3408
- Зарегистрирован: 31 июл 2011, 23:05
- Награды: 2
- Версия LabVIEW: 12-18
- Благодарил (а): 49 раз
- Поблагодарили: 176 раз
- Контактная информация:
Re: Ползущий массив (сравнение скоростей)
Задача изначально ставилась: хранить буфер "10 часов" с возможностью просматривать произвольный фрагмент (когда пользователь перематывает фрагмент). Конечно, можно сделать удобную навигацию по графику, тоже рабочий вариант.Jakob Brontfeyn писал(а): ↑14 дек 2021, 23:49 Зачем же "кусок" вырезать, задайте длинну истории чата равной длинне "куска".
-
dadreamer
- professor
- Сообщения: 3926
- Зарегистрирован: 17 фев 2013, 16:33
- Награды: 4
- Версия LabVIEW: 2.5 — 2022
- Благодарил (а): 11 раз
- Поблагодарили: 127 раз
- Контактная информация:
Re: Ползущий массив (сравнение скоростей)
Я имел ввиду что-то такое: Что тут происходит: получаем указатель на "провод" массива, первым MoveBlock'ом записываем данные из этого массива (начиная со 2-го элемента) в него же (с "головы"), вторым MoveBlock'ом дописываем "хвост". Можно дополнительно отключить генерацию обёрток, чтобы не плодить лишние копии (но как показало дальнейшее тестирование, выигрыш от этого мизерный, т.к. копия массива итак не создаётся нигде, а копиями отдельных чисел можно пренебречь).
Дальше я встроил этот код в первый сниппет, получились такие результаты. И, если немного приблизить, будет лучше заметно. Время выполнения MoveBlock такое же как у delete-build (3) и rotate-replace (5). Думаю, и по смыслу примерно то же и получается: сдвиг - фактически, две записи, замена - одна запись. Отключение обработки ошибок и отключение обёрток на результаты практически не влияет.
Вот :
Мне кажется, здесь виноваты два основных фактора. Во-первых, за пару десятилетий железо стало более продвинутым и отставание одной конструкции от другой почти не ощущается. Да, методички/бенчмарки уже устарели и по-хорошему их нужно подгонять уже под нынешние реалии. Во-вторых, в обоих случаях выполняется одно и то же, и там просто нечему тормозить (не считая операций с массивами; вероятно, в этом и была причина отставания While когда-то).Artem.spb писал(а): ↑11 дек 2021, 14:55Не могу понять, почему индексированный while практически не уступает по скорости for-у.
Предположил, что в первом тесте цикл сразу знает количество итераций, повторил тест с имитацией неизвестности - результат тот же, скорость не существенно ниже, чем у For...
У кого-нибудь есть идеи объяснения этого?
Копаться в ассемблере мне стало сильно лень, потому я просто сгенерил сишные портянки с помощью C Generator'а.
Для случая код такой:
Код: Выделить всё
for (heap->l_For_Loop_i = 0;(heap->l_For_Loop_i < heap->l_Size) && !gAppStop && !gLastError; (heap->l_For_Loop_i)++) {
{
/**/
/* Random Number (0-1) */
/**/
heap->n_Random_Number__0_1__number__0 = ((float32)SysRandom(0)/(float32)sysRandomMax);
*(float64 *)LptunNthElem(heap->a_Random_Number__0_1__number__0) = heap->n_Random_Number__0_1__number__0;
}
} /* end for */
Код: Выделить всё
heap->l_While_Loop_i_1 = 0;
do {
{
/**/
/* Random Number (0-1) */
/**/
heap->n_Random_Number__0_1__number__0 = ((float32)SysRandom(0)/(float32)sysRandomMax);
heap->l_Size = Size__A8;
/**/
/* Greater Or Equal? */
/**/
heap->b_Greater_Or_Equal__x____y_ = (heap->l_While_Loop_i_1 >= heap->l_Size);
if (!PDAArrAddElToLpTunArr( (VoidHand)(&heap->a_Random_Number__0_1__number__0), (VoidHand)(&heap->n_Random_Number__0_1__number__0) )){
CGenErr();
}
}
(heap->l_While_Loop_i_1)++;
}
while(!heap->b_Greater_Or_Equal__x____y_ && !gAppStop && !gLastError);
upd: На самом деле есть ещё одно значительное отличие. Хотя в обоих случаях массив выделяется перед циклом с помощью функции PDAArrCreateLpTunArr, в случае For Loop массив выделяется с заранее определённым размером, но в случае While Loop массив выделяется пустым! Отсюда разное поведение при добавлении очередного элемента в массив в теле цикла: макрос LptunNthElem просто записывает элемент (число) в уже выделенную ячейку массива, тогда как функция PDAArrAddElToLpTunArr предварительно расширяет массив и только потом записывает элемент (число) в ячейку массива.
Последний раз редактировалось dadreamer 16 май 2023, 11:17, всего редактировалось 1 раз.
-
- professor
- Сообщения: 3408
- Зарегистрирован: 31 июл 2011, 23:05
- Награды: 2
- Версия LabVIEW: 12-18
- Благодарил (а): 49 раз
- Поблагодарили: 176 раз
- Контактная информация:
Re: Ползущий массив (сравнение скоростей)
Именно что многие рекомендации базируются на устаревших взглядах на "мир".dadreamer писал(а): ↑30 дек 2021, 13:52Мне кажется, здесь виноваты два основных фактора. Во-первых, за пару десятилетий железо стало более продвинутым и отставание одной конструкции от другой почти не ощущается. Да, методички/бенчмарки уже устарели и по-хорошему их нужно подгонять уже под нынешние реалии. Во-вторых, в обоих случаях выполняется одно и то же, и там просто нечему тормозить (не считая операций с массивами; вероятно, в этом и была причина отставания While когда-то).Artem.spb писал(а): ↑11 дек 2021, 14:55Не могу понять, почему индексированный while практически не уступает по скорости for-у.
Предположил, что в первом тесте цикл сразу знает количество итераций, повторил тест с имитацией неизвестности - результат тот же, скорость не существенно ниже, чем у For...
У кого-нибудь есть идеи объяснения этого?
Даже идея проверить MoveBlock. Помню в нулевых не получалось "запихать" математику в слабый PXI (просмотр массива по точкам и простейшие сравнения), пришлось мне на С dll сделать и эту самую математику нестандартно проводить. Сейчас бы я не стал таким заниматься, компиляторы сильно продвинулись с тех пор.
А ещё в таких тестах у меня всегда подозрения, делает ли машина то, что я прошу? Если провод из цикла никуда дальше не идёт, то компилятор может решить, что и заполнять этот массив не нужно.
По поводу заполнения в циклах на мой взгляд разница должна быть. Во втором случае На каждой итерации приходится выделять новый массив, да ещё и копировать данные туда
UPD: вот как в моём представлении (и в устаревших методичках) описывается автоиндекс в циклах While. И тут разница скоростей в 5 раз.
-
dadreamer
- professor
- Сообщения: 3926
- Зарегистрирован: 17 фев 2013, 16:33
- Награды: 4
- Версия LabVIEW: 2.5 — 2022
- Благодарил (а): 11 раз
- Поблагодарили: 127 раз
- Контактная информация:
Re: Ползущий массив (сравнение скоростей)
К сожалению, может, такова цена высокоуровневого программирования. Я на рамку просто завожу или на однокадровый Sequence. Наверно все, кто с знаком не понаслышке, использовали хоть раз такие трюки. Мне встречались всякие странности, например вызов DLL в SubVI выполнялся намного быстрее, чем в основном , или "чудеса" с Formula Node, когда она по-разному в разных условиях выполняется. Думаю, это из-за того, что не всегда генерируется одинаковое промежуточное представление (IR) и получаются разные графы, из которых LLVM уже производит машинные данные. Но это только гипотеза.
Я это опустил, но в обоих случаях массив создаётся перед циклом:
Код: Выделить всё
if (!(heap->a_Random_Number__0_1__number__0 = PDAArrCreateLpTunArr( (ArrDimSize)heap->b_While_Loop_End, doubleDataType, uCharDataType, (ArrDimSize)0 ))){
CGenErr();
После выхода из цикла выполняется преобразование из "туннельного" массива в 1D (что бы это ни значило):
Код: Выделить всё
heap->a_Random_Number__0_1__number__0 = PDAArrCreate1DArrFromLpTunArr( heap->a_Random_Number__0_1__number__0 );
Последний раз редактировалось dadreamer 16 май 2023, 11:23, всего редактировалось 1 раз.
-
- professor
- Сообщения: 3408
- Зарегистрирован: 31 июл 2011, 23:05
- Награды: 2
- Версия LabVIEW: 12-18
- Благодарил (а): 49 раз
- Поблагодарили: 176 раз
- Контактная информация:
Re: Ползущий массив (сравнение скоростей)
В случае с While в теории (и в общем случае) система не знает, сколько будет элементов. Например, я бегаю по файлу и собираю интересующие меня элементы.
Или, например, выборка из базы: В таких случаях не очень понятно, как компилятор строит код и как выделяет память под массив.
В предыдущем посте ("upd") я написал своё предположение на этот счёт. Но судя по скорости оно не верно.
-
dadreamer
- professor
- Сообщения: 3926
- Зарегистрирован: 17 фев 2013, 16:33
- Награды: 4
- Версия LabVIEW: 2.5 — 2022
- Благодарил (а): 11 раз
- Поблагодарили: 127 раз
- Контактная информация:
Re: Ползущий массив (сравнение скоростей)
Динамически, по одному элементу (ячейке) докидывает. Ну, как в Дельфях раньше создавали пустой динамический массив и постоянно его расширяли, чтобы впихнуть новые данные. Хотя, не знаю, чем отличается "туннельный" массив от обычного, может, как раз скоростью работы с ним.
Надо листинг сделать, тогда понятнее будет. Посмотрю чуть позже.
Для случая с Build Array вместо туннеля:
Код: Выделить всё
heap->a_Build_Array_appended_array_SR_1 = heap->a_Constant;
heap->l_While_Loop_i_1 = 0;
do {
{
heap->a_Constant_SR = heap->a_Build_Array_appended_array_SR_1;
/**/
/* Random Number (0-1) */
/**/
heap->n_Random_Number__0_1__number__0 = ((float32)SysRandom(0)/(float32)sysRandomMax);
/* Build array */
{
ArrDimSize i;
ArrDimSize dimSize=0;
heap->a_Build_Array_appended_array = PDAArrNewEmptyWithNDims( doubleDataType, (ArrDimSize)1 );
if (!heap->a_Build_Array_appended_array){
CGenErr();
}
dimSize += PDAArrNthDim(heap->a_Constant_SR, (ArrDimSize)0);
dimSize += 1;
PDAArrSetDim(heap->a_Build_Array_appended_array, (ArrDimSize)0, dimSize);
if (!PDAArrAllocData(&heap->a_Build_Array_appended_array)){
CGenErr();
}
i=0;
if (!PDAArrAdd(heap->a_Build_Array_appended_array, i, heap->a_Constant_SR)) {
CGenErr();
}
i += PDAArrNthDim(heap->a_Constant_SR, (ArrDimSize)0);
PDAArrFree(heap->a_Constant_SR);
if (!PDAArrSetData(heap->a_Build_Array_appended_array, i, &heap->n_Random_Number__0_1__number__0, doubleDataType)) {
CGenErr();
}
i++;
}
heap->l_size = size__6E;
/**/
/* Greater Or Equal? */
/**/
heap->b_Greater_Or_Equal__x____y_ = (heap->l_While_Loop_i_1 >= heap->l_size);
heap->a_Build_Array_appended_array_SR_1 = heap->a_Build_Array_appended_array;
}
(heap->l_While_Loop_i_1)++;
}
while(!heap->b_Greater_Or_Equal__x____y_ && !gAppStop && !gLastError);
Короче, если всё на самом деле было так, как описано в старых методичках, получается, что компилятор действительно "допилили" до вменяемого состояния в новых версиях LV.
-
- professor
- Сообщения: 3408
- Зарегистрирован: 31 июл 2011, 23:05
- Награды: 2
- Версия LabVIEW: 12-18
- Благодарил (а): 49 раз
- Поблагодарили: 176 раз
- Контактная информация:
Re: Ползущий массив (сравнение скоростей)
Провёл оч странный тест с полной имитацией неизвестности. Конечно, накладные расходы гигантские, но сравнить производительность можно.
В целом For немного быстрее, но не существенно. А билдить массив руками всё же расточительно.
В целом For немного быстрее, но не существенно. А билдить массив руками всё же расточительно.
-
- Похожие темы
- Ответы
- Просмотры
- Последнее сообщение
-
- 6 Ответы
- 1063 Просмотры
-
Последнее сообщение JohnChaban