Производительность .NET
Сейчас занимаюсь прототипированием MMO сервера на C#. Как будет что-то готовое, будет серия постов на тему его архитектуры.
А сейчас хочу рассказать насколько быстрое создание объектов в шарпе через new().
В свое время кодируя на С/С++ всегда считал (и продолжаю считать), что лишние создания объектов это зло. Точнее - и операция медленная и вообще плохо. Потому, ради производительности, делались такие штуки, как всякие там кеширующие фабрики: если объект уничтожается, он не прибивается, а добавляется в очередь фабрики. Если нам понадобился новый экземпляр такого объекта - мы смотрим чего там у нас закешировано и если ничего - то создаем новый.
Понятное дело, что был контроль длины очереди и тому подобное.
И решил я, значит, сделать похожий велосипед для шарпа - у меня создается куча мелких объектов в рантайме.
Написал и ужаснулся: мой вариант с очередью в 6 (!) раз медленнее обычного new(). При том, что мой был однопоточный, а new() работает и в многопоточном окружении. Если что, за основу брался код отсюда: http://www.codeproject.com/KB/recipes/ObjectPooling.aspx который я сильно почистил и оптимизировал.
Если что, объекты были действительно мелкие - по 12 байт каждый.
Вторая часть истории - у C# есть такой класс BitConverter. У него есть кучка статических методов, которые позволяют конвертировать базовые типы данных (int, etc) в/из массив байт. Проблема состоит в том, что прототип функций конвертации в массив примерно такой:
byte[] ToShort(int value)
Внутри оно делает new byte[4] и сериализирует в него value.
Явная проблема с производительностью? Да, я тоже подумал. И написал свой очередной велосипед.
И что вы думаете? Вот мои результаты в ms, для int, 10000000 итераций:
MyFromArray: 121.0107
BitConverterFromArray: 65.0044
MyToArray: 115.01
BitConverterToArray: 194.0111
Грубо говоря, мои функции вида:
short ReadShort(byte[] buf, int offset);
оказались примерно в 2 раза медленней аналогов из BitConverter из за дополнительных проверок “а не надо ли менять byte order” и из за того, что мои методы не статические.
А функции записи вида:
void Write(short value, byte[] buf, int offset);
оказались всего лишь на ~80% быстрее чем вариант в BitConverter с созданием нового объекта… Я ожидал разницы где-то в порядок, на самом деле.
Вот что получается - запись в массив 4х байт одной операцией (*(int*)&buf = value) всего лишь на 80% быстрее чем создание массива из 4х байт в куче и записью туда этих самых 4х байт той же операцией.
Так что будем считать что new() в .NET почти бесплатен.