Недавно с коллегой поспорили на тему производительности в математических вычислениях. Задачка была довольно простая — перемножить 2 матрицы комплексных числе размером 600 на 600. Он писал на C#, я на C++. Изначально ожидалось, что C++ будет быстрее, но тем не менее результаты поразили нас обоих. Код на C++ работал в 9 (!) раз быстрее чем аналогичный на шарпе. И это после того, как мы включили все возможные оптимизации, по несколько раз пересмотрели сам код и оптимизировали все что могли.
От форумчан поступило предложение выложить тот и другой код, чтобы совместными услилиями C# догнал C++.
Требования обозначались простые:
1) Не использовать ничего стороннего. В случае C# — только managed код (никакого interop & unsafe) + .Net framework, в случае C++ — только сам язык + STL.
2) Код должен быть читаемым. Т.е. не стоит запихивать все в один метод только из-за того, чтобы устранить вызовы функций.
3) Настройка параметров проекта (компилятора, линкера) — произвольная. Так в C++ проекте я указал размер стека в 64 мб.
В приложенных файлах оба решения, открывать можно/нужно в 2005 студии.
Здравствуйте, AndreyR7, Вы писали:
AR>Код на C++ работал в 9 (!) раз быстрее чем аналогичный на шарпе.
Мне достаточно было посмотреть на ассемблерный код, который генерирует JIT, и все сразу стало понятно и без тестов. Смотреть Оптимизация в .NET]тут[/url].
Если тестировать каждый отдельный компонент, то результаты тестирования сравнимы. Ибо никто же не будет тестировать inline метод с обычным вызовом? Но когда все в начинает работать в комплексе, то...
Кстати, пресловутых преимуществ работы на другой архитектуре я не заметил Новые команды почти не используются (ибо это зачастую либо экзотика, либо надо изначально писать код под эти команды). Более того, смотрел на то, что генерирует .NET для 64 бит. Получаем:
1. По стандарту вызова метода, параметры передаются в регистрах.
2. JIT компилятор упорно размещает переменные производные от class в стеке (может это надо для сборки мусора или еще чего, не знаю).
Поэтому, при вызове метода происходило следующее
1. Аргументы копировались в регистры
2. Вызов функции
3. Инициализация cтека внутри функции
4. Копирование аргументов из регистров в стек
5. Выполнение кода функции
inline void multiply(const matrix<T, N>& second, matrix<T, N>& result) const
{
T value;
T temp;
for(int i = 0; i < N; ++i)
{
for(int j = 0; j < N; ++j)
{
value = T();
for(int k = 0; k < N; ++k)
{
temp = at(i, k);
temp *= at(k, j);
value += temp;
}
result.at(i, j) = value;
}
}
}
public static MatrixDouble operator *(MatrixDouble left, MatrixDouble right)
{
Require.IsTrue(left.Width == right.Height);
MatrixDouble result = new MatrixDouble(right.Width, left.Height);
for (int x = 0; x < result.Width; x++)
for (int y = 0; y < result.Height; y++)
for (int n = 0; n < left.Width; n++)
result[x, y] += left[n, y] * right[x, n];
return result;
}
1)Вариант на С++ написан не верно.
2)Вариант на С++ отдельно оптимизировали.
3)Многомерные массивы в .NET тормозят. Нужно использовать одномарные.
4)Используются разные генераторы псевдослучайных чисел.
5)Нет полного вывода результатов.
Короче сначала исправьте тесты, а потом можно будет вернутся к разговору.
... << RSDN@Home 1.2.0 alpha rev. 745>>
Пусть это будет просто:
просто, как только можно,
но не проще.
(C) А. Эйнштейн
Здравствуйте, WolfHound, Вы писали:
WH>1)Вариант на С++ написан не верно.
В чем ошибка?
WH>2)Вариант на С++ отдельно оптимизировали.
Я в Шарповом коде убрал оптимизацию, потому что она элементарна и JIT должен делать ее сам.
WH>3)Многомерные массивы в .NET тормозят. Нужно использовать одномарные.
Ты путаешь [][] с [,]. Первый вариант работает быстрее, он и использован.
WH>4)Используются разные генераторы псевдослучайных чисел.
Замеряется скорость перемножения, скорость заполнения матриц числами не учитывается.
WH>5)Нет полного вывода результатов.
Вывод результатов на скорость перемножения не влияет.
MatFiz wrote: > WH>3)Многомерные массивы в .NET тормозят. Нужно использовать одномарные. > Ты путаешь [][] с [,]. Первый вариант работает быстрее, он и использован.
В .NET нет настоящих многомерных массивов — поэтому формат обращения,
AFAIR, никакой роли не играет.
Здравствуйте, WolfHound, Вы писали:
WH>1)Вариант на С++ написан не верно.
Неверно математически?
WH>2)Вариант на С++ отдельно оптимизировали.
Да, выносил переменную за циклы и заменял + на +=. Дало немного. Правда я удивился, что что-то все-таки дало.
WH>3)Многомерные массивы в .NET тормозят. Нужно использовать одномарные.
Все массивы в .Net тормозят Некоторые больше, некоторые меньше.
WH>4)Используются разные генераторы псевдослучайных чисел.
Не имеет значения — время генерации не учитывается раз; замеры проводили на матрица заполненной 1+1i
WH>5)Нет полного вывода результатов.
Также не имеет значения.
Здравствуйте, Cyberax, Вы писали:
C>В .NET нет настоящих многомерных массивов — поэтому формат обращения, C>AFAIR, никакой роли не играет.
Ошибаешся, в .net в отличии от плюсов есть настоящие многомерные массивы. Это про [,]. Именно они и тормозят т.к. для них нет специальных инструкций msil (для одномерных есть) и приходится вызывать статический метод класса Array (В деталях могу ошибаться)
[][] — это псевдомногомерные массивы или jagged arrays. Они не должны тормозить так сильно как истинно многомерные.
AndreyR7 wrote: > Ошибаешся, в .net в отличии от плюсов есть настоящие многомерные > массивы. Это про [,]. Именно они и тормозят т.к. для них нет специальных > инструкций msil (для одномерных есть) и приходится вызывать статический > метод класса Array (В деталях могу ошибаться)
Т.е. оно реально в виде одного блока в памяти сидит, а не просто
засахареные jagged arrays?
> [][] — это псевдомногомерные массивы или jagged arrays. Они не должны > тормозить так сильно как истинно многомерные.
По идее, как раз настоящие многомерные массивы должны работать быстрее
(так как мы убираем лишнюю косвенность). В Фортране, например, это
именно так и есть.
Здравствуйте, MatFiz, Вы писали:
WH>>1)Вариант на С++ написан не верно. MF>В чем ошибка?
Вторую матрицу кто использовать будет?
WH>>2)Вариант на С++ отдельно оптимизировали. MF>Я в Шарповом коде убрал оптимизацию, потому что она элементарна и JIT должен делать ее сам.
А почему она есть в С++ном?
WH>>4)Используются разные генераторы псевдослучайных чисел. MF>Замеряется скорость перемножения, скорость заполнения матриц числами не учитывается. WH>>5)Нет полного вывода результатов. MF>Вывод результатов на скорость перемножения не влияет.
Без этого нельзя проверить корректность теста.
... << RSDN@Home 1.2.0 alpha rev. 745>>
Пусть это будет просто:
просто, как только можно,
но не проще.
(C) А. Эйнштейн
Здравствуйте, AndreyR7, Вы писали:
WH>>1)Вариант на С++ написан не верно. AR>Неверно математически?
Ага.
AR>Все массивы в .Net тормозят Некоторые больше, некоторые меньше.
Если их готовить правильно нет.
... << RSDN@Home 1.2.0 alpha rev. 745>>
Пусть это будет просто:
просто, как только можно,
но не проще.
(C) А. Эйнштейн
Здравствуйте, Cyberax, Вы писали:
C>По идее, как раз настоящие многомерные массивы должны работать быстрее (так как мы убираем лишнюю косвенность). В Фортране, например, это именно так и есть.
А в .NET они тормозят. Ибо мелкомягкие не осилили их оптимизировать.
... << RSDN@Home 1.2.0 alpha rev. 745>>
Пусть это будет просто:
просто, как только можно,
но не проще.
(C) А. Эйнштейн
Здравствуйте, WolfHound, Вы писали:
WH>>>1)Вариант на С++ написан не верно. MF>>В чем ошибка? WH>Вторую матрицу кто использовать будет?
Сорри
Поправил — на время заметно не повлияло
Здравствуйте, AndreyR7, Вы писали:
AR>Коллеги, присоединяйтесь
AR>Здесь C++ AR>Здесь C#
Сделайте нормальную оптимизацию и там, и там. Алгоритмическую, то бишь.
Тут дело в том, что вторая матрица обходится полностью, для вычисления каждой строки результирующей матрицы. Причем обход производится не в последовательном режиме.
А вот вы возьмите и транспонируйте правую матрицу, пока вычисляется первая строка результата. А потом транспонируйте обратно, когда будете вычислять последнюю строку. Разница с первоначальным вариантом будет ой-ой-ой Тогда можно будет смотреть кто кого и во сколько раз уел.
Здравствуйте, andrey.desman, Вы писали:
AD>Сделайте нормальную оптимизацию и там, и там. Алгоритмическую, то бишь. AD>Тут дело в том, что вторая матрица обходится полностью, для вычисления каждой строки результирующей матрицы. Причем обход производится не в последовательном режиме. AD>А вот вы возьмите и транспонируйте правую матрицу, пока вычисляется первая строка результата. А потом транспонируйте обратно, когда будете вычислять последнюю строку. Разница с первоначальным вариантом будет ой-ой-ой Тогда можно будет смотреть кто кого и во сколько раз уел.
На самом деле, идея в том, что я хочу просто описать алгоритм наиболее чистым способом, а оптимизатор должен сделать свое дело и сгенерировать максимально быстрый код, выкинув лишние проверки, развернув циклы, заинлайнив код и т.п.
У .Net-а с этим оказывается намного хуже, чем у плюсового компилера, хотя MS активно внедряет мнение о том, что JIT лучше плюсов, так как может компилить под конкретную платформу и использовать ее особенности. Получается, что это просто маркетинг, не имеющий за собой реальных оснований.
MF>У .Net-а с этим оказывается намного хуже, чем у плюсового компилера, хотя MS активно внедряет мнение о том, что JIT лучше плюсов, так как может компилить под конкретную платформу и использовать ее особенности. Получается, что это просто маркетинг, не имеющий за собой реальных оснований.
получается просто пример, который "высосан из пальца". Для наиболее типичных задач текущей потребности рынка , дотнета и джавы хватает вполне и они справляются лучше, чем плюсы.
уже кучу раз обсуждали "плюсы vs остальное". Не надоело?
кому и что есть желание доказать?
найти себе аргументацию остаться на плюсах, и не переходить на джаву и дотнет?
"пока плюсовики из Виларибо распухают от осознания соственной важности, пытаются мериться пиписками с другими, пытаются изобретать заново "велосипед" на проекте, джависты/дотнетчики из Вилабаджио давно сдали текущие проекты и получили новые. Бизнес, однако. Скорость, удобство разработки, однако. И заказчик, однако, уйдет к ребятам из Вилабаджио."
П.С. мот устроишь холивар "асм вс. плюсы"? Асм ваще там порвет плюсы аки тузик грелку. Бросишь всё, уйдешь на асм?
П.П.С. джава/дотнет — это не столько какой-то там Java-язык, C#-язык, это — ПЛАТФОРМЫ. Вот, что ключевое. у плюсов просто нет вменяемой платформы. Вот такие дела.