powered by simpleCommunicator - 2.0.51     © 2025 Programmizd 02
Форумы / WinForms, .Net Framework [игнор отключен] [закрыт для гостей] / Как делать кучу параллельных IO-Bound операций ?
57 сообщений из 57, показаны все 3 страниц
Как делать кучу параллельных IO-Bound операций ?
    #39689035
WaspNewCore
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Что-то ищу информацию, и похоже ни PLINQ ни Parallel не умеют работать с async вызовами.

Мне нужно выполнить работу, которая включает в себя кучу последовательных обращений к БД, с мелкой обработкой промежуточных результатов. Т.е. куча IO Bound операций с маленькой каплей Cpu-Bound.

Ну к примеру вот тут пишут про PLINQ не умеет async (ответ дан 5 дней назад)
https://stackoverflow.com/a/51600479
It does not matter, that you try to await the AsyncTask(), because ForAll() gets a plain Action and does not await the result of your AsyncTask().

А тут пишут, что класс Parallel тоже не в курсе про async
https://stackoverflow.com/a/11565317
и там же предлагают задействовать TPL Dataflow - вот он мол умеет работать. Но в тоже время является очень тяжеловесным решением.


Так какой выход на текущем временном отрезке ?
...
Рейтинг: 0 / 0
Как делать кучу параллельных IO-Bound операций ?
    #39689062
Фотография ЕвгенийВ
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
WaspNewCore,

1. Почему бы всю работу не сделать в БД?
2. Если у тебя 99,999% времени есть ожидание БД, а остальное обработка в приложении, то имеет смысл проверить, будет ли польза от параллельного обращение к БД, возможно встрянешь еще сильнее по скорости.
3. Если все же есть смысл ходить в БД параллельно, то просто запусти 10-100 потоков, пусть ждут наздоровье.
...
Рейтинг: 0 / 0
Как делать кучу параллельных IO-Bound операций ?
    #39689064
WaspNewCore
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
ЕвгенийВ,

В том и суть. что нужно сделать в несколько потоков. Распараллелить и загрузить бд работой.
Бд же не умеет в несколько потоков работать ?
...
Рейтинг: 0 / 0
Как делать кучу параллельных IO-Bound операций ?
    #39689066
WaspNewCore
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
ЕвгенийВ,

ну собственно я и хочу запустить 100 потоков - но, это правильно делать через async/await. А непосредственно генерацией этих сотен потоков занимаются PLINQ и Parallel. Но созданы они давно, и, как я понял, мелкософты не удосужились добавить в них поддержку async/await. А без них создавать сотни IO-Bound задач уже не комильфо.
...
Рейтинг: 0 / 0
Как делать кучу параллельных IO-Bound операций ?
    #39689067
Фотография ЕвгенийВ
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
WaspNewCoreБд же не умеет в несколько потоков работать ?
Ну тут смотря какая БД у тебя, odbc driver for dbf скорее всего не умеет :)
...
Рейтинг: 0 / 0
Как делать кучу параллельных IO-Bound операций ?
    #39689069
WaspNewCore
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
ms sql.
Я имел ввиду - что я же не могу внутри T-SQL написать код который будет параллелить. Нету такого функционала в принципе. Это в C# я могу создавать потоки вручную, как и сколько мне надо.

Я могу лишь запустить сотню потоков работающих с бд из клиентского приложения.
...
Рейтинг: 0 / 0
Как делать кучу параллельных IO-Bound операций ?
    #39689073
Фотография ЕвгенийВ
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
WaspNewCoreЕвгенийВ,

ну собственно я и хочу запустить 100 потоков - но, это правильно делать через async/await. А непосредственно генерацией этих сотен потоков занимаются PLINQ и Parallel. Но созданы они давно, и, как я понял, мелкософты не удосужились добавить в них поддержку async/await. А без них создавать сотни IO-Bound задач уже не комильфо.
Исторически TPL была создана раньше async/await для распараллеливания вычислений, последние же нужны для асинхронности.
...
Рейтинг: 0 / 0
Как делать кучу параллельных IO-Bound операций ?
    #39689074
WaspNewCore
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
ЕвгенийВ3. Если все же есть смысл ходить в БД параллельно, то просто запусти 10-100 потоков, пусть ждут наздоровье.

Собственно тут есть два пути - правильный и не правильный.

не правильный это Task.Run (()=> SqlConnection.Query<Model>). В этом случае потоки будут простаивать в момент этого самого Query (пользуясь терминологией Dapper.Net).
правильно это что-то типа Parallel.ForEach (async x=> await SqlConnection.QueryAsync<Model>). В этом случае потоки будут освобождены на время QueryAsync. Но беда в том, что, как я понял, Parallel.ForEach (и Plinq) не знаю про async/await. Они могут только работать по неправильному пути.
...
Рейтинг: 0 / 0
Как делать кучу параллельных IO-Bound операций ?
    #39689076
