Производительность .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 почти бесплатен.

blog comments powered by Disqus