Я занимаюсь хобби-проектом raytracer, и изначально я использовал структуры для своих объектов Vector и Ray, и я подумал, что raytracer - идеальная ситуация для их использования: вы создаете их миллионы, они не живут дольше чем один метод, они легкие. Однако, просто изменив «struct» на «class» в Vector и Ray, я получил очень значительное повышение производительности.
Что дает? Они оба маленькие (3 поплавка для вектора, 2 вектора для луча), не копируются слишком много. Конечно, я передаю их методам, когда это необходимо, но это неизбежно. Итак, какие распространенные подводные камни снижают производительность при использовании структур? Я прочитал эту статью MSDN, в которой говорится следующее:
Когда вы запустите этот пример, вы увидите, что цикл структуры на несколько порядков быстрее. Однако важно остерегаться использования ValueTypes, когда вы рассматриваете их как объекты. Это добавляет дополнительные расходы на упаковку и распаковку вашей программы и может в конечном итоге стоить вам дороже, чем если бы вы застряли с объектами! Чтобы увидеть это в действии, измените код выше, чтобы использовать массив foos и bars. Вы обнаружите, что производительность более или менее равна.
Однако он довольно старый (2001 год), и все «помещение их в массив вызывает коробку / распаковку» показалось мне странным. Это правда? Однако я предварительно рассчитал первичные лучи и поместил их в массив, поэтому я взялся за эту статью и вычислил первичный луч, когда мне это было нужно, и никогда не добавлял их в массив, но это ничего не изменило: с классы, это было все еще в 1,5 раза быстрее.
Я использую .NET 3.5 с пакетом обновления 1 (SP1), который, по моему мнению, устранил проблему, из-за которой методы struct никогда не были встроены, поэтому этого тоже не может быть.
Так в основном: какие-либо советы, на что стоит обратить внимание и чего следует избегать?
РЕДАКТИРОВАТЬ: Как указывалось в некоторых ответах, я создал тестовый проект, в котором я попытался передать структуры как 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% быстрее, чем при использовании класса. Почему это отличается от моего raytracer, я не знаю.