C # out параметр производительности - PullRequest
21 голосов
/ 09 февраля 2009

Имеют ли out параметры в C # какие-либо последствия для производительности, о которых я должен знать? (Как исключения)

Я имею в виду, хорошо ли иметь метод с параметром out в цикле, который будет выполняться пару миллионов раз в секунду?

Я знаю, что это некрасиво, но я использую его так же, как Int32.TryParse использует их - возвращает bool, чтобы сказать, была ли успешная проверка, и имеет параметр out, содержащий некоторые дополнительные данные, если он был успешным .

Ответы [ 6 ]

33 голосов
/ 09 февраля 2009

Я сомневаюсь, что вы найдете какое-либо существенное снижение производительности при использовании параметра 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, мы записываем данные непосредственно в конечный пункт назначения, а не записываем их в кадр стека небольшого метода, а затем копируем их обратно в кадр стека метода Main.

Не стесняйтесь критиковать тестовое приложение - возможно, я что-то пропустил!

5 голосов
/ 09 февраля 2009

Не проблема производительности, а то, что появилось раньше - вы не можете использовать их с дисперсией в C # 4.0 .

Лично я склонен использовать out параметры в значительном количестве в моем частном коде (т. Е. Внутри класса, имея метод, который возвращает несколько значений без использования отдельного типа) - но я склонен к избегайте их в общедоступном API, за исключением шаблона bool Try{Something}(out result).

5 голосов
/ 09 февраля 2009

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

Фактически, возвращаемые значения для всех типов> 32 бит обрабатываются аналогично out аргументам на машинном уровне в любом случае.

Обратите внимание, что в последнем утверждении не предлагается возвращать значение == out в .NET. Тест Джона показывает, что это явно (и, к сожалению) не так. Фактически, чтобы сделать его идентичным, в компиляторах C ++ используется названная оптимизация возвращаемого значения . Нечто подобное можно было бы потенциально сделать в будущих версиях JIT для повышения производительности возврата больших структур (однако, поскольку большие структуры встречаются в .NET довольно редко, это может быть ненужной оптимизацией).

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

1 голос
/ 09 февраля 2009

Выходные параметры передаются по реф. Поэтому в стеке передается только указатель.

Если ваш тип значения большой, копирование меньше, но вам нужно разыменовать указатель на каждое использование переменной.

1 голос
/ 09 февраля 2009

Основная причина, по которой следует избегать параметров, - это удобочитаемость кода, а не производительность.

Для типов значений в любом случае нет реальной разницы (они всегда копируются), а для ссылочных типов это в основном то же, что и передача по ссылке.

В девяти случаях из десяти вам лучше создавать свой собственный тупой класс записей, чем использовать параметр out - это проще читать и понимать, когда вы позже вернетесь к коду.

0 голосов
/ 09 февраля 2009

Использование параметра out не снижает производительность. Параметр Out в основном является опорным параметром, поэтому и вызывающий, и вызываемый указывают на один и тот же фрагмент памяти.

...