Производительность параметра C # out
-
22-08-2019 - |
Вопрос
Делай вон параметры в C# есть какие-либо последствия для производительности, о которых я должен знать?(Например, исключения)
Я имею в виду, хорошая ли это идея - иметь метод с out
параметр в цикле, который будет выполняться пару миллионов раз в секунду?
Я знаю, что это некрасиво, но я использую его так же, как Int32.TryParse
использует их - возвращает bool
чтобы сообщить, прошла ли какая-то проверка успешно, и наличие out
параметр, содержащий некоторые дополнительные данные, если это было успешно.
Решение
Я сомневаюсь, что вы обнаружите какое-либо существенное снижение производительности при использовании out
параметр.Вы должны так или иначе вернуть информацию звонящему - out
это просто другой способ сделать это.Вы можете обнаружить, что есть некоторые штраф, если вы широко используете параметр out в рамках метода, поскольку это вполне может означать дополнительный уровень перенаправления для каждого доступа.Однако я бы не ожидал, что это будет что-то значительное.Как обычно, напишите наиболее читаемый код и проверьте, достаточно ли высока производительность прежде чем пытаться оптимизировать дальше.
Редактировать:Все остальное, по сути, является отступлением в сторону.Это действительно актуально только для больших типов значений, чего обычно следует избегать в любом случае :)
Однако я не согласен с утверждением Конрада о том, что "возвращаемые значения для всех типов > 32 бит в любом случае обрабатываются аналогично или идентично аргументам out на машинном уровне".Вот небольшое тестовое приложение:
using System;
using System.Diagnostics;
using System.Runtime.CompilerServices;
struct BigStruct
{
public Guid guid1, guid2, guid3, guid4;
public decimal dec1, dec2, dec3, dec4;
}
class Test
{
const int Iterations = 100000000;
static void Main()
{
decimal total = 0m;
// JIT first
ReturnValue();
BigStruct tmp;
OutParameter(out tmp);
Stopwatch sw = Stopwatch.StartNew();
for (int i=0; i < Iterations; i++)
{
BigStruct bs = ReturnValue();
total += bs.dec1;
}
sw.Stop();
Console.WriteLine("Using return value: {0}",
sw.ElapsedMilliseconds);
sw = Stopwatch.StartNew();
for (int i=0; i < Iterations; i++)
{
BigStruct bs;
OutParameter(out bs);
total += bs.dec1;
}
Console.WriteLine("Using out parameter: {0}",
sw.ElapsedMilliseconds);
}
[MethodImpl(MethodImplOptions.NoInlining)]
public static BigStruct ReturnValue()
{
return new BigStruct();
}
[MethodImpl(MethodImplOptions.NoInlining)]
public static void OutParameter(out BigStruct x)
{
x = new BigStruct();
}
}
Результаты:
Using return value: 11316
Using out parameter: 7461
По сути, используя параметр out, мы записываем данные непосредственно в конечный пункт назначения, вместо того чтобы записывать их во фрейм стека малого метода, а затем копировать обратно во фрейм стека основного метода.
Однако не стесняйтесь критиковать приложение benchmark - возможно, я что-то пропустил!
Другие советы
Не проблема с производительностью, а то, что возникло ранее - вы не можете использовать их с различиями в C # 4.0.
Лично я склонен использовать out
параметров изрядное количество в моем Частное код (т.е.внутри класса, имеющего метод, который возвращает несколько значений без использования отдельного типа) - но я склонен избегать их в общедоступном API, за исключением bool Try{Something}(out result)
шаблон.
Это не влияет на производительность. out
с технической точки зрения, это в основном то же самое, что и любая передача старого аргумента.Хотя может показаться правдоподобным, что копируются огромные массивы данных (напримердля больших структур), это фактически то же самое, что и для возвращаемых значений.
Фактически, возвращаемые значения для всех типов > 32 бит обрабатываются аналогично out
аргументы по поводу машинный уровень в любом случае.
Пожалуйста, обратите внимание, что последнее утверждение не предполагает, что возвращаемое значение == out
параметр в файле .NET.Тест Джона показывает, что это явно (и, к сожалению) не так.На самом деле, чтобы сделать его идентичным, оптимизация именованного возвращаемого значения используется в компиляторах C ++.Нечто подобное потенциально можно было бы сделать в будущих версиях JIT для повышения производительности возврата больших структур (однако, поскольку большие структуры довольно редки в .NET, это может оказаться ненужной оптимизацией).
Однако, (и с моими очень ограниченными знаниями сборки x86), возврат объектов из вызовов функций обычно влечет за собой выделение достаточного пространства на сайте вызова, добавление адреса в стек и заполнение его путем копирования в него возвращаемого значения.Это в основном то же самое , что out
выполняет, только опуская ненужную временную копию значения, поскольку к целевой ячейке памяти можно получить прямой доступ.
Основной причиной отказа от использования out parameters является удобочитаемость кода, а не производительность.
Для типов значений в любом случае нет реальной разницы (они всегда копируются), а для ссылочных типов это в основном то же самое, что передача по ссылке.
В девяти случаях из десяти вам лучше создать свой собственный класс dumb record, а не использовать параметр out - это проще прочитать и понять, когда вы вернетесь к коду позже.
Параметры Out передаются по ссылке.Таким образом, в стек передается только указатель.
Если ваш тип значения большой, объем копирования будет меньше, но тогда вам придется разыменовывать указатель на каждую используемую переменную.
Использование параметра out не снижает производительность.Параметр Out в основном является ссылочным параметром, поэтому и вызывающий объект, и вызываемый получатель указывают на один и тот же фрагмент памяти.