WaspNewCore
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
ЕвгенийВ,

Конечно TPL появилось раньше async/await.
Странно лишь то, что умные головы из Microsoft не добавляют поддержку в PLINQ и Parallel поддержку методов вида Parallel.ForEachAsync. При том что это сделали для любых маломальских классов типа File.GetAllTextAsync и прочего сразу с вводом async/await. Не пойму почему.

И даже альтернативы то не сделали. Кроме огромнейшего TPL Dataflow. Тянуть его в проект ради того, чтобы один раз воспользоватся аналогом Parallel.ForEachAsync как-то не хочется.

Вот и ищу, может есть какие-то другие альтернативы ? Кроме самописных велосипедов...
...
Рейтинг: 0 / 0
Как делать кучу параллельных IO-Bound операций ?
    #39689077
Фотография ЕвгенийВ
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
WaspNewCorems sql.
Я имел ввиду - что я же не могу внутри T-SQL написать код который будет параллелить.
Ну почему же, можешь !

В T-SQL что делать решает оптимизатор и каждый простенький запрос может обслуживаться кучей потоков.
...
Рейтинг: 0 / 0
Как делать кучу параллельных IO-Bound операций ?
    #39689083
WaspNewCore
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
ЕвгенийВ,

Если я правильно понимаю тут речь о распараллеливании селекта.
А мне нужно запустить некую T-SQL процедуру в нескольких экземлярах.
...
Рейтинг: 0 / 0
Как делать кучу параллельных IO-Bound операций ?
    #39689084
Фотография Petro123
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
WaspNewCore,
Сколько понаписал то.
Внутри потока создавай коннект и работай.
В чем проблема?
...
Рейтинг: 0 / 0
Как делать кучу параллельных IO-Bound операций ?
    #39689085
Фотография ЕвгенийВ
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
WaspNewCore При том что это сделали для любых маломальских классов типа File.GetAllTextAsync и прочего сразу с вводом async/await. Не пойму почему.


Это для тех случаев, когда Cpu-Bound не хватает, например в web highload приложении или для ГУЯ, дабы не блокировать его и не сильно напрягать программистов для этого.
...
Рейтинг: 0 / 0
Как делать кучу параллельных IO-Bound операций ?
    #39689088
Фотография ЕвгенийВ
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
WaspNewCoreЕвгенийВ,

Если я правильно понимаю тут речь о распараллеливании селекта.
А мне нужно запустить некую T-SQL процедуру в нескольких экземлярах.
Грубо говоря, если ты запустишь несколько раз долгоиграющую процедуру из разных окон например SSMS, то все запущенные будут исполняться параллельно.
...
Рейтинг: 0 / 0
Как делать кучу параллельных IO-Bound операций ?
    #39689091
Фотография LR
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
WaspNewCoreА тут пишут, что класс Parallel тоже не в курсе про async
https://stackoverflow.com/a/11565317
и там же предлагают задействовать TPL Dataflow - вот он мол умеет работать. Но в тоже время является очень тяжеловесным решением.

Там ниже предлагаются варианты на все возможные случаи "замеса" параллельности и асинхронности - RunWithMaxDegreeOfConcurrency и ForEachAsync.
...
Рейтинг: 0 / 0
Как делать кучу параллельных IO-Bound операций ?
    #39689093
Фотография LR
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
LRТам ниже предлагаются варианты на все возможные случаи "замеса" параллельности и асинхронности - RunWithMaxDegreeOfConcurrency и ForEachAsync.
...и еще ниже - ParallelForEachAsync
...
Рейтинг: 0 / 0
Как делать кучу параллельных IO-Bound операций ?
    #39689097
