Информация об изменениях

Сообщение Производительность .Net на вычислительных задачах от 21.10.2020 2:52

Изменено 21.10.2020 6:20 Sinclair

Производительность .Net на вычислительных задачах
Мой доклад успешно прошёл. Запись я обещал не выкладывать до середины ноября, да и не особо там чего смотреть.
Хочу поделиться самой зрелищной частью: сравнение производительности разных способов реализации вычислительно-интенсивного кода.
Вот график, который сравнивает разные способы посчитать бинаризацию по Sauvola 7184019.1 для изображения в 33 мегапиксела (время в ms):

Участники:
  • Naive — простая реализация на C#, на основе двумерных массивов
  • Unsafe — реализация на С# с прямой манипуляцией указателями (fixed)
  • С++ — реализация на С++, внешняя dll, в неё делаем вызов. Стоимостью маршалинга одного указателя на фоне сотен миллисекунд обработки можно пренебречь.
  • Linq — честная реализация на linq2d, которая на каждое вычисление фильтра строит код трансформации с нуля
  • Cached Linq — реализация на linq2d, один раз (за пределами измерений) строит код трансформации и сохраняет его в делегат Func<byte[,], byte[,]>. Замеряется стоимость вычисления фильтра Sauvola при помощи этого сохранённого делегата.
Слева — замеры на моём лаптопе, Windows 10, в качестве С++ выступает MSVC. Справа — замеры на машинке в github, Ubuntu, в качестве С++ выступает gcc. В обоих вариантах сборки указана оптимизация по скорости (-O3).

Итого: гарантированно безопасный код на C# работает быстрее небезопасного кода на C#, и сравнимо с небезопасным кодом на С++.

В более простом случае, с фильтром C4, вот такой вот linq2d код объезжает С++ на обеих платформах:
private static Func<byte[,], int[,]> _c4 = 
  (from d in new byte[0,0] 
   select (d[-1, 0] + d[1, 0] + d[0, -1] + d[0, 1])/4).Transform;

public static void int[,] C4(byte[,] data) => _c4(data);

Вот C4 по тому же изображению размером 33МП:


Весь код, включая бенчмарки — в репозитории https://github.com/evilguest/linq2d

Выводы, как грится, каждый может сделать сам.
Производительность .Net на вычислительных задачах
Мой доклад успешно прошёл. Запись я обещал не выкладывать до середины ноября, да и не особо там чего смотреть.
Хочу поделиться самой зрелищной частью: сравнение производительности разных способов реализации вычислительно-интенсивного кода.
Вот график, который сравнивает разные способы посчитать бинаризацию по Sauvola (2D-Linq и оптимизация цифровых фильтров) для изображения в 33 мегапиксела (время в ms):

Участники:
  • Naive — простая реализация на C#, на основе двумерных массивов
  • Unsafe — реализация на С# с прямой манипуляцией указателями (fixed)
  • С++ — реализация на С++, внешняя dll, в неё делаем вызов. Стоимостью маршалинга одного указателя на фоне сотен миллисекунд обработки можно пренебречь.
  • Linq — честная реализация на linq2d, которая на каждое вычисление фильтра строит код трансформации с нуля
  • Cached Linq — реализация на linq2d, один раз (за пределами измерений) строит код трансформации и сохраняет его в делегат Func<byte[,], byte[,]>. Замеряется стоимость вычисления фильтра Sauvola при помощи этого сохранённого делегата.
Слева — замеры на моём лаптопе, Windows 10, в качестве С++ выступает MSVC. Справа — замеры на машинке в github, Ubuntu, в качестве С++ выступает gcc. В обоих вариантах сборки указана оптимизация по скорости (-O3).

Итого: гарантированно безопасный код на C# работает быстрее небезопасного кода на C#, и сравнимо с небезопасным кодом на С++.

В более простом случае, с фильтром C4, вот такой вот linq2d код объезжает С++ на обеих платформах:
private static Func<byte[,], int[,]> _c4 = 
  (from d in new byte[0,0] 
   select (d[-1, 0] + d[1, 0] + d[0, -1] + d[0, 1])/4).Transform;

public static void int[,] C4(byte[,] data) => _c4(data);

Вот C4 по тому же изображению размером 33МП:


Весь код, включая бенчмарки — в репозитории https://github.com/evilguest/linq2d

Выводы, как грится, каждый может сделать сам.