C # Struct Layouts и неожиданный результат разметки стенда? - PullRequest
0 голосов
/ 26 февраля 2012

Я не очень микро-управляю производительностью приложения, но меня интересует следующий сценарий.

Для Structs по умолчанию компилятор C # создает макет LayoutType.Последовательная.Это означает, что поля должны оставаться в порядке, определенном программистом.Я считаю, что это для поддержки взаимодействия с неуправляемым кодом.Однако большинство пользовательских структур не имеют ничего общего с совместимостью.Я читал, что для повышения производительности мы можем явно указать LayoutKind.Auto и позволить CLR выбрать наилучший возможный макет.Чтобы проверить это, я подумал о том, чтобы сделать быстрый тест для обоих макетов.Однако мой результат говорит, что макет по умолчанию (LayoutType.Sequnetial) немного быстрее, чем явный макет (LayoutType.Auto).Я ожидал обратного.

Ниже приведен тест, который я провел на своем компьютере (x86 под управлением .NET 4)

//uses LayoutKind.Sequence by default
public struct StructSeq
{
    private readonly Byte mb;
    private readonly Int16 mx;
    public string a;
    public string b;
    public string c;
    public string d;
}

[StructLayout(LayoutKind.Auto)]
public struct StructAuto
{
    private readonly Byte mb;
    private readonly Int16 mx;
    public string a;
    public string b;
    public string c;
    public string d;
}

public sealed class Program
{
    public static void Main()
    {
        StructSeq sq = new StructSeq();
        Stopwatch sw1 = new Stopwatch();
        sw1.Start();
        for (int i = 0; i < 10000; i++)
        {
            sq = ProcessStructSeq(sq);
        }
        sw1.Stop();
        Console.WriteLine("Struct LayoutKind.Sequence (default) {0}", sw1.Elapsed.TotalMilliseconds);

        StructAuto so = new StructAuto();
        Stopwatch sw2 = new Stopwatch();
        sw2.Start();
        for (int i = 0; i < 10000; i++)
        {
            so = ProcessStructAuto(so);
        }
        sw2.Stop();
        Console.WriteLine("Struct LayoutKind.Auto (explicit) {0}", sw2.Elapsed.TotalMilliseconds);
        Console.ReadLine();
    }

    public static StructSeq ProcessStructSeq(StructSeq structSeq)
    {
        structSeq.a = "1";
        structSeq.b = "2";
        structSeq.c = "3";
        structSeq.d = "4";
        return structSeq;
    }        

    public static StructAuto ProcessStructAuto(StructAuto structAuto)
    {
        structAuto.a = "1";
        structAuto.b = "2";
        structAuto.c = "3";
        structAuto.d = "4";
        return structAuto;
    }
}

Ниже приведен пример результата, который я получаю на своем компьютере (x86 под управлением .NET 4)

Struct LayoutKind.Sequence (по умолчанию) 0,7488

Struct LayoutKind.Auto (явный) 0,7643

Я запускал этот тест несколько раз и всегда получаю Struct LayoutKind.Sequence (по умолчанию)

Несмотря на разницу в микросекундах, я ожидаю, что Struct LayoutKind.Auto (явное) будет ниже, чем Struct LayoutKind.Sequence (по умолчанию).

Кто-нибудь знает причину этого?Или это мой сравнительный анализ недостаточно точен, чтобы дать мне правильный результат?

Ответы [ 2 ]

2 голосов
/ 26 февраля 2012

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

Кроме того, в качестве быстрой проверки я посмотрел код x86 в отладчике и не вижу никакой разницы в сгенерированном коде.Итак, с вашей программой, как она есть, разница, которую вы наблюдали в своих измерениях, по сути, кажется шумом.

0 голосов
/ 26 февраля 2012

Честно говоря, это так близко, что не будет никакой видимой разницы, если вы не обработаете несколько миллионов этих структур.Фактически, запуск его несколько раз может дать разные результаты.Я бы увеличил количество итераций и попытался запустить программу без отладчика, чтобы посмотреть, не изменится ли что-либо.

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

Если вы хотите оптимизировать этот тест, вы должны передать структуры методам процесса в качестве ссылок и не возвращать другую структуру (избегая создания 2 дополнительных структур для метода), чтодолжно обеспечивать гораздо большее ускорение, чем различные типы макетов:

public static void ProcessStructSeq(ref StructSeq structSeq)
{
    structSeq.a = "1";
    structSeq.b = "2";
    structSeq.c = "3";
    structSeq.d = "4";
}        

public static void ProcessStructAuto(ref StructAuto structAuto)
{
    structAuto.a = "1";
    structAuto.b = "2";
    structAuto.c = "3";
    structAuto.d = "4";
}

Кроме того, есть точка, в которой структуры становятся медленнее, чем их классные аналоги, и это оценивается примерно в 16 байтов согласно thisСтатья MSDN и дальнейшее объяснение в этого вопроса StackOverflow .

...