Фотография Petro123
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
WaspNewCoreне правильный это Task.Run (()=> SqlConnection.Query<Model>).
Правильный:
Task.Run(()=> MyMetodAsync()
...
Рейтинг: 0 / 0
Как делать кучу параллельных IO-Bound операций ?
    #39689118
Фотография hVostt
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
WaspNewCore,

Кажется, что вы смешали мухи с котлетами.

Чтобы понять где косяк, надо просто понять одну простую вещь.
Код с асинхронными вызовами с точки зрения execution flow абсолютно синхронный.

В тоже время TPL это параллельный execution flow, поэтому "уметь" async он не должен.
Вполне достаточно WhenAll, который умеет await.
...
Рейтинг: 0 / 0
Как делать кучу параллельных IO-Bound операций ?
    #39689143
Фотография LR
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
hVosttВполне достаточно WhenAll, который умеет await.
Ну а если элементов сотни, глупо же выполнять их на сотне потоков... Разумно приделать какое-то "горло". По второй ссылке из первого поста ТСа весьма привлекательным выглядит самое последнее решение, где горлышко организовано с помощью SemaphoreSlim
ForEachAsyncConcurrentAn extension method for this which makes use of SemaphoreSlim and also allows to set maximum degree of parallelism
Код: c#
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
    /// <summary>
    /// Concurrently Executes async actions for each item of <see cref="IEnumerable<typeparamref name="T"/>
    /// </summary>
    /// <typeparam name="T">Type of IEnumerable</typeparam>
    /// <param name="enumerable">instance of <see cref="IEnumerable<typeparamref name="T"/>"/></param>
    /// <param name="action">an async <see cref="Action" /> to execute</param>
    /// <param name="maxDegreeOfParallelism">Optional, An integer that represents the maximum degree of parallelism,
    /// Must be grater than 0</param>
    /// <returns>A Task representing an async operation</returns>
    /// <exception cref="ArgumentOutOfRangeException">If the maxActionsToRunInParallel is less than 1</exception>
    public static async Task ForEachAsyncConcurrent<T>(
        this IEnumerable<T> enumerable,
        Func<T, Task> action,
        int? maxDegreeOfParallelism = null)
    {
        if (maxDegreeOfParallelism.HasValue)
        {
            using (var semaphoreSlim = new SemaphoreSlim(
                maxDegreeOfParallelism.Value, maxDegreeOfParallelism.Value))
            {
                var tasksWithThrottler = new List<Task>();

                foreach (var item in enumerable)
                {
                    // Increment the number of currently running tasks and wait if they are more than limit.
                    await semaphoreSlim.WaitAsync();

                    tasksWithThrottler.Add(Task.Run(async () =>
                    {
                        await action(item);

                        // action is completed, so decrement the number of currently running tasks
                        semaphoreSlim.Release();
                    }));
                }

                // Wait for all tasks to complete.
                await Task.WhenAll(tasksWithThrottler.ToArray());
            }
        }
        else
        {
            await Task.WhenAll(enumerable.Select(item => action(item)));
        }
    }


Sample Usage:
Код: c#
1.
2.
3.
4.
5.
6.
await enumerable.ForEachAsyncConcurrent(
    async item =>
    {
        await SomeAsyncMethod(item);
    },
    5);

...
Рейтинг: 0 / 0
Как делать кучу параллельных IO-Bound операций ?
    #39689148
Фотография hVostt
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
...
Рейтинг: 0 / 0
Как делать кучу параллельных IO-Bound операций ?
    #39689150
Фотография hVostt
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
LRНу а если элементов сотни, глупо же выполнять их на сотне потоков...

https://docs.microsoft.com/ru-ru/dotnet/standard/asynchronous-programming-patterns/consuming-the-task-based-asynchronous-pattern#throttling
...
Рейтинг: 0 / 0
Как делать кучу параллельных IO-Bound операций ?
    #39689157
Фотография LR
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
hVostt https://docs.microsoft.com/ru-ru/dotnet/standard/asynchronous-programming-patterns/consuming-the-task-based-asynchronous-pattern#throttling
Ага, получается в документации эти вопросы рассмотрены, но кто ж ее читает)) И еще, если посмотреть в Task.WhenAny , с созданием копии списка задач, то вариант с семафором может получиться и побыстрее, х.з.
...
Рейтинг: 0 / 0
Как делать кучу параллельных IO-Bound операций ?
    #39689161
Фотография Petro123
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
LRНу а если элементов сотни, глупо же выполнять их на сотне потоков...
С чего их сотня если пул потоков по умолчанию настройка макс около 20 потоков.
Остальные ждать будут.
И писать ничего не надо.
...
Рейтинг: 0 / 0
Как делать кучу параллельных IO-Bound операций ?
    #39689166
Фотография LR
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Petro123LRНу а если элементов сотни, глупо же выполнять их на сотне потоков...
С чего их сотня если пул потоков по умолчанию настройка макс около 20 потоков.
Остальные ждать будут.
И писать ничего не надо.
Ну какая разница, суть в другом - забирать все потоки из пула для одной задачи, если только она не единственная - глупо.
...
Рейтинг: 0 / 0
Как делать кучу параллельных IO-Bound операций ?
    #39689167
Фотография Petro123
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
LRНу какая разница, суть в другом - забирать все потоки из пула для одной задачи, если только она не единственная - глупо.
суть нелогична.
Если задача одна - зачем параллелить?
Об чём топик?
Как хранимку распарралелить "чтобы было"?
...
Рейтинг: 0 / 0
Как делать кучу параллельных IO-Bound операций ?
    #39689310
WaspNewCore
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Petro123,

Ну короче сейчас все сделано через T-SQL процедуру. Извлекаются данные курсором и потом идет длинный алгоритм со сложной логикой со считыванием данных из других таблиц и последущими расчетами. По каждому элементу отдельно. Получается обрабатывать около 20-30 элементов в секунду. Нужно существенно увеличить эту цифру. Т.к. нужно обрабатывать архивы данных.
Что я хочу сделать через перевод на C# и увеличение количества потоков, выполняющих данный алгоритм, что выльется в некую цепочку: CpuBound(мелкая обработка)+IOBound(долго)+CpuBound(мелкая обработка)+IOBound(долго)+IOBound(долго)+CpuBound(мелкая обработка).
...
Рейтинг: 0 / 0
Как делать кучу параллельных IO-Bound операций ?
    #39689319
