распараллеливание и ресурсоёмкость
От: vvv848165@ya.ru  
Дата: 16.04.19 08:38
Оценка: 3 (1)
когда-то давно распараллеливал на С++ при помощи CreateThread — было просто чудо!
работа идеально делилась на количество потоков — ресурсоёмкости не добавлялась

на С# паралелил при помощи Parallel.For — какая-то жуть — накидывает 5% нагрузки в лучшем случае
а иногда так глючит что распараллеленый код выполняется за большее количество времени

что за хрень из-за чего?

пример кода
double[][] tmp = new double[no_channel][];

//for(int ic=0;ic<no_channel;ic++){
Parallel.For(0, no_channel, (Action<int>)((ic) =>
{
tmp[ic] = new double[no_point];
fs[ic].Next(ref tmp[ic], src);
}));
//}
Next — ресурсоёмкая функция с циклами
no_channel — от 8 до 16 (процессор 4х ядерный)
В чём косяк может быть?

Можно ли как нибудь передать ссылочную переменную?
Re: распараллеливание и ресурсоёмкость
От: Sharov Россия  
Дата: 16.04.19 09:47
Оценка: 1 (1)
Здравствуйте, vvv848165@ya.ru, Вы писали:

VYR>когда-то давно распараллеливал на С++ при помощи CreateThread — было просто чудо!

VYR>работа идеально делилась на количество потоков — ресурсоёмкости не добавлялась

VYR>на С# паралелил при помощи Parallel.For — какая-то жуть — накидывает 5% нагрузки в лучшем случае

VYR>а иногда так глючит что распараллеленый код выполняется за большее количество времени

VYR>что за хрень из-за чего?


VYR>пример кода

VYR> double[][] tmp = new double[no_channel][];

VYR> //for(int ic=0;ic<no_channel;ic++){

VYR> Parallel.For(0, no_channel, (Action<int>)((ic) =>
VYR> {
VYR> tmp[ic] = new double[no_point];
VYR> fs[ic].Next(ref tmp[ic], src);
VYR> }));
VYR> //}
VYR>Next — ресурсоёмкая функция с циклами
VYR>no_channel — от 8 до 16 (процессор 4х ядерный)
VYR>В чём косяк может быть?

Конкретные цифры есть -- однопоточный вариант супротив многопоточного?


VYR>Можно ли как нибудь передать ссылочную переменную?


Куда передать?

Нужно больше инф-ии о проблеме. Попробуйте поиграть с параметро MaxParallelDegree, и сделать его, например, 4 (по числу ядер).
Кодом людям нужно помогать!
Re: распараллеливание и ресурсоёмкость
От: RushDevion Россия  
Дата: 16.04.19 10:22
Оценка: 2 (1) +1
Здравствуйте, vvv848165@ya.ru, Вы писали:

VYR>на С# паралелил при помощи Parallel.For — какая-то жуть — накидывает 5% нагрузки в лучшем случае

VYR>а иногда так глючит что распараллеленый код выполняется за большее количество времени
VYR>что за хрень из-за чего?

Дело в том, что Parallel.For — под капотом довольно сложная вещь.
Помимо собственно параллельного выполнения она:
-Умеет обрабатывать exceptions и останавливает рабочие потоки, если в одном из них произошла ошибка
-Поддерживает несколько вариантов останова цикла (CancellationToken, ParallelLoopState.Break/Stop)
-Пытается оптимально балансить нагрузку, разбивая входные данные на диапазоны для независимой обработки и даже реализуя упрощенный work-stealing между этими диапазонами

Понятное дело, все это вносит накладные расходы.
Также, думаю понятно, что каким-бы умным ни был алгоритм балансировки в Parallel.For, он не вытянет против самописного алгоритма,
который досконально знает особенности входных данных, использует выделенные потоки (а не потоки из тред-пула) и в идеальном случае может вообще не тратиться на синхронизацию.

