Я занимаюсь хобби-проектом по трассировщику лучей, и изначально я использовал структуры для своих векторных и лучевых объектов, и я подумал, что трассировщик лучей был идеальной ситуацией для их использования: вы создаете миллионы из них, они живут не дольше одного метод, они легкие. Однако, просто изменив «структуру» на «класс» в Vector и Ray, я получил очень значительный прирост производительности.
Что дает? Они оба маленькие (3 поплавка для вектора, 2 вектора для луча), не копируются чрезмерно. Я, конечно, передаю их методам, когда это необходимо, но это неизбежно. Итак, каковы общие подводные камни, которые убивают производительность при использовании структур? Я прочитал эту статью MSDN, в которой говорится следующее:
Когда вы запустите этот пример, вы увидите, что цикл структуры работает на несколько порядков быстрее. Однако важно остерегаться использования ValueTypes, когда вы относитесь к ним как к объектам. Это добавляет дополнительную нагрузку на упаковку и распаковку в вашу программу и может в конечном итоге обойтись вам дороже, чем если бы вы застряли с объектами! Чтобы увидеть это в действии, измените приведенный выше код, чтобы использовать массив foos и bars. Вы обнаружите, что производительность более или менее одинакова.
Однако он довольно старый (2001 г.), и вся фраза «размещение их в массиве вызывает упаковку / распаковку» показалась мне странной. Это правда? Однако я предварительно рассчитал первичные лучи и поместил их в массив, поэтому я занялся этой статьей и вычислил первичный луч, когда он мне нужен, и никогда не добавлял их в массив, но это ничего не меняло: с классов, он все еще был в 1,5 раза быстрее.
Я использую .NET 3.5 с пакетом обновления 1 (SP1), который, как я считаю, устранил проблему, из-за которой методы структуры никогда не были встроены, так что этого тоже не может быть.
Итак, в основном: какие советы, что нужно учитывать и чего избегать?
РЕДАКТИРОВАТЬ: Как было предложено в некоторых ответах, я создал тестовый проект, в котором я пробовал передавать структуры как ref. Способы добавления двух векторов:
public static VectorStruct Add(VectorStruct v1, VectorStruct v2)
{
return new VectorStruct(v1.X + v2.X, v1.Y + v2.Y, v1.Z + v2.Z);
}
public static VectorStruct Add(ref VectorStruct v1, ref VectorStruct v2)
{
return new VectorStruct(v1.X + v2.X, v1.Y + v2.Y, v1.Z + v2.Z);
}
public static void Add(ref VectorStruct v1, ref VectorStruct v2, out VectorStruct v3)
{
v3 = new VectorStruct(v1.X + v2.X, v1.Y + v2.Y, v1.Z + v2.Z);
}
Для каждого из них я получил вариант следующего метода тестирования:
VectorStruct StructTest()
{
Stopwatch sw = new Stopwatch();
sw.Start();
var v2 = new VectorStruct(0, 0, 0);
for (int i = 0; i < 100000000; i++)
{
var v0 = new VectorStruct(i, i, i);
var v1 = new VectorStruct(i, i, i);
v2 = VectorStruct.Add(ref v0, ref v1);
}
sw.Stop();
Console.WriteLine(sw.Elapsed.ToString());
return v2; // To make sure v2 doesn't get optimized away because it's unused.
}
Кажется, что все они работают практически одинаково. Возможно ли, что они будут оптимизированы JIT для оптимального способа передачи этой структуры?
EDIT2: кстати, я должен отметить, что использование структур в моем тестовом проекте примерно на 50% быстрее, чем использование класса. Я не знаю, чем отличается мой трассировщик лучей.