когда-то давно распараллеливал на С++ при помощи 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х ядерный)
В чём косяк может быть?
Можно ли как нибудь передать ссылочную переменную?
Здравствуйте, 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 (по числу ядер).
Здравствуйте, 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.
Здравствуйте, vvv848165@ya.ru, Вы писали: VYR>Можно ли как нибудь передать ссылочную переменную?
Непонятно, куда передавать. Непонятно, что такое fs[ic], и почему в Next нужно передавать ref double[].
Для начала проверьте, что код делает то, что нужно, а не генерирует мусор.
Затем можно бенчмаркнуть три варианта кода при помощи стандартных средств:
— линейный
— честно-параллельный, через ручной System.Threading.Thread
— Parallel.For
После этого можно уже делать какие-то выводы и выбирать направление дальнейших раскопок.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
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;
}
}
}
Здравствуйте, 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)Если код числодробилка, то возможно параллелить его особого смысла нет, дольше времени уйдет на переключение контекстов\управление потоками.
S>>2)Если код числодробилка, то возможно параллелить его особого смысла нет, дольше времени уйдет на переключение контекстов\управление потоками.
VYR>Почему??? переключение контекстов??? VYR>на С++ числодробилки хорошо паралелились даже без SSE
Я не уверен\ не знаю, использует ли CLR SSE по умолчанию? Это раз. А два, ну какой-то managed overhead на управление потоками все же присутсвтует.
А можно все-таки цифры производительности без и с многопоточностью увидеть?
Здравствуйте, Sharov, Вы писали:
S>Здравствуйте, vvv848165@ya.ru, Вы писали:
S>>>2)Если код числодробилка, то возможно параллелить его особого смысла нет, дольше времени уйдет на переключение контекстов\управление потоками.
VYR>>Почему??? переключение контекстов??? VYR>>на С++ числодробилки хорошо паралелились даже без SSE
S>Я не уверен\ не знаю, использует ли CLR SSE по умолчанию? Это раз. А два, ну какой-то managed overhead на управление потоками все же присутсвтует. S>А можно все-таки цифры производительности без и с многопоточностью увидеть?
запускаю с периодичностью 60 ms загрузка проца с распараллеливанием 14% без 12.5 %
Здравствуйте, vvv848165@ya.ru, Вы писали:
VYR>когда-то давно распараллеливал на С++ при помощи CreateThread — было просто чудо! VYR>работа идеально делилась на количество потоков — ресурсоёмкости не добавлялась
VYR>на С# паралелил при помощи Parallel.For — какая-то жуть — накидывает 5% нагрузки в лучшем случае VYR>а иногда так глючит что распараллеленый код выполняется за большее количество времени
VYR>что за хрень из-за чего?
Из-за того, что Parallel.For большая абстракция, чем потоки. Когда она работает, всё отлично — чем больше абстракция, тем более отлично. А когда не работает, то чем больше абстракция, тем ты хрен поймёшь, что с ней не так.
Напиши на потоках, и посмотри, что получится. Я подозреваю, что функция слишком быстрая, или garbage collection мозги пудрит, но по крайней мере, когда ты напишешь на потоках (которые Thread, такие же практически, как в C++), будет виднее.
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
пробовал даже так — все ровно ощутимая добавка к загрузке
Здравствуйте, vvv848165@ya.ru, Вы писали:
1)Просто интересно, что именно должна делать строка >>v2 = v1; v1 = v0; если v1,v2 -филды объекта ипользуемого в n потоках одновременно.
2)зачем ref double[]. ref нужен если предпологаеться пере-аллоцировать массив внутри функции а не изменить содержимое, если если лямбда сопротивляеться (а с чего бы), значит вы делаете что то странное. лямбды в циклах любят сюрпризы, и иногда делают не совсем то что кажется.
3)LongLength бесполезен, снаружи int, и вообще если нужна производительность тут самое место для fixed unsafe.
4)Array.CopyTo делает то же что и половина циклов но несколько быстрее, кроме того фильтру не нужны два массива, можно тупо скопировать начальное состояние в выходной массив, и потом последовательно применить фильтры прямо к нему, передавая и итерируя только один массив, промежуточный который создаеться в кажном Next, не нужен.
Здравствуйте, 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 потоках одновременно.
T>2)зачем ref double[]. ref нужен если предпологаеться пере-аллоцировать массив внутри функции а не изменить содержимое, если если лямбда сопротивляеться (а с чего бы), значит вы делаете что то странное. лямбды в циклах любят сюрпризы, и иногда делают не совсем то что кажется. T>3)LongLength бесполезен, снаружи int, и вообще если нужна производительность тут самое место для fixed unsafe. T>4)Array.CopyTo делает то же что и половина циклов но несколько быстрее, кроме того фильтру не нужны два массива, можно тупо скопировать начальное состояние в выходной массив, и потом последовательно применить фильтры прямо к нему, передавая и итерируя только один массив, промежуточный который создаеться в кажном Next, не нужен.
убрал ref и ненужное копирование — ситуация не поменялась
в метод лучше передавать два массива (исходный массив тоже нужен)
VVV>>пробовал даже так — все ровно ощутимая добавка к загрузке VVV>> po.MaxDegreeOfParallelism=4;
B>Ну разве ты не добиваешься загрузки? Если ты имеешь ввиду, что лучше не стало, значит у тебя где-то лок глобальный и задачи его по очереди ждут
!!! если б я добивался загрузки я бы нашёл 1000 других способов !!!
Здравствуйте, 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> код отлажен и проверен ...
Уйдемте отсюда, Румата! У вас слишком богатые погреба.