VYR>когда-то давно распараллеливал на С++ при помощи CreateThread — было просто чудо!

VYR>работа идеально делилась на количество потоков — ресурсоёмкости не добавлялась
Используй старый добрый System.Threading.Thread.
Отредактировано 16.04.2019 11:07 RushDevion . Предыдущая версия .
Re: распараллеливание и ресурсоёмкость
От: Sinclair Россия https://github.com/evilguest/
Дата: 16.04.19 10:28
Оценка:
Здравствуйте, vvv848165@ya.ru, Вы писали:
VYR>Можно ли как нибудь передать ссылочную переменную?
Непонятно, куда передавать. Непонятно, что такое fs[ic], и почему в Next нужно передавать ref double[].
Для начала проверьте, что код делает то, что нужно, а не генерирует мусор.
Затем можно бенчмаркнуть три варианта кода при помощи стандартных средств:
— линейный
— честно-параллельный, через ручной System.Threading.Thread
— Parallel.For

После этого можно уже делать какие-то выводы и выбирать направление дальнейших раскопок.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[2]: распараллеливание и ресурсоёмкость
От: vvv848165@ya.ru  
Дата: 16.04.19 10:53
Оценка:
Здравствуйте, Sharov, Вы писали:


S>Куда передать?


ссылку в лямда выражение (она просто так не передаётся)
Re[2]: распараллеливание и ресурсоёмкость
От: vvv848165@ya.ru  
Дата: 16.04.19 11:08
Оценка:
Здравствуйте, Sinclair, Вы писали:


S>Непонятно, куда передавать. Непонятно, что такое fs[ic], и почему в Next нужно передавать ref double[].

ссылку в лямда выражение (она просто так не передаётся)
а что с ref double[]?
S>Для начала проверьте, что код делает то, что нужно, а не генерирует мусор.
код отлажен и проверен ...
no_channel — от 8 до 16 (процессор 4х ядерный)
src.LongLength = 8192

public void Init(string[] names)
        {
            no_channel = names.Length;
            fs = new FilterSections[no_channel];
            gain = new double[no_channel];

            for (int i = 0; i < no_channel; i++)
            {
                fs[i] = new FilterSections();
                gain[i] = 1.0d;
                Coeff coeff = FilterCoefficient.GetCoefficients(names[i]);
                fs[i].Init(coeff);
            }
        }     

class FilterSections
    {
        private Filter[] filters = null;
        private int no_filtres = 0;

        public FilterSections()
        { 
        }

        public void Init(Coeff coef) 
        {
            no_filtres=coef.no_filtres;
            filters = new Filter[no_filtres];

            for (int i = 0; i < no_filtres; i++)
                filters[i] = new Filter();

            for (int i = 0; i < no_filtres; i++)
                filters[i].Init(coef.pre_gain[i], coef.DEN[i * 2 + 0], coef.DEN[i * 2 + 1],coef.NUM[i]);
        }
        public void Next(ref double[] y, double[] x)
        {
            int no=x.Length;
            double[] val = new double[no];
            
            for (int j = 0; j < no; j++)
                val[j] = x[j];

            for (int i = 0; i < no_filtres; i++)
                filters[i].Next(ref val, val);
            
            for (int j = 0; j < no; j++)
                y[j] = val[j];
            
            /*
            int no_filtres_m_1 = no_filtres - 1;
            filters[0].Next(ref val, x);
            for (int i = 1; i < no_filtres_m_1; i++)
                filters[i].Next(ref val, val);
            filters[no_filtres_m_1].Next(ref y, val);
             */
        }
    }

public class Filter
    {
        private double v1 = 0.0d, v2 = 0.0d;
        private double pregain, a2, a3, b2;

        public Filter()
        {
        }

        public void Init(double _pregain, double _a2, double _a3, double _b2)
        {
            pregain = _pregain; a2 = _a2; a3 = _a3; b2 = _b2;
        }

        public void Next(ref double[] y, double[] x)
        {
            for (long i = 0; i < x.LongLength; i++)
            {
                double v0 = pregain * x[i] - a2 * v1 - a3 * v2;
                y[i] = v0 + b2 * v1 + v2;

                v2 = v1; v1 = v0;
            }
        }
    }