Сон Веры Павловны
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
WaspNewCoreИзвлекаются данные курсором и потом идет длинный алгоритм со сложной логикой со считыванием данных из других таблиц и последущими расчетами.
Курсоры в T-SQL = тормоза. Это было подтверждено уже не раз. И способ убыстрения таких алгоритмов, как ни странно - отказ от курсоров (и скалярных UDF). Все расчеты и извлечение данных помещаются в один запрос.
...
Рейтинг: 0 / 0
Как делать кучу параллельных IO-Bound операций ?
    #39689322
WaspNewCore
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
А тему я создал потому, что хотел понять есть ли стандартные способы от разработчиков C#. Странно стало, что PLinq и Parallel не умеют по async. Думал может чего упустил, но получается не упустил.

Ну, придется воспользоватся велосипедом ForEachAsyncConcurrent, только я думаю там имеет смысл добавить ThreadPool.SetMaxThreads если maxDegreeOfParallelism задан больше Environment.ProcessorCount, что не учтено в данном методе. Иначе толку от maxDegreeOfParallelism не будет - пул потоков тупо не запустит новые задачи.

Вот поэтому я и не хотел использовать велосипеды, уж разрабы C# точно не упустят важные вещи.
...
Рейтинг: 0 / 0
Как делать кучу параллельных IO-Bound операций ?
    #39689323
WaspNewCore
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Сон Веры ПавловныWaspNewCoreИзвлекаются данные курсором и потом идет длинный алгоритм со сложной логикой со считыванием данных из других таблиц и последущими расчетами.
Курсоры в T-SQL = тормоза. Это было подтверждено уже не раз. И способ убыстрения таких алгоритмов, как ни странно - отказ от курсоров (и скалярных UDF). Все расчеты и извлечение данных помещаются в один запрос.

Ну а как без алгоритмов то, в условиях когда есть алгоритмы вида if сделай то-то, else сделай длинную цепочку другого алгоритма.
Упихивать это в чистый SQL слишком сложно и запутанно - через всякий там merge и прочие.
Ну вот думаю проще перенести логику на C# хотя бы.
...
Рейтинг: 0 / 0
Как делать кучу параллельных IO-Bound операций ?
    #39689333
Сон Веры Павловны
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
WaspNewCoreНу а как без алгоритмов то, в условиях когда есть алгоритмы вида if сделай то-то, else сделай длинную цепочку другого алгоритма.
Упихивать это в чистый SQL слишком сложно и запутанно - через всякий там merge и прочие.
Вполне нормально. Декомпозиция решает. Сложные/громоздкие вычисления выносятся в инлайновые TVF, и используются в основном запросе посредством cross/outer apply. Поскольку функции инлайновые, то оптимизатором всё это будет собрано в один запрос с единым планом выполнения.
...
Рейтинг: 0 / 0
Как делать кучу параллельных IO-Bound операций ?
    #39689334
Tosh
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
WaspNewCoreСон Веры Павловныпропущено...

Курсоры в T-SQL = тормоза. Это было подтверждено уже не раз. И способ убыстрения таких алгоритмов, как ни странно - отказ от курсоров (и скалярных UDF). Все расчеты и извлечение данных помещаются в один запрос.

Ну а как без алгоритмов то, в условиях когда есть алгоритмы вида if сделай то-то, else сделай длинную цепочку другого алгоритма.
Упихивать это в чистый SQL слишком сложно и запутанно - через всякий там merge и прочие.
Ну вот думаю проще перенести логику на C# хотя бы.
Да запросто - от CASE в качестве полей выбора, до UNION нескольких наборов, удовлетворяющих разным условиям. Настоятельно рекомендую Вам об этом подумать ... ну или назовите приложение, над которым работаете, - чтобы нечаянно с ним не познакомиться
...
Рейтинг: 0 / 0
Как делать кучу параллельных IO-Bound операций ?
    #39689335
WaspNewCore
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
мне проще на C#. тут видимо вопрос опыта в конкретных тенологиях.
...
Рейтинг: 0 / 0
Как делать кучу параллельных IO-Bound операций ?
    #39689340
Фотография Petro123
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
WaspNewCoreмне проще на C#. тут видимо вопрос опыта в конкретных тенологиях.именно батенька.
Вопрос где размещать БЛ бизнес логику. Он флеймовый.
Можно в бд на его ЯП. Можно в АппСервере.
Ты выбрал второе т.к. больше знаешь шарп.
Логично).
...
Рейтинг: 0 / 0
Как делать кучу параллельных IO-Bound операций ?
    #39689341
