Понятно. Конкретно в моем тесте тогда получается прогревать не чего, объясню:
1. Весь код теста находится в одной функции, т.е. она будет уже скомпилирована
2. Третьи функции не вызываются из участков кода, подлежащих измерению.
Иными словами в тесте код уже «прогрет» к моменту начала измерений.
На счет прогрева с целью положить данные в кэш процессора — да понимаю о чем вы говорите. Но тут тесты С++ и С# находятся в равном положении — такой прогрев и там и там отсутствует.
В своих суждениях я исхожу лишь из прочитаных постов, и разумеется мне было бы интересно почитать статьи DreamWalker-а с анализом производительности С# и C++, если бы таковые были.
Я не говорю о том что DreamWalker пишет неправильные вещи, я во многом согласен с тем что он пишет, однако, как мне кажется, он пытается так или иначе уйти от прямого сравнения производительности, и вот уже это мне не понятно.
Тем временем ниже, Mrrl, за что ему огромное спасибо уже написали пример, который используя unsafe код работает ни хуже С++ решения.
Ваши основные тезисы, как мне кажется сводятся к тому, что мерить производительность не нужно, потому-что сложно учесть все и сделать максимально адекватный тест. Я же хочу ее измерить.
Я не принимаю на веру утверждения о том что что-то работает быстро или медленно, пока сам это не измерю.
Считаете что бенчмарк плох — это ваше право. Вы можете написать лучше и агрументировано опровергнуть результаты.
Даже для С++ разработчика данный код выглядит жестко.
Но у меня в тесте он показал производительность примерно(а рамках погрешности измерений) равную производительности С++ кода не переписанного подобным образом.
В целом это выход, если очень нужна производительность, но конечно очень не простой для среднестатистического С# разработчика.
У меня под VS2015 x64 unsafe реализация работает медленнее safe, даже с учетом оптимизаций
Код в тесте такой:
fixed (int* items = itemsi)
{
for (int i = 0; i < ITEMS_COUNT; i++)
items[i] = i;
int a, b;
for (int i = 0; i < ITEMS_COUNT; i++)
for (int j = i; j < ITEMS_COUNT; j++)
{
a = items[i];
b = items[j];
if (a < b)
{
items[j] = a;
items[i] = b;
}
}
}
Напишите пожалуйста, а я прогоню его в тесте. Заодно сравним эффект от unsafe. Интересно и disassembly сравнить будет.
Конкретно в этом тесте измеряем скорость операций с элементами массива т.е. их чтение и запись. Сортировка лишь неплохой пример для вызова чтения\записи. Саму же сортировку конечно стоит проверять другими способами, но это уже к алгоритмам и за рамками данной статьи.
Я имею в виду именно примитивную сортировку, такую же как приведена в статье, но с использованием unsafe кода C#.
И как я понял, DreamWalker видит именно задачу ее реализации в unsafe коде непростой.
1. Согласен, поэтому чтобы не быть голословным, в комментариях, добавил результаты для х64.
2. Это было в случае с VS2010, на которой были выполнены измерения в статье. А уже позже, в комментариях я провел измерения на VS2015. Результаты получились схожими (+\- 5%)
3,4,6,7. Я не уверен что C#, останется C#-ом в привычном нам понимании, если мы перепишем код на unsafe решение, таким образом полезность теста будет сомнительной. К тому же, как вы сами сказали в посте выше: «как написать сортировку «наиболее правильным методом» на неуправляемый код — это действительно не такая простая задача, тут думать надо.», что еще раз подтверждает то что это очень спорная задача для С# разработчика. В любом случае буду очень рад, если кто-нибудь напишет такой пример.
8,9. У меня есть некоторые выводы об этом в самой статье, да и многое видно по результатам измерений приведенных в таблицах. Заключительный же вывод я сделал более общим.
1. х86 был выбран из-за большей совместимости. Тем более с х64 результаты по сути отличаются не сильно больше чем просто результаты на разных платформах. Но конечно х64 более актуально сейчас. Относительно размотки циклов, не уверен что она существенно поможет в рассмотренном примере.
2. Ага понятно, значит с 2015 студией я его и использовал. (C# компилятор 1.0.0.50618, C++ компилятор 19.00.23026)
3. В том то и дело, вопросов с написанием неуправляемого кода на С# кажется даже больше чем с кодом на С++. Сортировку я конечно писал не потому что мне не подошла стандартная, а исключительно для сравнения.
4,6,7. Попробую глянуть на библиотеку когда будет время. Однако думаю что все-таки «измеритель» для С++ и С# должен быть максимально идентичным, чтобы получать корректные результаты.
5,10. В статье я ссылался на то что результаты приведены исключительно для .Net Framework (при этом использовал разные его версии, и в конечном итоге 2 разных компилятора), Mono действительно оказалсе не охвачен, разве что немного в Head-to-head benchmark: C++ vs .NET.
8,9. Относительно различных реализаций тестовой задачи, как на одном, так и на другом языке думаю можно сделать выводы типа «если в C#(или С++) писать вот так, то работать будет столько, а если писать вот так, то вот столько». Конечно статья, на которую я ссылаюсь, более полная.
Да, пожалуй 153 105 воспринимается легче чем 153105, но сразу появляются вопросы, не два ли это разных числа, или в случае 153,105 — отделение ли это дробной части.
Хотя возможно это мое субъективное восприятие проблемы, и запятая там где идут целые числа не должна вносить путаницу… Возможно я просто привык к отсутствию разделителей в цифрах…
Можете чуть подробнее описать (в идеале показать на disassembly), какие именно инструкции кэшируются в начале цикла С#? И как именно нужно прогревать С# код?
Спасибо, интересная статья, хотя конечно 15-ти кратное превосходство С++ в тесте regex-dna выглядит немного странным, и версия Mono оставляет некоторые вопросы, но в целом результаты очень интересные, особенно учитывая возможность сравнения с другими языками.
1. Сортировка х64 и 2015 студии показала 133875 — как лучший результат для С++ против 200469, как лучший результат для C#. Разница получилась даже больше чем для случая х86.
2. Я использовал стандартные 2015 студию и 2010 студию. Как можно включить использование LegacyJIT и RyuJIT в 2015 студии?
3. С небезопасным кодом возникает много вопросов, о том как именно его писать и насколько сделать безопасным. Если не сложно приведите пример самой простой сортировки переписанной на небезопасный код, наиболее правильным с вашей точки зрения методом.
4. Я проверял разные размерности массива. 10000 было выбрано по причине того что меньшие размерности давали слишком большую прогрешность и таким образом не давали выполнить сравнение, а большие лишь несколько увеличивали стабильность результата, но не меняли соотношение.
5. Mono действительно не сравнивал, так как очень мало использовал его, да и адекватный выбор платформы (ОС) на которой стоит проверять Mono это очень спорный вопрос. На Winodws врядли Mono целесоообразен, а альтернативных OC слишком много для того чтобы предоставить объективную картину.
6. Расскажите пожалуйста, как именно грамотный прогрев может повлиять на тесты, так же было бы интересно узнать как правильно прогревать .Net код.
7. Разброс на многократном запуске действительно был, порядка 3-5%, хочу также заметить что С++ на многократных запусках давал более стабильные результаты (отклонение менее 2%) в то время как С# давал до 5% отклонения между запусками.
8.Основной вывод в том что overhead есть и в грубой оценке его размера.
9. Конечно лучше смотреть больше примеров, в частности поэтому я сослался на одну из статей где примеров рассмотрено больше, есть и другие, однако как правило из результаты примерно в рамках 10..80% на overhead.
10. Сложно охватить все многообразие компиляторов и платформ, но к этому конечно надо стремиться в разумных рамках.
1. Весь код теста находится в одной функции, т.е. она будет уже скомпилирована
2. Третьи функции не вызываются из участков кода, подлежащих измерению.
Иными словами в тесте код уже «прогрет» к моменту начала измерений.
На счет прогрева с целью положить данные в кэш процессора — да понимаю о чем вы говорите. Но тут тесты С++ и С# находятся в равном положении — такой прогрев и там и там отсутствует.
Я не говорю о том что DreamWalker пишет неправильные вещи, я во многом согласен с тем что он пишет, однако, как мне кажется, он пытается так или иначе уйти от прямого сравнения производительности, и вот уже это мне не понятно.
Ваши основные тезисы, как мне кажется сводятся к тому, что мерить производительность не нужно, потому-что сложно учесть все и сделать максимально адекватный тест. Я же хочу ее измерить.
Я не принимаю на веру утверждения о том что что-то работает быстро или медленно, пока сам это не измерю.
Считаете что бенчмарк плох — это ваше право. Вы можете написать лучше и агрументировано опровергнуть результаты.
Но у меня в тесте он показал производительность примерно(а рамках погрешности измерений) равную производительности С++ кода не переписанного подобным образом.
В целом это выход, если очень нужна производительность, но конечно очень не простой для среднестатистического С# разработчика.
Код в тесте такой:
fixed (int* items = itemsi)
{
for (int i = 0; i < ITEMS_COUNT; i++)
items[i] = i;
int a, b;
for (int i = 0; i < ITEMS_COUNT; i++)
for (int j = i; j < ITEMS_COUNT; j++)
{
a = items[i];
b = items[j];
if (a < b)
{
items[j] = a;
items[i] = b;
}
}
}
Что я делаю не так?..
Конкретно в этом тесте измеряем скорость операций с элементами массива т.е. их чтение и запись. Сортировка лишь неплохой пример для вызова чтения\записи. Саму же сортировку конечно стоит проверять другими способами, но это уже к алгоритмам и за рамками данной статьи.
Собирал 2015 студией, C# компилятор 1.0.0.50618, C++ компилятор 19.00.23026 .Net Framework 4.6
Пробовал и х86 и х64 сборки, результаты получились схожими:
Самая быстрая C# реализация на 30% медленнее самой быстрой С++ реализации.
И как я понял, DreamWalker видит именно задачу ее реализации в unsafe коде непростой.
Сравнение производительности UI в WPF, Qt, WinForms и FLTK
Я разбирал некоторые ее проблемы относительно работы с Datagrid.
2. Это было в случае с VS2010, на которой были выполнены измерения в статье. А уже позже, в комментариях я провел измерения на VS2015. Результаты получились схожими (+\- 5%)
3,4,6,7. Я не уверен что C#, останется C#-ом в привычном нам понимании, если мы перепишем код на unsafe решение, таким образом полезность теста будет сомнительной. К тому же, как вы сами сказали в посте выше: «как написать сортировку «наиболее правильным методом» на неуправляемый код — это действительно не такая простая задача, тут думать надо.», что еще раз подтверждает то что это очень спорная задача для С# разработчика. В любом случае буду очень рад, если кто-нибудь напишет такой пример.
8,9. У меня есть некоторые выводы об этом в самой статье, да и многое видно по результатам измерений приведенных в таблицах. Заключительный же вывод я сделал более общим.
2. Ага понятно, значит с 2015 студией я его и использовал. (C# компилятор 1.0.0.50618, C++ компилятор 19.00.23026)
3. В том то и дело, вопросов с написанием неуправляемого кода на С# кажется даже больше чем с кодом на С++. Сортировку я конечно писал не потому что мне не подошла стандартная, а исключительно для сравнения.
4,6,7. Попробую глянуть на библиотеку когда будет время. Однако думаю что все-таки «измеритель» для С++ и С# должен быть максимально идентичным, чтобы получать корректные результаты.
5,10. В статье я ссылался на то что результаты приведены исключительно для .Net Framework (при этом использовал разные его версии, и в конечном итоге 2 разных компилятора), Mono действительно оказалсе не охвачен, разве что немного в Head-to-head benchmark: C++ vs .NET.
8,9. Относительно различных реализаций тестовой задачи, как на одном, так и на другом языке думаю можно сделать выводы типа «если в C#(или С++) писать вот так, то работать будет столько, а если писать вот так, то вот столько». Конечно статья, на которую я ссылаюсь, более полная.
Хотя возможно это мое субъективное восприятие проблемы, и запятая там где идут целые числа не должна вносить путаницу… Возможно я просто привык к отсутствию разделителей в цифрах…
1. Сортировка х64 и 2015 студии показала 133875 — как лучший результат для С++ против 200469, как лучший результат для C#. Разница получилась даже больше чем для случая х86.
2. Я использовал стандартные 2015 студию и 2010 студию. Как можно включить использование LegacyJIT и RyuJIT в 2015 студии?
3. С небезопасным кодом возникает много вопросов, о том как именно его писать и насколько сделать безопасным. Если не сложно приведите пример самой простой сортировки переписанной на небезопасный код, наиболее правильным с вашей точки зрения методом.
4. Я проверял разные размерности массива. 10000 было выбрано по причине того что меньшие размерности давали слишком большую прогрешность и таким образом не давали выполнить сравнение, а большие лишь несколько увеличивали стабильность результата, но не меняли соотношение.
5. Mono действительно не сравнивал, так как очень мало использовал его, да и адекватный выбор платформы (ОС) на которой стоит проверять Mono это очень спорный вопрос. На Winodws врядли Mono целесоообразен, а альтернативных OC слишком много для того чтобы предоставить объективную картину.
6. Расскажите пожалуйста, как именно грамотный прогрев может повлиять на тесты, так же было бы интересно узнать как правильно прогревать .Net код.
7. Разброс на многократном запуске действительно был, порядка 3-5%, хочу также заметить что С++ на многократных запусках давал более стабильные результаты (отклонение менее 2%) в то время как С# давал до 5% отклонения между запусками.
8.Основной вывод в том что overhead есть и в грубой оценке его размера.
9. Конечно лучше смотреть больше примеров, в частности поэтому я сослался на одну из статей где примеров рассмотрено больше, есть и другие, однако как правило из результаты примерно в рамках 10..80% на overhead.
10. Сложно охватить все многообразие компиляторов и платформ, но к этому конечно надо стремиться в разумных рамках.