Отредактировано 16.04.2019 12:07 vvv848165@ya.ru . Предыдущая версия .
Re[3]: распараллеливание и ресурсоёмкость
От: Sharov Россия  
Дата: 16.04.19 11:59
Оценка:
Здравствуйте, vvv848165@ya.ru, Вы писали:

VYR>Здравствуйте, Sinclair, Вы писали:



S>>Непонятно, куда передавать. Непонятно, что такое fs[ic], и почему в Next нужно передавать ref double[].

VYR> ссылку в лямда выражение (она просто так не передаётся)
VYR> а что с ref double[]?
S>>Для начала проверьте, что код делает то, что нужно, а не генерирует мусор.
VYR> код отлажен и проверен ...
VYR>no_channel — от 8 до 16 (процессор 4х ядерный)
VYR>src.LongLength = 8192

VYR>public void Init(string[] names)

VYR> {
VYR> no_channel = names.Length;
VYR> fs = new FilterSections[no_channel];
VYR> gain = new double[no_channel];

VYR> for (int i = 0; i < no_channel; i++)

VYR> {
VYR> fs[i] = new FilterSections();
VYR> gain[i] = 1.0d;
VYR> Coeff coeff = FilterCoefficient.GetCoefficients(names[i]);
VYR> fs[i].Init(coeff);
VYR> }
VYR> }

VYR>class FilterSections

VYR> {
VYR> private Filter[] filters = null;
VYR> private int no_filtres = 0;

VYR> public FilterSections()

VYR> {
VYR> }

VYR> public void Init(Coeff coef)

VYR> {
VYR> no_filtres=coef.no_filtres;
VYR> filters = new Filter[no_filtres];

VYR> for (int i = 0; i < no_filtres; i++)

VYR> filters[i] = new Filter();

VYR> for (int i = 0; i < no_filtres; i++)

VYR> filters[i].Init(coef.pre_gain[i], coef.DEN[i * 2 + 0], coef.DEN[i * 2 + 1],coef.NUM[i]);
VYR> }
VYR> public void Next(ref double[] y, double[] x)
VYR> {
VYR> int no=x.Length;
VYR> double[] val = new double[no];

VYR> for (int j = 0; j < no; j++)

VYR> val[j] = x[j];

VYR> for (int i = 0; i < no_filtres; i++)

VYR> filters[i].Next(ref val, val);

VYR> for (int j = 0; j < no; j++)

VYR> y[j] = val[j];

VYR> /*

VYR> int no_filtres_m_1 = no_filtres — 1;
VYR> filters[0].Next(ref val, x);
VYR> for (int i = 1; i < no_filtres_m_1; i++)
VYR> filters[i].Next(ref val, val);
VYR> filters[no_filtres_m_1].Next(ref y, val);
VYR> */
VYR> }
VYR> }

VYR>public class Filter

VYR> {
VYR> private double v1 = 0.0d, v2 = 0.0d;
VYR> private double pregain, a2, a3, b2;

VYR> public Filter()

VYR> {
VYR> }

VYR> public void Init(double _pregain, double _a2, double _a3, double _b2)

VYR> {
VYR> pregain = _pregain; a2 = _a2; a3 = _a3; b2 = _b2;
VYR> }

VYR> public void Next(ref double[] y, double[] x)

VYR> {
VYR> for (long i = 0; i < x.LongLength; i++)
VYR> {
VYR> double v0 = pregain * x[i] — a2 * v1 — a3 * v2;
VYR> y[i] = v0 + b2 * v1 + v2;

VYR> v2 = v1; v1 = v0;

VYR> }
VYR> }
VYR> }