Фотография Petro123
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Сон Веры ПавловныПоскольку функции инлайновые, то оптимизатором всё это будет собрано в один запрос с единым планом выполнения.в этом я не силен.
Я либо оптимизатор бд и его большая хранимка.
Либо ОРМ и тогда оптимизатор голова разработчика).
Конечно, при аналитике как бы и ОРМ уже не подходит.
...
Рейтинг: 0 / 0
Как делать кучу параллельных IO-Bound операций ?
    #39689345
Фотография Petro123
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
WaspNewCoreТ.к. нужно обрабатывать архивы данных.сделайте для архива денормализацию и olap\oltp.
Обычно архивы для ускорения как раз отдельно.
...
Рейтинг: 0 / 0
Как делать кучу параллельных IO-Bound операций ?
    #39689349
Сон Веры Павловны
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Здесь есть еще один нюанс: когда данные собираются единым запросом, сервер сам обеспечивает консистентность этих данных (либо механизмом блокировок, либо механизмом IRCS). Если зависящие друг от друга данные собираются кучей атомарных запросов в параллельных потоках (и сессиях), есть достаточно неплохой шанс смоделировать ситуацию грязного чтения.
...
Рейтинг: 0 / 0
Как делать кучу параллельных IO-Bound операций ?
    #39689350
Фотография Petro123
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
WaspNewCoreчто выльется в некую цепочку:при увлечении цепочками будут потери при переключении контекста.
Со школы известно что 2 потока не быстрее в 2 раза одного.
А например, денормализация ускорит вам в 10 раз.
IMHO
...
Рейтинг: 0 / 0
Как делать кучу параллельных IO-Bound операций ?
    #39689357
Фотография Petro123
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Сон Веры Павловныгрязного чтения.ну, у него архив вроде. Данные статичны.
Но конечно, один запрос без курсоров это идеал.
Это обычно в ветке БД пробуют написать.
...
Рейтинг: 0 / 0
Как делать кучу параллельных IO-Bound операций ?
    #39689378
WaspNewCore
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Petro123WaspNewCoreчто выльется в некую цепочку:при увлечении цепочками будут потери при переключении контекста.
Со школы известно что 2 потока не быстрее в 2 раза одного.
А например, денормализация ускорит вам в 10 раз.
IMHO

Petro123Сон Веры Павловныгрязного чтения.ну, у него архив вроде. Данные статичны.
Но конечно, один запрос без курсоров это идеал.
Это обычно в ветке БД пробуют написать.

Написать такую сложную логику для меня будет сложновато. Тем более с OLTP и пр. Мне проще перенести на C#. Понятно, что параллельность не увеличит производительность прям ровно в нужное количество потоков, см. Закон Амдала . Но я думаю увеличение будет достаточно существенным. Раз этак в 1.87 с двух потоков и т.д. Со 100 потоков может выжмется ускорение раз в 70. Полагаю для моей текущей задачи такой производительности хватит за глаза. Если потом упрусь в потолок и его все равно будет не хватать (а это вряд ли произойдет в ближайшие годы) подключу спеца по SQL, чтобы он уже ворочал OLPT и пр. Но это слишком далекие планы.

Что касается грязного чтения.. хм. Вряд ли. Работа идет с уже готовыми статичными данными. Берется родительская запись и ее обрабатывает один поток. Который запрашивает подчиненные сущности и их обрабатывает, с последующей заливкой в другие таблицы. Пересечений и дедлоков в принципе не должно быть.
...
Рейтинг: 0 / 0
Как делать кучу параллельных IO-Bound операций ?
    #39689379
WaspNewCore
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Или это не Закон Амдала утверждает, что накладные расходы при распараллеливании вычислений уменьшают общую производительность. Забыл, не важно в общем )
...
Рейтинг: 0 / 0
Как делать кучу параллельных IO-Bound операций ?
    #39689408
Фотография ЕвгенийВ
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
WaspNewCore Берется родительская запись и ее обрабатывает один поток. Который запрашивает подчиненные сущности и их обрабатывает, с последующей заливкой в другие таблицы. Пересечений и дедлоков в принципе не должно быть.
Ну тут прямо напрашивается select->insert. без всяких await/async
...
Рейтинг: 0 / 0
Как делать кучу параллельных IO-Bound операций ?
    #39689443
Фотография Petro123
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
WaspNewCoreСо 100 потоков может выжмется ускорение раз в 70.сомневаюсь.
По правилом нужна демка ПРОТОТИП.
Ставьте sleep() вместо БЛ и уже завтра, в выходной отпишитсь во сколько раз
20 потоков SELECT record быстрее чем курсор в базе.
...
Рейтинг: 0 / 0
Как делать кучу параллельных IO-Bound операций ?
    #39689446
