Здравствуйте, pilgrim_, Вы писали: _>Тут s_count=80'000.
Тут s_count=8'000'000; https://godbolt.org/z/1vhEzfE4c
Добавил "многопоточность", а она ни на что не влияет . Что я делаю не так?:
Здравствуйте, B0FEE664, Вы писали:
BFE>Здравствуйте, pilgrim_, Вы писали:
BFE>Добавил "многопоточность", а она ни на что не влияет . Что я делаю не так?:
#define PARALLEL
#include <execution>
видимо, для MSVC и clang тоже надо что-то магическое дефайнить.
Здравствуйте, rg45, Вы писали:
R>Ну и в-третьих, а чем, в данном случае, хуже "лобовое" решение с использованием обычного цикла?
R>
R> for(int x : numbers_in) if (!(x % 2)) numbers_out.push_back(x / 2);
R>
R>А, ну и по сути вопроса — "почему так сложно?" — а ХЗ, почему. Мозги у людей как-то странно сложены.
Следует добавить, что третье решение — правильное, потому что решает задачу при максимальной простоте и читаемости, а не демонстрирует возможности с++ биб-ки. Это решение понятно и смузеру, и старпёру с базовыми знаниями по программированию. Остальные решение это оверинжениринг.
Здравствуйте, B0FEE664, Вы писали:
BFE>Тут s_count=8'000'000; BFE>https://godbolt.org/z/1vhEzfE4c BFE>Добавил "многопоточность", а она ни на что не влияет . Что я делаю не так?:
А почему ты ожидал ускорения от использования "многопоточки"? В частности для par (copy_if/transform+par_unseq), код копирования/фильтрации однопоточный, что фильтрация ,что трансформация примитивны, и если их убрать (копировать всё, возвращать оригинальное значение) на скорость это почти не влияет, но в твоём случае (для par) пробег по памяти в 1.5 раза больше. Хз что тут меряем, да ещё и на godbold (к слову доступно всего 2 потока, в FAQ написано про 3).
==
А твой код был однопоточным, поэтому быстрым
По умолчанию алгоритмы execution используют дефолтный, последовательный backend (по asm листингу видно), надо добавить т.н. parallel backend, пока только один доступный — Intel TBB (-ltbb, но на godbold надо выбрать из предустановленных).
Убрал ranges, т.к. завёлся только gcc 11.2 с c++17 (как в примере для TBB на godbold), trunk правда тоже работает. Свежие версии gcc требуют отладочные либы TBB, которых нет в наличии.
Не, для gcc надо либу явно линковать (-ltbb, какой-то хак gcc — по используемой либе определяет соотв. макрос, анализируемый уже в коде на предмет доступности TBB), на godbold выбирается из предустановленных в GUI. В транке gcc видно начало поддержки OpenMP как ещё одного бэкенда, но не доделано, видать в бранче каком развивается.
Добавил свой алгоритм, сделанный на основании ranges::copy_if
( modified_copy_if: В предикат передаётся исходное значение, а в результат пишется проекция )
( В стандартном copy_if: проекция передаётся в предикат, но в результат пишет исходное )
Здравствуйте, Sm0ke, Вы писали:
S>Добавил свой алгоритм, сделанный на основании ranges::copy_if S>( modified_copy_if: В предикат передаётся исходное значение, а в результат пишется проекция ) S>( В стандартном copy_if: проекция передаётся в предикат, но в результат пишет исходное )
S>Добавил custom div
S>Исходник: https://godbolt.org/z/K5hKP5fnv
Похоже, теперь я чего-то не понимаю. Я с самого начала думал, что здесь проходят соревнования, у кого короче.
Здравствуйте, rg45, Вы писали:
S>>Исходник: https://godbolt.org/z/K5hKP5fnv
R>Похоже, теперь я чего-то не понимаю. Я с самого начала думал, что здесь проходят соревнования, у кого короче.
Все соревнования всегда сводятся к тому, у кого длиннее
Здравствуйте, pilgrim_, Вы писали:
BFE>>Добавил "многопоточность", а она ни на что не влияет . Что я делаю не так?:
_>А почему ты ожидал ускорения от использования "многопоточки"?
Я ничего не читал про возможности параллельного выполнения, кроме описания на cppreference.com из которого мало что следует. Чисто теоретически все операции над элементами в std::transform могут быть выполнены параллельно и за одно и тоже время. Т.е. вот этот вызов:
в идеале может быть выполнен за за время равное одному вызову [](int n){ return n/2;}.
Думаю, что на практике такая операция будет выполнятся над кусками.
Понятно, что многое зависит от аппаратной части и архитектуры процессора, за которыми я не слежу. Просто хотелось увидеть, как оно сейчас на практике. Похоже, что никак.
Здравствуйте, B0FEE664, Вы писали:
BFE>Допустим, есть вектор с целыми числами, и требуется поделить все его числа на два. Те, которые не делятся, нужно просто выкинуть.
Если "в одну строчку", то удобно использовать ренжи.
Заодно позволит не создавать лишние буфера в процессе многостадийной обработки:
#include <iostream>
#include <ranges>
#include <boost/phoenix.hpp>
int main() {
using namespace std;
using views::filter;
using views::transform;
using boost::phoenix::placeholders::_1;
//===========================================================auto const v1 = { 1, 2, 3, 4, 5, 3 };
auto rng = v1 | filter(_1 % 2 == 0) | transform(_1 / 2);
auto const vec = rng | ranges::to<vector>();
//===========================================================
ostream_iterator<int> const out { cout, " " };
ranges::copy(v1, out), cout << std::endl;
ranges::copy(rng, out), cout << std::endl;
ranges::copy(vec, out), cout << std::endl;
return 0;
}
ranges::to — из 23-го стандарта
Согласно cppref поддержка есть в:
* clang 17 (которого пока нет на готболт)
* msvc 19.34 (и он там есть)
Вы используете using-и, чтобы укоротить основную строчку, однако они сами добавляют отдельные строчки (хз честно это или нет).
V>Если "в одну строчку", то удобно использовать ренжи.
Там у вас две строки, а если в одну, то for всё равно короче)
Здравствуйте, Sm0ke, Вы писали:
S>Вы используете using-и, чтобы укоротить основную строчку, однако они сами добавляют отдельные строчки (хз честно это или нет).
Однократно на некий файл, допустим, с несколькими функциями манипуляций данными.
V>>Если "в одну строчку", то удобно использовать ренжи. S>Там у вас две строки
В одну, в одну ))
Копирование в вектор было необязательным, поэтому вынес в отдельную строку, но можно было приписать последней операцией в строке.
Т.е. суть в том, что на каждом этапе стоит оперировать концепцией ренжей и только ими, сохранять в переменную можно тоже просто ренж — специально это продемонстрировал, показав ranges::copy(rng, out) без сохранения в промежуточный контейнер.
А уже конечный ренж копировать или в хранилище, или передать в output_iterator, или еще куда-нить для последующей обработки.
S>а если в одну, то for всё равно короче)
На некоторых задачах да, но стремление к декларативности операций порой рулит, особенно если задача не диктует битовыжимание.
Опять же, ценность порой представляют не готовые трансформаторы данных, а вся инфрастуктура вокруг них, где ты относительно дешево можешь описывать свои прикладные повторно-используемые трансформаторы. Собсно, ради такого подхода к решению задач весь огород и нагородили.
S>ranges::to — из 23-го стандарта
Есть такое.
Можно юзать ренжи из буста (первоначальный вариант был бустовый), бо в стандарт еще не все плюшки переехали.
В смысле ренжей, атомик и потоков (пусть даже в версии 4) и прочих emplace, optional и т.д. Boost, конечно, оказал чудовищное влияние на язык и его стандарты.
Красавцы, чо! ))
S>Однако для повторного использования забиндить цепочку изменений в переменную rng — конечно удобнее.
Концепт ренжей прямо в стандарте описан как тип, который имеет операции begin(rng) и end(rng), при том что дефолтная реализация пытается взять rgn.begin() и rng.end(), т.е. подхватывает имеющиеся стандартные контейнеры и пользовательские типы, оформленные в STL-like манере, экономя на стороне прикладного кода избыточный однообразный код, где ты оперировал парой итераторов явно, замусоривая исходник. Казалось бы — на поверности... Но когда-то сделано не было в STL из-за отсутствия полноценной поддержки частичной специализации шаблонов... Но тут, как грится, лучше поздно, чем никогда. ))
Здравствуйте, ArtDenis, Вы писали:
AD>Как тут уже сказали, перед замером надо "прогревать" процессор. ОС выставит для него максимальную частоту и переключит твой поток на мощное ядро. Затем, один и тот же тест надо прогонять много раз, каждый раз считать время, а затем брать например, медианное значение.
Если измеряется быстродействие алгоритма в идеальных условиях, то надо брать лучшее значение, а не медианное.
(Идею не моя, а Александреску, он говорл о замерах алгоритмов в ФБ.)
Здравствуйте, Skorodum, Вы писали:
S>Если измеряется быстродействие алгоритма в идеальных условиях, то надо брать лучшее значение, а не медианное. S>(Идею не моя, а Александреску, он говорл о замерах алгоритмов в ФБ.)
Я просто исхожу из того, что средства измерения тоже имеют погрешности и их тоже надо учитывать. А лучшее время может отказаться совсем некорректным. Но вопрос конечно спорный.
Здравствуйте, ArtDenis, Вы писали:
AD>Я просто исхожу из того, что средства измерения тоже имеют погрешности и их тоже надо учитывать. А лучшее время может отказаться совсем некорректным. Но вопрос конечно спорный.
В этом и суть: погрешность измерений только ухудшает результат. Самаое маленькое значение — самое близкое к достижимому в идеальных условиях.
Другое дело если хочется сравнить производительность не в рафинированных условиях.