1)Просьба, используйте форматирование для кода -- тэги c#.
2)Если код числодробилка, то возможно параллелить его особого смысла нет, дольше времени уйдет на переключение контекстов\управление потоками.
Кодом людям нужно помогать!
Re[4]: распараллеливание и ресурсоёмкость
От: vvv848165@ya.ru  
Дата: 16.04.19 12:18
Оценка:
S>2)Если код числодробилка, то возможно параллелить его особого смысла нет, дольше времени уйдет на переключение контекстов\управление потоками.

Почему??? переключение контекстов???
на С++ числодробилки хорошо паралелились даже без SSE
Отредактировано 16.04.2019 12:19 vvv848165@ya.ru . Предыдущая версия .
Re: распараллеливание и ресурсоёмкость! Караул!!!
От: vvv848165@ya.ru  
Дата: 16.04.19 12:55
Оценка: -1
Караул https://docs.microsoft.com/en-us/dotnet/standard/parallel-programming/how-to-write-a-simple-parallel-for-loop

тестировал Matrix and stopwatch example
40% ресурсов при распаралеливании теряется в никуда
Отредактировано 16.04.2019 12:56 vvv848165@ya.ru . Предыдущая версия .
Re[5]: распараллеливание и ресурсоёмкость
От: Sharov Россия  
Дата: 16.04.19 12:55
Оценка:
Здравствуйте, vvv848165@ya.ru, Вы писали:


S>>2)Если код числодробилка, то возможно параллелить его особого смысла нет, дольше времени уйдет на переключение контекстов\управление потоками.


VYR>Почему??? переключение контекстов???

VYR>на С++ числодробилки хорошо паралелились даже без SSE

Я не уверен\ не знаю, использует ли CLR SSE по умолчанию? Это раз. А два, ну какой-то managed overhead на управление потоками все же присутсвтует.
А можно все-таки цифры производительности без и с многопоточностью увидеть?
Кодом людям нужно помогать!
Re[6]: распараллеливание и ресурсоёмкость
От: vvv848165@ya.ru  
Дата: 16.04.19 13:01
Оценка:
Здравствуйте, Sharov, Вы писали:

S>Здравствуйте, vvv848165@ya.ru, Вы писали:



S>>>2)Если код числодробилка, то возможно параллелить его особого смысла нет, дольше времени уйдет на переключение контекстов\управление потоками.


VYR>>Почему??? переключение контекстов???

VYR>>на С++ числодробилки хорошо паралелились даже без SSE

S>Я не уверен\ не знаю, использует ли CLR SSE по умолчанию? Это раз. А два, ну какой-то managed overhead на управление потоками все же присутсвтует.

S>А можно все-таки цифры производительности без и с многопоточностью увидеть?

запускаю с периодичностью 60 ms загрузка проца с распараллеливанием 14% без 12.5 %
Re: распараллеливание и ресурсоёмкость
От: bzig  
Дата: 16.04.19 14:13
Оценка:
VYR>на С# паралелил при помощи Parallel.For — какая-то жуть — накидывает 5% нагрузки в лучшем случае

Как у тебя ThreadPool настроен? Что возвращают GetAvailableThreads/GetMaxThreads ?

https://docs.microsoft.com/en-us/dotnet/api/system.threading.threadpool?redirectedfrom=MSDN&amp;view=netframework-4.7.2
Re: распараллеливание и ресурсоёмкость
От: Sharowarsheg  
Дата: 16.04.19 14:24
Оценка: +2
Здравствуйте, vvv848165@ya.ru, Вы писали:

VYR>когда-то давно распараллеливал на С++ при помощи CreateThread — было просто чудо!

VYR>работа идеально делилась на количество потоков — ресурсоёмкости не добавлялась

VYR>на С# паралелил при помощи Parallel.For — какая-то жуть — накидывает 5% нагрузки в лучшем случае

VYR>а иногда так глючит что распараллеленый код выполняется за большее количество времени

VYR>что за хрень из-за чего?