Фотография Petro123
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
ЕвгенийВWaspNewCore Берется родительская запись и ее обрабатывает один поток. Который запрашивает подчиненные сущности и их обрабатывает, с последующей заливкой в другие таблицы. Пересечений и дедлоков в принципе не должно быть.
Ну тут прямо напрашивается select->insert. без всяких await/asyncон хочет 100 select по одной записи одновременно в базу.
...
Рейтинг: 0 / 0
Как делать кучу параллельных IO-Bound операций ?
    #39689743
Фотография LR
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
WaspNewCoreСтранно стало, что PLinq и Parallel не умеют по async.
Видимо, научить непросто. Представьте, к очереди элементов еще первоначально не опоточенных, добавится очередь готовых к
продолжению после await. Части (или всем) из этих последних нужно продолжить в первоначальном потоке. А еще бывают и такие асинхи , которые не умеют продолжать в чужом потоке (несмотря на ConfigureAwait(false)).
...
Рейтинг: 0 / 0
Как делать кучу параллельных IO-Bound операций ?
    #39689855
Фотография hVostt
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
LR
Код: c#
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
/// <summary>
    /// Concurrently Executes async actions for each item of <see cref="IEnumerable<typeparamref name="T"/>
    /// </summary>
    /// <typeparam name="T">Type of IEnumerable</typeparam>
    /// <param name="enumerable">instance of <see cref="IEnumerable<typeparamref name="T"/>"/></param>
    /// <param name="action">an async <see cref="Action" /> to execute</param>
    /// <param name="maxDegreeOfParallelism">Optional, An integer that represents the maximum degree of parallelism,
    /// Must be grater than 0</param>
    /// <returns>A Task representing an async operation</returns>
    /// <exception cref="ArgumentOutOfRangeException">If the maxActionsToRunInParallel is less than 1</exception>
    public static async Task ForEachAsyncConcurrent<T>(
        this IEnumerable<T> enumerable,
        Func<T, Task> action,
        int? maxDegreeOfParallelism = null)
    {
        if (maxDegreeOfParallelism.HasValue)
        {
            using (var semaphoreSlim = new SemaphoreSlim(
                maxDegreeOfParallelism.Value, maxDegreeOfParallelism.Value))
            {
                var tasksWithThrottler = new List<Task>();

                foreach (var item in enumerable)
                {
                    // Increment the number of currently running tasks and wait if they are more than limit.
                    await semaphoreSlim.WaitAsync();

                    tasksWithThrottler.Add(Task.Run(async () =>
                    {
                        await action(item);

                        // action is completed, so decrement the number of currently running tasks
                        semaphoreSlim.Release();
                    }));
                }

                // Wait for all tasks to complete.
                await Task.WhenAll(tasksWithThrottler.ToArray());
            }
        }
        else
        {
            await Task.WhenAll(enumerable.Select(item => action(item)));
        }
    }



Кстати, по поводу этой реализации, которая встречается множество раз (автор знатно постарался в распространении своего говнокода), должен сказать.

Не нужно использовать эту реализацию у себя, так как при достаточно большом количестве элементов тупо пожирает память и выход может быть более медленным, чем хотелось бы.

В общем, не нужно бездумно копировать всякую дичь со стеков :)
...
Рейтинг: 0 / 0
Как делать кучу параллельных IO-Bound операций ?
    #39690314
WaspNewCore
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
hVosttНе нужно использовать эту реализацию у себя, так как при достаточно большом количестве элементов тупо пожирает память и выход может быть более медленным, чем хотелось бы.

А можете объяснить в чем проблемы ? Я не вижу критичных, метод выглядит вполне юзабельным. А значит я чего-то не понимаю в async/await. Нужно срочно это исправить, просвятите :)
И что значит "выход может быть более медленным" ? Какого порядка цифры ? Если речь о лишних микросекундах из-за ожиданий await semaphoreSlim.WaitAsync() то не вижу тут проблем, потери в десяток микросекунд я не считаю - такой сверхоптимизации мне не требуется.

У меня всего лишь пара мелких замечаний:
1. добавить ConfigureAwait(false) везде.
2.
Код: c#
1.
await Task.WhenAll(tasksWithThrottler.ToArray());


Начиная с 4.5 фреймворка уже не нужно преобразовывать в массив. WhenAll с тех пор принимает IEnumerable.
...
Рейтинг: 0 / 0
Как делать кучу параллельных IO-Bound операций ?
    #39690322
Фотография hVostt
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
WaspNewCore,

Проблема здесь:

LR
Код: c#
1.
var tasksWithThrottler = new List<Task>();



Если через аргумент функции enumerable будет прокачано миллиард элементов, в памяти останутся миллиард экземпляров Task, да и эвейтить этот миллиард то ещё удовольствие.
...
Рейтинг: 0 / 0
Как делать кучу параллельных IO-Bound операций ?
    #39690329
WaspNewCore
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
hVostt,

