C/С++ по сравнению с Java/С# в высокопроизводительных приложениях
Мой вопрос касается производительности Java по сравнению со скомпилированным кодом, например, C++/fortran/assembly в высокопроизводительных числовых приложениях. Я знаю, что это спорная тема, но я ищу конкретные ответы/примеры. Также сообщество вики. Я задавал подобные вопросы и раньше, но, думаю, я выразился в общих чертах и не получил ответов, которые искал.
Умножение матрицы на матрицу двойной точности, обычно известное как dgemm в библиотеке blas, позволяет достичь почти 100-процентной пиковой производительности ЦП (с точки зрения операций с плавающей запятой в секунду).
Есть несколько факторов, которые позволяют достичь этой производительности:
-
блокировка кеша для достижения максимальной локализации памяти
-
развертывание цикла для минимизации накладных расходов на управление
-
векторные инструкции, такие как SSE
-
предварительная загрузка из памяти
-
не гарантирует алиасинг памяти
Я видел множество тестов, использующих ассемблер, C++, Fortran, Atlas, поставщик BLAS (типичные случаи - матрица измерения 512 и выше). С другой стороны, я слышал, что основные байтовые скомпилированные языки/реализации, такие как Java, могут быть быстрыми или почти такими же быстрыми, как машинно-компилируемые языки. Однако я не видел определенных ориентиров, показывающих, что это так. Напротив, кажется (из моего собственного исследования) скомпилированные байты языки намного медленнее.
У вас есть хорошие тесты умножения матриц-матриц для Java/С#? может ли компилятор точно в срок (фактическая реализация, а не гипотетическая) создавать инструкции, которые удовлетворяют перечисленным пунктам?
Спасибо
Что касается производительности: каждый процессор имеет пиковую производительность, в зависимости от количества команд, которые процессор может выполнять в секунду. Например, современный процессор Intel с частотой 2 ГГц может достигать 8 миллиардов с двойной точностью добавления/умножения в секунду, что приводит к пиковой производительности 8 Гфлопс. Матрица-матричное умножение является одним из алгоритмов, который способен достичь почти полной производительности в отношении числа операций в секунду, основной причиной является более высокое соотношение вычислений к операциям с памятью (N^3/N^2)
. Числа меня интересуют, что-то порядка N > 500
.
Что касается реализации: детали более высокого уровня, такие как блокировка, выполняются на уровне исходного кода. Оптимизация нижнего уровня выполняется компилятором, возможно, с подсказками компилятора относительно выравнивания/псевдонима. Байт-скомпилированная реализация также может быть написана с использованием блочного подхода, поэтому в принципе детали исходного кода для достойной реализации будут очень похожи.
Ответы
Ответ 1
Сравнение VC++/.NET 3.5/Mono 2.2 в сценарии умножения чистой матрицы:
Источник
Mono с Mono.Simd имеет большое значение для сокращения разрыва в производительности с оптимизированной вручную C++, но версия C++ по-прежнему явно самая быстрая. Но Mono сейчас на уровне 2.6 и может быть ближе, и я ожидаю, что если .NET когда-нибудь получит что-то вроде Mono.Simd, он может быть очень конкурентоспособным, так как здесь нет большой разницы между .NET и последовательным C++.
Ответ 2
Все факторы, которые вы указываете, возможно, выполняются с помощью ручной оптимизации памяти/кода для вашей конкретной задачи. Но JIT-компилятор не располагает достаточной информацией о вашем домене, чтобы сделать код оптимальным, поскольку вы делаете его вручную, и можете применять только общие правила оптимизации. В результате он будет медленнее, чем код манипуляции с матрицей C/С++ (но может использовать 100% CPU, если вы этого хотите:)
Ответ 3
Решение проблемы SSE: Java использует инструкции SSE с J2SE 1.4.2.
Ответ 4
Java не может конкурировать с C в умножении матриц, одной из причин является то, что при каждом доступе к массиву проверяется, превышены ли границы массива. Дальнейшая математика Java медленная, она не использует процессор sin(), cos().
Ответ 5
в чисто математическом сценарии (вычисление 25 типов или трехмерных координат алгебраических поверхностей) c++ превосходит Java в соотношении 2,5