Является ли передача значения структуры методу по ссылке в C# приемлемой оптимизацией?


Скажем, у меня есть структура:

struct MyStruct
{
    public int X
    public int Y
}

И метод в некотором классе, который повторяется много раз в другом месте:

public bool MyMethod( MyStruct myStruct )
{
    return ...
}

Является ли изменение сигнатуры MyMethod на следующую приемлемую оптимизацию?

public bool MyMethod( ref MyStruct myStruct )

Если да, то насколько большим преимуществом это было бы на самом деле? Если нет, то сколько полей потребуется структуре для получения достаточно большого преимущества, используя ref таким образом?

5 3

5 ответов:

Поскольку вы явно спросили, является ли это "приемлемым" ...

Я бы ответил Нет. Передавая аргумент с помощью ref, вы лжете компилятору и программисту; ref в .NET (исключительно) означает, что вы намерены изменить аргумент внутри метода.

Конечно, вы можете предоставить дополнительный комментарий, объясняющий "ложь" программисту (но не компилятору ... ). Но зачем вообще злоупотреблять семантикой?

Если вам действительно нужен такой экстрим микрооптимизации (и смотрите другие ответы – любое преимущество производительности сомнительно по целому ряду причин!) .NET может быть просто неправильной средой. Реализуйте соответствующую часть в C++.

В 32-битной системе вместо 4 байт данных вы бы сделали изменение, чтобы толкать 8 байт данных. Нет изменений в объеме данных, передаваемых на 64-битную систему. Вы также добавляете требование к компилятору / JITter, что структура должна существовать в памяти, так как вы собираетесь взять ее адрес, что может свести на нет другие оптимизации.

Я сомневаюсь, что это покажет какое-либо увеличение производительности в вашей программе. Сначала составьте профиль и посмотрите, является ли MyMethod узким местом в вашей программе.

Тогда, предположив, что в MyMethod нет других возможностей оптимизации, внесите изменение, чтобы пройти мимо ref, и снова профилируйте, чтобы увидеть, есть ли какие-либо улучшения.

Маловероятно, учитывая ваш пример. На 64-битном процессоре структура будет удобно помещаться в регистрах, она не будет передаваться через стек. Хотя и не на 32-битном процессоре и методе экземпляра. Когда вы проходите по ссылке, вы платите за доступ к элементам структуры, требуется дополнительное разыменование указателя. Это может оказаться дороже, чем избежать копирования, ymmv.

Выигрыш обычно начинается, когда структура превышает 16 байт. Одна из причин для общие рекомендации по переходу в класс, когда структура становится больше, чем это. Учитывая, что это полностью зависит от использования, вам придется либо проанализировать свой код и прочитать сборку (выпуск сборки, конечно), либо использовать профилировщик. Профилировщик вряд ли покажет разницу. Обычно довольно трудно измерить наносекунду, если вы не делаете это искусственно миллиард раз.

С двумя полями int преимущество будет очень небольшим, и вряд ли это что-то изменит. Возможно, это вообще ничего не изменит.

Как только вы начинаете добавлять более двух полей Int, это преимущество начинает расти. Тем не менее, недостаточно, чтобы иметь значение, если вы не вызываете этот метод Много.

В general я бы сказал нет: придерживайтесь класса со свойствами. Однако есть обстоятельства, которые могут требовать чего - то подобного-в частности, на компактной структуре (подумайте: XNA) или микро-структуре. В обоих случаях GC является очень различным, и распределение объектов (и недетерминированная коллекция) может оказать значительное влияние на приложение.

В таких случаях не является чем-то неслыханным, чтобы увидеть больше структур вступают в игру, и ref избегает общие проблемы "потерянного обновления" (в большинстве .NET, структуры должны быть неизменными; опять же, XNA делает есть некоторые сценарии, которые делают его заманчивым, чтобы нарушить это правило). Другой подход, конечно, состоит в том, чтобы принять и вернуть данные - тогда тип может быть неизменяемым.

Если вы имеете в виду производительность передачи объекта; то Вы должны профилировать на точной установке, которую вы собираетесь использовать. x86 против x64-это очевидная разница, но есть и другие. Например, используя ref требует, чтобы значение находилось либо в поле, либо в локальной переменной - но многие настройки производительности JIT на структурах работают на голове стека - это означает, что у вас может быть намного больше "ldloc" / "stloc", чем вам строго нужно. Вы также вводите дополнительное разыменование.