Да, я думал на этот момент. Но это стандартная проблема с кодом вообще - не создать случайно List на триллиард записей.
Полагаю, что проблема как раз из-за необходимости передать в Task.WhenAll массив.

Можно немного оптимизировать
- заменить
Код: c#
1.
var tasksWithThrottler = new List<Task>(); 


на
Код: c#
1.
var tasksWithThrottler = new BlockingCollection<Task>(); 



- заменить
Код: c#
1.
await Task.WhenAll(tasksWithThrottler.ToArray());


на
Код: c#
1.
await Task.WhenAll(tasksWithThrottler.GetConsumingEnumerable ());



не смотрел код Task.WhenAll, но если там что-то типа
Код: c#
1.
2.
foreach (var element in enumerable)
  await element;



то должно работать гладко.


Но в принципе на не очень больших коллекциях ( ну чтобы не растить List до миллиардов) и так будет работать. В принципе конкретно в своем решении я разбиваю работы на пакеты по ~1000 элементов, так, что особых рисков не будет.
...
Рейтинг: 0 / 0
Как делать кучу параллельных IO-Bound операций ?
    #39690392
Фотография hVostt
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
WaspNewCoreМожно немного оптимизировать

...или взять правильный, корректно работающий код без блокирующих коллекций

https://docs.microsoft.com/ru-ru/dotnet/standard/asynchronous-programming-patterns/consuming-the-task-based-asynchronous-pattern#throttling

:)

не думаю, что BlockingCollection в контексте отсутствия параллелизма, а это именно так на таск эвейте, хорошая идея, скорее очень плохая.
...
Рейтинг: 0 / 0
Как делать кучу параллельных IO-Bound операций ?
    #39690393
Фотография hVostt
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
WaspNewCoreНо в принципе на не очень больших коллекциях ( ну чтобы не растить List до миллиардов) и так будет работать. В принципе конкретно в своем решении я разбиваю работы на пакеты по ~1000 элементов, так, что особых рисков не будет.

Это заряженное ружьишко, которое может очень больно может садануть и прострелить ногу.
...
Рейтинг: 0 / 0
Как делать кучу параллельных IO-Bound операций ?
    #39690503
Фотография ЕвгенийВ
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
WaspNewCore
не смотрел код Task.WhenAll, но если там что-то типа
Код: c#
1.
2.
foreach (var element in enumerable)
  await element;




Там такая мутота
Код: c#
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
	List<Task> list = new List<Task>();
	foreach (Task task2 in tasks)
	{
		if (task2 == null)
		{
			throw new ArgumentException(Environment.GetResourceString("Task_MultiTaskContinuation_NullTask"), "tasks");
		}
		list.Add(task2);
	}
	return Task.InternalWhenAll(list.ToArray());
...
Рейтинг: 0 / 0
Как делать кучу параллельных IO-Bound операций ?
    #39690515
Фотография ЕвгенийВ
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Вообще, по хорошему, нужна система с обратной связью, т. е. на каждой итерации увеличиваешь количество потоков/тасков, до тез пор, пока растет итоговая производительность и не увеличиваешь/понижаешь когда итоговая производительность начинает падать.
await/async тут тебе не нужно.
Ну и как правило нужно улучшить производительность БД, созданием индексов/статистик/мат. view и протиранием монитора.
...
Рейтинг: 0 / 0
Как делать кучу параллельных IO-Bound операций ?
    #39690524
Фотография hVostt
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
ЕвгенийВawait/async тут тебе не нужно

При наличии I/O весьма полезен.

Систему с обратной связью покажи )
...
Рейтинг: 0 / 0
Как делать кучу параллельных IO-Bound операций ?
    #39690526
WaspNewCore
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
ЕвгенийВ,

Интересно. Какой прок тогда принимать IEnumerable, если все сводится к ToArray.
Наверное ради того, чтобы собрать результат работы всех тасков, чтобы потом выбросить AggregateException. Но даже в этом случае не лучше ли было работать с List в который динамически добавлять каждую задачу после ее ожидания.

Ну. Не нам судить профи, скорее всего причина все таки у них есть, просто нужно изучать код. Причина скорее всего есть, я верю в профессионализм разрабов C#.

В любом случае, как я сказал выше, на достаточно небольшом числе задач, указанный метод вполне рабочий. Буду его пока придерживаться.
...
Рейтинг: 0 / 0
Как делать кучу параллельных IO-Bound операций ?
    #39690546
Фотография ЕвгенийВ
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
hVostt
Систему с обратной связью покажи )
Как то так.
Код: c#
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
57.
58.
59.
60.
61.
62.
63.
64.
65.
66.
67.
68.
69.
70.
71.
72.
73.
74.
75.
76.
77.
78.
79.
80.
81.
82.
83.
84.
85.
86.
87.
88.
89.
90.
91.
92.
93.
94.
95.
96.
97.
98.
99.
100.
101.
102.
103.
104.
105.
106.
107.
108.
109.
110.
111.
112.
113.
114.
115.
116.
117.
118.
119.
120.
121.
122.
123.
124.
125.
126.
127.
using System;
using System.Linq;
using System.Threading.Tasks;
using System.Collections.Concurrent;
using System.Diagnostics;
using Profiling;
using System.IO;