Из-за того, что Parallel.For большая абстракция, чем потоки. Когда она работает, всё отлично — чем больше абстракция, тем более отлично. А когда не работает, то чем больше абстракция, тем ты хрен поймёшь, что с ней не так.

Напиши на потоках, и посмотри, что получится. Я подозреваю, что функция слишком быстрая, или garbage collection мозги пудрит, но по крайней мере, когда ты напишешь на потоках (которые Thread, такие же практически, как в C++), будет виднее.
Re[2]: распараллеливание и ресурсоёмкость
От: VVVa  
Дата: 16.04.19 15:42
Оценка:
Здравствуйте, bzig, Вы писали:

VYR>>на С# паралелил при помощи Parallel.For — какая-то жуть — накидывает 5% нагрузки в лучшем случае


B>Как у тебя ThreadPool настроен? Что возвращают GetAvailableThreads/GetMaxThreads ?


B>https://docs.microsoft.com/en-us/dotnet/api/system.threading.threadpool?redirectedfrom=MSDN&amp;view=netframework-4.7.2



            int aworkerThreads;
            int acompletionPortThreads;
            ThreadPool.GetAvailableThreads(out aworkerThreads,out acompletionPortThreads);
            int mworkerThreads;
            int mcompletionPortThreads;
            ThreadPool.GetMaxThreads(out mworkerThreads, out mcompletionPortThreads);


aworkerThreads 1023 int
acompletionPortThreads 1000 int
mworkerThreads 1023 int
mcompletionPortThreads 1000 int


пробовал даже так — все ровно ощутимая добавка к загрузке


            ParallelOptions po=new ParallelOptions();
            po.MaxDegreeOfParallelism=4;
            Parallel.For(0, no_channel,po,(Action<int>)((i) => {
                tmp[i] = new double[no_point];
                fs[i].Next(ref tmp[i], src); 
            }));
Отредактировано 16.04.2019 15:50 VVVa . Предыдущая версия .
Re[3]: распараллеливание и ресурсоёмкость
От: bzig  
Дата: 16.04.19 15:54
Оценка: +1
VVV>пробовал даже так — все ровно ощутимая добавка к загрузке
VVV> po.MaxDegreeOfParallelism=4;

Ну разве ты не добиваешься загрузки? Если ты имеешь ввиду, что лучше не стало, значит у тебя где-то лок глобальный и задачи его по очереди ждут
Re[3]: распараллеливание и ресурсоёмкость
От: Teolog  
Дата: 16.04.19 17:59
Оценка: 3 (1) +2
Здравствуйте, vvv848165@ya.ru, Вы писали:
1)Просто интересно, что именно должна делать строка
>>v2 = v1; v1 = v0; если v1,v2 -филды объекта ипользуемого в n потоках одновременно.
2)зачем ref double[]. ref нужен если предпологаеться пере-аллоцировать массив внутри функции а не изменить содержимое, если если лямбда сопротивляеться (а с чего бы), значит вы делаете что то странное. лямбды в циклах любят сюрпризы, и иногда делают не совсем то что кажется.
3)LongLength бесполезен, снаружи int, и вообще если нужна производительность тут самое место для fixed unsafe.
4)Array.CopyTo делает то же что и половина циклов но несколько быстрее, кроме того фильтру не нужны два массива, можно тупо скопировать начальное состояние в выходной массив, и потом последовательно применить фильтры прямо к нему, передавая и итерируя только один массив, промежуточный который создаеться в кажном Next, не нужен.
Re[4]: распараллеливание и ресурсоёмкость
От: vvv848165@ya.ru  
Дата: 17.04.19 05:15
Оценка:
Здравствуйте, Teolog, Вы писали:

T>Здравствуйте, vvv848165@ya.ru, Вы писали:

T>1)Просто интересно, что именно должна делать строка
нужны два предыдущих внутренних отсчёта Direct form II https://ccrma.stanford.edu/~jos/fp/Direct_Form_II.html (для каждого фильтра свой — кофициенты разные)

