@@ -335,59 +335,286 @@ auto first = tasks.begin();
335335auto last = tasks.end();
336336co_await pot::coroutines::when_all(first, last);
337337```
338- ## Скалярное произведение
339- Высокопроизводительная реализация скалярного произведения с векторизацией (SIMD) и параллельной обработкой по блокам через ` executor ` .
338+ ## Elementwise_reduce
339+ Асинхронная поэлементная редукция над двумя массивами. Сначала к каждой паре элементов применяется бинарная операция (` elem_op(a[i], b[i]) ` ), затем результаты сводятся редукцией (` reduce_op ` ) с начальным элементом ` identity ` . Есть удобные перегрузки для ` pointer ` /` std::span ` /` std::vector ` .
340+ ### Сигнатуры
341+ ``` cpp
342+ // 1) Указатели
343+ template <typename T, typename R = T,
344+ typename ElemOp = std::plus<T>,
345+ typename ReduceOp = std::plus<R>>
346+ pot::coroutines::lazy_task<R>
347+ elementwise_reduce (pot::executor& exec,
348+ const T* a, const T* b, std::size_t n,
349+ ElemOp elem_op, ReduceOp reduce_op, R identity)
350+ requires(std::is_arithmetic_v<T > && std::is_arithmetic_v<R >);
351+
352+ // 2) std::span
353+ template <typename T, typename R = T,
354+ typename ElemOp = std::plus<T >,
355+ typename ReduceOp = std::plus<T >>
356+ pot::coroutines::lazy_task<R >
357+ elementwise_reduce(pot::executor& exec,
358+ std::span<const T > a, std::span<const T > b,
359+ ElemOp elem_op, ReduceOp reduce_op, R identity);
360+
361+ // 3) std::vector
362+ template <typename T, typename R, typename ElemOp, typename ReduceOp>
363+ pot::coroutines::lazy_task<R >
364+ elementwise_reduce(pot::executor& exec,
365+ const std::vector<T >& a, const std::vector<T >& b,
366+ ElemOp elem_op, ReduceOp reduce_op, R identity);
367+ ```
368+
369+ ### Параметры и требования
370+
371+ - `T` — тип входных элементов (арифметический).
372+
373+ - `R` — тип результата (арифметический, по умолчанию `T`).
374+
375+ - `ElemOp` — вызываемый `(T, T) -> R`, применяется поэлементно.
376+
377+ - `ReduceOp` — вызываемый `(R, R) -> R`, сводит результаты.
378+
379+ - `exec` — `pot::executor` для планирования задач (используется `parfor`).
380+
381+ - Для перегрузок `span`/`vector` размеры должны совпадать, иначе `std::invalid_argument`.
382+
340383
384+ ### Возвращаемое значение
385+
386+ `pot::coroutines::lazy_task<R>` — завершается значением редукции (при `n == 0` возвращается `identity`).
387+
388+ ### Пример использования
389+
390+ **Скаларное произведение**
391+ ```cpp
392+ auto dot = [&](pot::executor& exec,
393+ const std::vector<double>& a,
394+ const std::vector<double>& b)
395+ -> pot::coroutines::lazy_task<double>
396+ {
397+ co_return co_await pot::algorithms::elementwise_reduce<double,double>(
398+ exec, a, b,
399+ std::multiplies<double>{},
400+ std::plus<double>{},
401+ 0.0
402+ );
403+ };
404+
405+ ```
406+
407+ ## Elementwise_reduce_simd
408+ SIMD-вариант поэлементной редукции: обрабатывает несколько элементов за итерацию через ` pot::simd::simd_forced<..., ST> ` , затем доредуцирует хвост скалярно.
341409### Сигнатуры
342410``` cpp
343- // указатели + длина
411+ // 1) Указатели (SIMD)
412+ template <typename T, typename R = T,
413+ pot::simd::SIMDType ST,
414+ typename SimdElemOp, // (simd_forced<T, ST>, simd_forced<T, ST>) -> simd_forced<R, ST>
415+ typename ScalarElemOp, // (T, T) -> R
416+ typename ReduceOp> // (R, R) -> R
417+ pot::coroutines::lazy_task<R>
418+ elementwise_reduce_simd (pot::executor& exec,
419+ const T* a, const T* b, std::size_t n,
420+ SimdElemOp simd_elem_op,
421+ ScalarElemOp scalar_elem_op,
422+ ReduceOp reduce_op,
423+ R identity)
424+ requires(std::is_arithmetic_v<T > && std::is_arithmetic_v<R >);
425+
426+ // 2) std::span (SIMD)
427+ template <typename T, typename R, pot::simd::SIMDType ST,
428+ typename SimdElemOp, typename ScalarElemOp, typename ReduceOp>
429+ pot::coroutines::lazy_task<R >
430+ elementwise_reduce_simd(pot::executor& exec,
431+ std::span<const T > a, std::span<const T > b,
432+ SimdElemOp simd_elem_op,
433+ ScalarElemOp scalar_elem_op,
434+ ReduceOp reduce_op,
435+ R identity);
436+
437+ // 3) std::vector (SIMD)
438+ template <typename T, typename R, pot::simd::SIMDType ST,
439+ typename SimdElemOp, typename ScalarElemOp, typename ReduceOp>
440+ pot::coroutines::lazy_task<R >
441+ elementwise_reduce_simd(pot::executor& exec,
442+ const std::vector<T >& a, const std::vector<T >& b,
443+ SimdElemOp simd_elem_op,
444+ ScalarElemOp scalar_elem_op,
445+ ReduceOp reduce_op,
446+ R identity);
447+ ```
448+ ### Параметры и требования
449+
450+ - `ST` — конкретный векторный тип `pot::simd::SIMDType` (например, SSE/AVX и т.п.).
451+
452+ - `simd_elem_op` — операция на SIMD-регистры (возвращает SIMD-аккумулятор).
453+
454+ - `scalar_elem_op` — операция для хвостовых скалярных элементов.
455+
456+ - Остальные требования аналогичны скалярной версии.
457+
458+
459+ ### Возвращаемое значение
460+
461+ `pot::coroutines::lazy_task<R>` — результат редукции с использованием SIMD и параллельной обработки блоков.
462+
463+ ### Пример использования
464+ **L1-норма**
465+ ```cpp
344466template <typename T, pot::simd::SIMDType ST>
345467pot::coroutines::lazy_task<T>
346- dot_simd (pot::executor& exec, const T* a, const T* b, std::size_t n)
347- requires(std::is_arithmetic_v<T >);
468+ l1_simd(pot::executor& exec, std::span<const T> a, std::span<const T> b)
469+ {
470+ auto simd_abs_diff = [](auto va, auto vb){
471+ auto vd = va - vb; // simd_forced<T, ST>
472+ return vd.abs();
473+ };
474+ auto scalar_abs_diff = [](T x, T y){ return std::abs(x - y); };
475+
476+ co_return co_await pot::algorithms::elementwise_reduce_simd<T, T, ST>(
477+ exec, a, b, simd_abs_diff, scalar_abs_diff, std::plus<T>{}, T{0});
478+ }
479+ ```
348480
349- // vectors
481+ ## Dot / Dot_simd
482+ Асинхронное скалярное произведение двух массивов. Есть обычная и SIMD-версия; обе возвращают ленивую корутину с результатом.
483+ ### Сигнатуры
484+ ``` cpp
485+ // SIMD: std::span
350486template <typename T, pot::simd::SIMDType ST>
351487pot::coroutines::lazy_task<T>
352- dot_simd(pot::executor& exec, const std::vector< T >& a, const std::vector< T >& b)
353- requires(std::is_arithmetic_v<T >);
488+ dot_simd (pot::executor& exec, std::span< const T > a, std::span< const T > b)
489+ requires(std::is_arithmetic_v<T >);
354490
355- // полу-указатели (a_begin/a_end + b_begin)
491+ // SIMD: std::vector
356492template <typename T, pot::simd::SIMDType ST>
357493pot::coroutines::lazy_task<T >
358- dot_simd(pot::executor& exec, const T* a_begin, const T* a_end, const T* b_begin)
359- requires(std::is_arithmetic_v<T >);
494+ dot_simd(pot::executor& exec, const std::vector<T >& a, const std::vector<T >& b)
495+ requires(std::is_arithmetic_v<T >);
496+
497+ // Без SIMD: std::span
498+ template <typename T >
499+ pot::coroutines::lazy_task<T >
500+ dot(pot::executor& exec, std::span<const T > a, std::span<const T > b)
501+ requires(std::is_arithmetic_v<T >);
502+
503+ // Без SIMD: std::vector
504+ template <typename T >
505+ pot::coroutines::lazy_task<T >
506+ dot(pot::executor& exec, const std::vector<T >& a, const std::vector<T >& b)
507+ requires(std::is_arithmetic_v<T >);
360508```
361- ### Параметры
509+ ### Параметры и требования
362510
363511- `T` — арифметический тип элементов.
364512
365- - `ST` — стратегия SIMD из `pot::simd::SIMDType` (SSE/AVX/AVX512 ).
513+ - `ST` — целевой SIMD-тип (`pot::simd::SIMDType`) для `dot_simd`.
514+
515+ - `exec` — `pot::executor` для распараллеливания.
516+
517+ - `a`, `b` — входные последовательности одинаковой длины (иначе `std::invalid_argument`).
518+
519+
520+ ### Возвращаемое значение
521+
522+ `pot::coroutines::lazy_task<T>` — завершается значением скалярного произведения.
523+
524+ ### Примеры использования
525+
526+ 1. Обычная версия (vector)
527+ ```cpp
528+ std::vector<double> a = /* ... */, b = /* ... */;
529+ auto res = co_await pot::algorithms::dot(exec, a, b);
530+ ```
531+ 2 . SIMD-версия (span)
532+ ``` cpp
533+ pot::coroutines::lazy_task<float > run_simd (pot::executor& exec,
534+ std::span<const float > a,
535+ std::span<const float > b) {
536+ co_return co_await pot::algorithms::dot_simd<float, AVX>(exec, a, b);
537+ }
538+ ```
539+
540+ ## Parsections
541+ Запускает несколько независимых секций параллельно на заданном исполнителе. Каждая секция — это вызываемый объект (`void()` или корутина, возвращающая `task<void>` / `lazy_task<void>`). Завершается, когда **все** секции отработают.
542+
543+ ### Сигнатуры
544+
545+ ```cpp
546+ template<typename... Funcs> requires (std::is_invocable_v<Funcs> && ...)
547+ pot::coroutines::lazy_task<void> parsections(pot::executor& executor, Funcs&&... funcs);
548+ ```
549+
550+ ### Параметры и требования
551+
552+ - ` executor ` — экземпляр ` pot::executor ` , на котором будут запущены все секции.
553+
554+ - ` funcs... ` — один или несколько вызываемых объектов:
366555
367- - `exec` — исполнитель (одно- или многопоточный).
556+ - синхронные ` void() ` функции/лямбда-функции;
557+
558+ - корутины, возвращающие ` pot::coroutines::task<void> ` или ` pot::coroutines::lazy_task<void> ` .
559+
560+ - Требования:
368561
369- - `a`, `b`, `n` — массивы и их размер; для перегрузок со `span`/`vector` размеры должны совпадать (иначе будет `std::invalid_argument`).
562+ - как минимум один аргумент (` static_assert(sizeof...(Funcs) > 0) ` ).
563+
564+ - каждый ` Func ` должен быть вызываем без аргументов (` std::is_invocable_v ` ).
565+
370566
371567### Возвращаемое значение
372568
373- `lazy_task<T >` — ленивая корутина, завершающаяся, когда всё посчитано. Результат — сумма типа `T` .
569+ ` pot::coroutines:: lazy_task<void >` — завершается после завершения ** всех ** секций .
374570
375571### Пример использования
376572``` cpp
377- #include "pot/algorithms/dot_simd.h"
378- #include "pot/executors/thread_pool_executor_lfgq.h"
573+ pot::coroutines::task<void > coroA ();
574+ pot::coroutines::lazy_task<void > coroB ();
575+
576+ co_await pot::algorithms::parsections (exec,
577+ [ ] { prepare(); },
578+ [ ] ( ) -> pot::coroutines::task<void > { co_await coroA(); co_return; },
579+ [ ] ( ) -> pot::coroutines::lazy_task<void > { co_await coroB(); co_return; }
580+ );
581+ ```
582+ ## Resume_on
583+ Функция, возвращающая awaitable, которая возобновляет выполнение текущей корутины на заданном ` executor ` .
379584
380- pot::coroutines::task<void> example() {
381- std::vector<T> a = {/* ... */};
382- std::vector<T> b = {/* ... */};
585+ ### Сигнатуры
383586
384- pot::executors::thread_pool_executor_lfgq pool("dot-pool", 8);
587+ ``` cpp
588+ namespace pot ::coroutines {
589+ // Возвращает awaitable-объект; при await — планирует продолжение на executor
590+ auto resume_on(pot::executor& executor) noexcept;
591+ }
592+ ```
385593
386- // как lazy_task<T>: можно co_await прямо здесь или позже
387- T res =
388- co_await pot::algorithms::dot_simd<float, pot::simd::SIMDType::AVX>(pool, a, b);
594+ ### Поведение
389595
390- std::cout << "dot(a,b) = " << res << "\n";
596+ - `co_await resume_on(exec)` всегда откладывает продолжение и передаёт `std::coroutine_handle<>` в `executor.run_detached(...)`.
597+
598+ - `await_ready()` всегда `false` — продолжение **всегда** переназначается на переданный `executor`.
599+
600+ - Без возвращаемого значения; ошибки (если есть) определяются реализацией `executor`.
601+
602+
603+ ### Параметры и требования
604+
605+ - `executor` — экземпляр `pot::executor`, способный принять `std::coroutine_handle<>` через `run_detached(handle)` и возобновить его на собственном планировщике.
606+
607+ ### Примеры использования
608+ ```cpp
609+ using pot::coroutines::resume_on;
610+
611+ pot::coroutines::task<void> do_work(pot::executor& cpu1, pot::executor& cpu2)
612+ {
613+ co_await resume_on(cpu1); // продолжить на CPU1
614+ co_await heavy_compute(); // тяжёлая работа на CPU1
615+ co_await resume_on(cpu2); // продолжить на CPU2
616+ update(); // update() на CPU2
391617 co_return;
392618}
393- ```
619+ ```
620+
0 commit comments