namespace Leo.Loader.Service
{
    public class RmqThreadManagerConsumer2<T> where T : class
    {
        static object Locker = new object();
        Func<Tuple<T[], string, Action>> _getter;
        Action<T> _runner;
        ConcurrentQueue<double> concurrentQueue = new ConcurrentQueue<double>();
        System.Timers.Timer timer2;
        volatile int Counter;
        Logics.FileWriter inputFileWriter;// = new Logics.FileWriter(System.Configuration.ConfigurationManager.AppSettings[])
        Logics.FileWriter errorFileWriter;
        public RmqThreadManagerConsumer2(Func<Tuple<T[], string, Action>> getter, Action<T> runner, string queueName, string inputRoot, string errorRoot)
        {
            _getter = getter ?? throw new ArgumentNullException(nameof(getter));
            _runner = runner ?? throw new ArgumentNullException(nameof(runner));
            if (string.IsNullOrEmpty(queueName))
                throw new ArgumentNullException(nameof(queueName));
            inputFileWriter = new Logics.FileWriter(inputRoot);
            errorFileWriter = new Logics.FileWriter(errorRoot);
            timer2 = new System.Timers.Timer(1000);
            timer2.SynchronizingObject = new SynchronizeInvoke();
            timer2.Elapsed += (o, a) => 
            {
                Run();
            };
            timer2.Start();
        }

        void Run()
        {
            lock (Locker)
            {
                Measure();
            }
        }
        void Grow(double d)
        {
            var agg = concurrentQueue.Count < 1 ? 1 : concurrentQueue.Count;
            var summ = concurrentQueue.Sum();
            var avg = summ / agg;
            Console.WriteLine("avg-" + avg);
            if ((1.1*d) > avg)//время выросло
            {
                if (Counter > 2)
                    Counter--;
            }
            else//время уменьшилось
            {
                if (Counter < 100)
                    Counter++;
            }
            concurrentQueue.Enqueue(d);
            if (agg > 100)
            {
                concurrentQueue.TryDequeue(out double x);
            }
        }
        void Measure()
        {
            Tuple<T[], string, Action> t = null;
            try
            {
                var c = Counter;

                while (c-- >= 0)
                {
                    Stopwatch sv = new Stopwatch();
                    bool IsNull = false;
                    t = _getter();
                    var res = Task.Factory.StartNew(() =>
                    {
                        sv.Start();
                        
                        if (t?.Item1 == null)
                        {
                            IsNull = true;
                            return;
                        }
                        foreach (var x in t.Item1)
                        {
                            _runner(x);
                        }
                        if (!string.IsNullOrEmpty(t.Item2))
                        {
                            lock (inputFileWriter)
                            {
                                inputFileWriter.Write(DateTime.Now, t.Item2, Path.GetRandomFileName() + ".json");
                            }
                        }
                    }).ContinueWith(task =>
                    {
                        var exc = task.Exception;
                        sv.Stop();
                        if (!IsNull)
                            Grow(sv.ElapsedMilliseconds);
                    });
                    var sdf = res.Exception;
                    if (sdf != null)
                    {
                        Logger.Instance.Error(sdf);
                        continue;
                    }
                    //помечаем сообщение обработанным
                    t?.Item3?.Invoke();
                    //res.Wait();
                }
            }
            catch (Exception exc)
            {
                Logger.Instance.Error(exc, "Ошибка в Measure");
                lock (errorFileWriter)
                {
                    errorFileWriter.Write(DateTime.Now, t?.Item2, Path.GetRandomFileName() + ".json");
                }
            }
        }
    }
}
...
Рейтинг: 0 / 0
Как делать кучу параллельных IO-Bound операций ?
    #39690880
Фотография hVostt
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
ЕвгенийВ,

спасибо, потестирую в ближайшее время :)
...
Рейтинг: 0 / 0
Как делать кучу параллельных IO-Bound операций ?
    #39690890
Фотография ЕвгенийВ
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
hVosttЕвгенийВ,

спасибо, потестирую в ближайшее время :)
Хорошо. Это только идея и первая реализация в лоб.
...
Рейтинг: 0 / 0
57 сообщений из 57, показаны все 3 страниц
Форумы / WinForms, .Net Framework [игнор отключен] [закрыт для гостей] / Как делать кучу параллельных IO-Bound операций ?
Целевая тема:
Создать новую тему:
Автор:
Закрыть
Цитировать
Найденые пользователи ...
Разблокировать пользователей ...
Читали форум (0):
Пользователи онлайн (0):
x
x
Закрыть


Просмотр
0 / 0
Close
Debug Console [Select Text]