>>>v2 = v1; v1 = v0; если v1,v2 -филды объекта ипользуемого в n потоках одновременно.
Re[4]: распараллеливание и ресурсоёмкость
От: vvv848165@ya.ru  
Дата: 17.04.19 05:43
Оценка:
Здравствуйте, Teolog, Вы писали:


T>2)зачем ref double[]. ref нужен если предпологаеться пере-аллоцировать массив внутри функции а не изменить содержимое, если если лямбда сопротивляеться (а с чего бы), значит вы делаете что то странное. лямбды в циклах любят сюрпризы, и иногда делают не совсем то что кажется.

T>3)LongLength бесполезен, снаружи int, и вообще если нужна производительность тут самое место для fixed unsafe.
T>4)Array.CopyTo делает то же что и половина циклов но несколько быстрее, кроме того фильтру не нужны два массива, можно тупо скопировать начальное состояние в выходной массив, и потом последовательно применить фильтры прямо к нему, передавая и итерируя только один массив, промежуточный который создаеться в кажном Next, не нужен.

убрал ref и ненужное копирование — ситуация не поменялась
в метод лучше передавать два массива (исходный массив тоже нужен)
Re[4]: распараллеливание и ресурсоёмкость
От: vvv848165@ya.ru  
Дата: 17.04.19 05:45
Оценка:
Здравствуйте, bzig, Вы писали:


VVV>>пробовал даже так — все ровно ощутимая добавка к загрузке

VVV>> po.MaxDegreeOfParallelism=4;

B>Ну разве ты не добиваешься загрузки? Если ты имеешь ввиду, что лучше не стало, значит у тебя где-то лок глобальный и задачи его по очереди ждут



!!! если б я добивался загрузки я бы нашёл 1000 других способов !!!
Re[3]: распараллеливание и ресурсоёмкость
От: Sinclair Россия https://github.com/evilguest/
Дата: 17.04.19 06:30
Оценка:
Здравствуйте, vvv848165@ya.ru, Вы писали:

VYR> ссылку в лямда выражение (она просто так не передаётся)

Прекрасно передаётся.
VYR> а что с ref double[]?
Вы делаете много лишней работы.
Вот ваш код без мусора, лишних копирований и ref:

        public void Init(string[] names)
        {
            fs = (from name in names select new FilterSections(FilterCoefficient.GetCoefficients(name));
        }

        class FilterSections
        {
            private readonly Filter[] _filters;

            public FilterSections(Coeff coef)
            {
                _filters = new Filter[coef.no_filtres];

                for (int i = 0; i < _filters.Length; i++)
                    _filters[i] = new Filter(coef.pre_gain[i], coef.DEN[i * 2 + 0], coef.DEN[i * 2 + 1], coef.NUM[i]);
            }
            public void Next(double[] y, double[] x)
            {
                foreach(var filter in _filters)
                    filter.Next(y, x);
            }
        }
        public class Filter
        {
            private readonly double _pregain;
            private readonly double _a2;
            private readonly double _a3;
            private readonly double _b2;
            private double _v1, _v2;

            public Filter(double pregain, double a2, double a3, double b2)
            {
                _pregain = pregain; _a2 = a2; _a3 = a3; _b2 = b2;
            }

            public void Next(double[] y, double[] x)
            {
                for (long i = 0; i < x.LongLength; i++)
                {
                    double v0 = _pregain * x[i] - _a2 * _v1 - _a3 * _v2;
                    y[i] = v0 + _b2 * _v1 + _v2;
                    _v2 = _v1;
                    _v1 = v0;
                }
            }
        }

...
        {
            Parallel.For(0, no_channel, ((i) =>
            {
                tmp[i] = new double[8192];
                fs[i].Next(tmp[i], src);
            }));
        }


S>>Для начала проверьте, что код делает то, что нужно, а не генерирует мусор.

VYR> код отлажен и проверен ...
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.