Подтвердить что ты не робот

Производительность параметра С#

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

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

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

4b9b3361

Ответ 1

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

РЕДАКТИРОВАТЬ: Остальная часть этого - в стороне, эффективно. Это действительно актуально для больших типов значений, которых обычно следует избегать:)

Я не согласен с утверждением Konrad о "возвращаемых значениях для всех типов". 32 бит обрабатываются аналогично или идентичны аргументам на уровне машины в любом случае ". Здесь небольшое тестовое приложение:

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

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

Ответ 2

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

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

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

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

Ответ 3

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

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

Ответ 4

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

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

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

Ответ 5

Параметры выхода передаются по ссылке. Таким образом, только указатель прошел в стеке.

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

Ответ 6

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