Решил я приглядеться к .NET платформе для интеграции с движком. Раньше использовал тока для оконных приложений и работы с БД. Решил провести тест на производительность. Конечно самую ресурсоемкую часть я выкинул бы в DLL'ку на C++, но всеже решил проверить. Вообщем проверил умножение 10.000.000 матриц 4х4 и что-то меня совсем скорость разочаровала. Если на С++ у меня это занимает в среднем 1100 мсек то под NET это уже 11000 мсек. Плюс для описания матрицы я использовал класс, так как в перегруженных операциях я так понял нельзя использовать ref или out. Но в сторонних исходниках поглядел все используют структуры и в том числе и в XNA. Почему? если передавать не ссылке (описал матрицу структурой как в XNA) работет еще на 20 процентов медленнее. Отсюда у меня вопрос, почему такая большая разница? Почему всеже в XNA выбрали структуру для описания скажем матрици? Неужели просто по тупости? Или я не знаю каких-то настроек компилятора? Ну разница в 10 раз это уж слишком. Единственно ньанс который я могу представить это в том что данные матрицы в классе описаны 2д массивом 4х4 и это требует каких-то тактов для проверки не вышел ли я за пределы массива или еще каких-то но не в 10 же раз.
Хотелось бы чтобы знающие люди сказали где я не прав или же на оборот подтвердили мой маленький тест.
П.С. Для тестов использовал VS2008 Express Edition
П.П.С Просьба говорить только о платформе NET и не вступать в полемику кто круче C++, C# или VB.NET (неверняка данный код последних двух после компиляции в IL получится одинаковый)
Код теста покажи.
Core 2 Duo 2.2 ghz. Умножение 10.000.000 XNA матриц -
1. Использование перегруженных операций - 1343 милисек.
2. Использование статического метода Multiply(ref Matrix m1, ref Matrix m2, out Matrix result) - 953 мс
> Но в сторонних исходниках поглядел все используют структуры и в том числе и в XNA. Почему?
Вообще я не заглядывал в XNA или в какой-то Managed DirectX, и не знаю как там все это "варится".
Но на счет структур, так как это value type то переменные, объявленые например в функции, будут хранится в стеке.
Вот например:
void Foo()
{
SomeStruct t;
}
void Foo()
{
SomeClass t = new SomeClass();
}
- в первом случае t будет хранится в стеке,
во втором случае - это ссылка, для неё нужно выделить память, для этого пинается механизм выделения памяти
из managed heap, сразу можно понять что это дело не быстрое...
А потом нужно GC это все будет почистить, плюс передвижение этих блоков памяти (дефрагментирование), в общем целое дело...
А теперь представим себе какую-то не простую мат. функцию, как правило в ней объявляются переменные хранящие какие-то промежуточные
результаты, ну например:
void TestSomeIntersection(...)
{
Vector temp1, temp2, temp3 ...
Vector result ...
}
И эта функция вызывается для треугольников меша (например) коих там ~2000, а мешей может быть несколько, например штук ~4-6,
то есть, вызовов функции за кадр (одну итерацию главного цикла) - будет много...
и если этот Vector будет классом, можно себе представить сколько раз дернется менеджер памяти (а потом и GC) для создания
инстансов: 2000*6*(кол-во переменных в функции, напр. 5) = 60000 - и это только для этой функции, все, производительность на нуле
(даже пусть там VS и .NET применит какие-то оптимизации, все равно...).
Совсем другое дело структуры, они (инстансы) хранятся в стеке, и с этим будет намного попроще (побыстрее).
А передавать инстансы структур в методы как параметры - через ref/out, и никакого копирования.
Вообще, тест интересный, у самого никак руки не доходят проверить... покажи лучше код теста на .NET
В общем, и я провел небольшой тест. Я не буду вдаваться в подробности или постить код. Скажу лишь
что код абсолютно идентичен на обоих языках С++ и С# (в обоих случаях для матрицы юзал структуру), функцию умножения писал сам.
Умножение идет самым прямолинейным способом, то есть перебирается каждый элемент матрицы и для него считается значение,
никаких оптимизаций (и конечно же все в одном потоке). Данные матрицы хранятся в отдельных 16-и флоатах.
Метод для .NET выглядит так:
void MulMatr(ref Matrix A, ref Matrix B, out Matrix res);
Для С++:
void MulMatr(const Matrix& A, const Matrix& B, Matrix& res);
Результаты - примерно 4 секунды для обоих вариантов, то есть, одинаково (10,000,000 умножений).
Intel Core 2 Duo, 3.2Гц
С++ : в среднем 2200мс
Matlab: в среднем 2150мс
С# не юзаю, извиняй. Почему Matlab здесь привел - он оптимизирован под работу с матрицами и там используются максимально быстрые алгоритмы для работы с ними. Правда увеличение скорости значительно для матриц порядка 10 и выше, т.к. там идет куча проверок на все и вся.
а если C++ скомпилять с ключами "/Ox /arch:SSE2" какие времена?
Abibok
забавно твои тесты и камень смотрятся на фоне моих. Результаты ты уже видел, вот код -
Matrix first = Matrix.Identity; Matrix second = Matrix.CreateRotationX(0.5f); Matrix result = Matrix.Identity; result = first * second; // jit-им нужные методы Matrix.Multiply( ref first, ref second, out result);// jit-им нужные методы DateTime start1 = DateTime.Now; for ( int i = 0; i < 10000000; i++) { result = first * second; } TimeSpan elapsed1 = DateTime.Now - start1; // 1343 милисек. Matrix test = result; // эт чтоб компилер не наоптимизировал чего лишнего DateTime start2 = DateTime.Now; for ( int i = 0; i < 10000000; i++) { Matrix.Multiply( ref first, ref second, out result); } TimeSpan elapsed2 = DateTime.Now - start2;// 953 милисек. test = result;
using Microsoft.DirectX; using Microsoft.DirectX.Direct3D; namespace MdxTestMatrix { class Program { static void Main(string[] args ) { Matrix a = new Matrix( ), b = new Matrix( ), r = new Matrix( ); a.M11 = 1; a.M12 = 2; a.M13 = 3; a.M14 = 4; a.M21 = 5; a.M22 = 6; a.M23 = 7; a.M24 = 8; a.M31 = 9; a.M32 = 10; a.M33 = 11; a.M34 = 12; a.M41 = 13; a.M42 = 14; a.M43 = 15; a.M44 = 16; b.M11 = 17; b.M12 = 18; b.M13 = 19; b.M14 = 20; b.M21 = 21; b.M22 = 22; b.M23 = 23; b.M24 = 24; b.M31 = 25; b.M32 = 26; b.M33 = 27; b.M34 = 28; b.M41 = 29; b.M42 = 30; b.M43 = 31; b.M44 = 32; DateTime utc = DateTime.UtcNow; for ( int i = 0; i < 100000000; ++i) r = a * b; TimeSpan diff = DateTime.UtcNow - utc; // 1сек = 10 млн тиков Console.WriteLine( "{0} {1}", diff.Ticks, r.M11 ); } } }
Pentium-4 3GHz (2 года тачке, ядро хз какое)
100 млн итераций
примерно такие матрицы и вычисления на всех языках
там, где можно было юзать ref, юзал ref (а этот код для MDX)
MDX:
139 687 500
141 875 000
VС++9 D3DXMatrixMultiply (в disasm-e реализован через FPU):
33 750 000
32 968 750
VC++9 самописный через ссылки на матрицы (r11=a11*b11+..) ключи: /fp:fast
65 000 000
64 843 750
с ключами: /fp:fast /arch:SSE2 (по генерации кода ничего не изменилось, а время просто.. раз на раз)
63 593 750
62 500 000
VC++9 mul4x4 (где-то здесь на форуме у кого-то была реализация через SSE mul4x4):
22 968 750
23 125 000
SlimDX (september 2008, написан на C++\CLI):
175 937 500
178 281 250
XNA2-Matrix, а также XNA3-Matrix, а также самописный mul в простой консоли:
~92 000 000
Мда, намудрил я чегой-то...
void CMyApp::ProcessMultiply() { D3DXMATRIXA16 matFirst, matSecond, matResult; D3DXMatrixIdentity( &matFirst ); D3DXMatrixIdentity( &matSecond ); D3DXMatrixIdentity( &matResult ); double StartTime = GetTimer(); for ( int i = 0; i < 10000000; i++ ) { D3DXMatrixMultiply( &matResult, &matFirst, &matSecond ); } double EndTime = GetTimer(); dResult = EndTime - StartTime; _sntprintf( tResult, 16, "%.2f", dResult ); return; }
офигеть...
248мс...
а в прошлый раз умножение вручную писал...
да если честно у меня примерно одинакого вышло:
XNA - http://code.hash.su/415 - 1500 ms
C++ - http://code.hash.su/416 - 238 ms
Причину нашел. Работа с массивами меня убила. Вообщем если использовать массивы в среднем 11000 мсек. Если массивы заменить на переменные примерно 1600 мсек. Я понимаю постоянные проверки на то чтоб не выйти за приделы массива но блин такая разница!:(
Заоптимизировал до 1300 мсек. (Cel 2.8 ГГц)
Еще провел тест если функцию перемножения импортировать из сишной дллки. Так же 10.000.000 матриц
CPPMult(ref Matrix4 m1, ref Matrix4 m2, ref Matrix4 r); - 2900 мсек
Mult(ref Matrix4 m1, ref Matrix4 m2, ref Matrix4 r)
{
CPPMult(ref m1, ref m2, ref r); -2600 мсек
}
Тоесть во втором варианте над этой функцией для эксперимента сделал обертку в шарпе. 300 мсек, тоесть разница примерно 10.5 процентов. Помоему многовато для простого вызова функции с тремя аргументами (по ссылке)
Блин да и вообще скорость вызова функции из длл удручает:(
Тема в архиве.