Выравнивание памяти классов в C #? - PullRequest
14 голосов
/ 23 декабря 2009

(кстати, это относится к 32-битной ОС)

НЕКОТОРЫЕ ОБНОВЛЕНИЯ:

  • Это определенно проблема выравнивания

  • Иногда выравнивание (по какой-либо причине?) Настолько плохое, что доступ к двойнику более чем в 50 раз медленнее, чем его самый быстрый доступ.

  • Выполнение кода на 64-битной машине устраняет проблему, но я думаю, что она все еще чередовалась между двумя временными интервалами (из которых я мог получить аналогичные результаты, изменив double на float на 32-битной машине)

  • Запуск кода в режиме моно не представляет никаких проблем - Microsoft, есть ли шанс, что вы сможете скопировать что-нибудь из этих ребят из Novell ???


Есть ли способ выравнивания памяти в c #? 1024 *

Следующее демонстрирует (я думаю!) То, что неправильно выровнять двойники. Он выполняет простую математику для двойника, хранящегося в классе, рассчитывая время каждого запуска, выполняя 5 временных запусков для переменной перед распределением нового и делая это снова.

В основном результаты выглядят так, как будто у вас быстрая, средняя или медленная позиция в памяти (на моем древнем процессоре они заканчиваются примерно на 40, 80 или 120 мс за цикл)

Я пытался играть с StructLayoutAttribute, но не испытывал радости - может быть, что-то еще происходит?

class Sample
{
    class Variable { public double Value; }

    static void Main()
    {
        const int COUNT = 10000000;
        while (true)
        {
            var x = new Variable();
            for (int inner = 0; inner < 5; ++inner)
            {
                // move allocation here to allocate more often so more probably to get 50x slowdown problem
                var stopwatch = Stopwatch.StartNew();

                var total = 0.0;
                for (int i = 1; i <= COUNT; ++i)
                {
                    x.Value = i;
                    total += x.Value;
                }
                if (Math.Abs(total - 50000005000000.0) > 1)
                    throw new ApplicationException(total.ToString());

                Console.Write("{0}, ", stopwatch.ElapsedMilliseconds);
            }
            Console.WriteLine();
        }
    }
}

Итак, я вижу много веб-страниц о выравнивании структур для взаимодействия, так как насчет выравнивания классов?

(Или мои предположения неверны, и есть еще одна проблема с вышеизложенным?)

Спасибо, Пол.

Ответы [ 6 ]

6 голосов
/ 24 декабря 2009

Чтобы доказать концепцию смещения объектов в куче в .NET, вы можете запустить следующий код, и вы увидите, что теперь он всегда работает быстро. Пожалуйста, не стреляйте в меня, это всего лишь PoC, но если вы действительно беспокоитесь о производительности, вы можете подумать об этом;)

public static class AlignedNew
{
    public static T New<T>() where T : new()
    {
        LinkedList<T> candidates = new LinkedList<T>();
        IntPtr pointer = IntPtr.Zero;
        bool continue_ = true;

        int size = Marshal.SizeOf(typeof(T)) % 8;

        while( continue_ )
        {
            if (size == 0)
            {
                object gap = new object();
            }

            candidates.AddLast(new T());

            GCHandle handle = GCHandle.Alloc(candidates.Last.Value, GCHandleType.Pinned);
            pointer = handle.AddrOfPinnedObject();
            continue_ = (pointer.ToInt64() % 8) != 0 || (pointer.ToInt64() % 64) == 24;

            handle.Free();

            if (!continue_)
                return candidates.Last.Value;
        }

        return default(T);
    }
}

class Program
{

    [StructLayoutAttribute(LayoutKind.Sequential)]
    public class Variable
    {
        public double Value;
    }

    static void Main()
    {

        const int COUNT = 10000000;

        while (true)
        {

            var x = AlignedNew.New<Variable>();


            for (int inner = 0; inner < 5; ++inner)
            {

                var stopwatch = Stopwatch.StartNew();

                var total = 0.0;
                for (int i = 1; i <= COUNT; ++i)
                {
                    x.Value = i;
                    total += x.Value;
                }
                if (Math.Abs(total - 50000005000000.0) > 1)
                    throw new ApplicationException(total.ToString());


                Console.Write("{0}, ", stopwatch.ElapsedMilliseconds);
            }
            Console.WriteLine();
        }

    }
}
4 голосов
/ 23 декабря 2009

Интересно взглянуть на шестерни, на которых работают машины. У меня есть небольшая проблема, объясняющая, почему существует несколько различных значений (я получил 4), когда двойное можно выровнять только двумя способами. Я думаю, что выравнивание по строке кэша ЦП также играет роль, хотя это только добавляет до 3-х возможных моментов времени.

Что ж, с этим ничего не поделаешь, CLR обещает выравнивание только для 4-байтовых значений, чтобы гарантировать атомарные обновления на 32-битных машинах. Это не просто проблема с управляемым кодом, C / C ++ тоже имеет эту проблему . Похоже, что производители чипов должны решить эту проблему.

Если это критично, вы можете выделить неуправляемую память с помощью Marshal.AllocCoTaskMem () и использовать небезопасный указатель, который вы можете выровнять точно. То же самое, что вам нужно сделать, если вы выделите память для кода, который использует SIMD-инструкции, они требуют выравнивания 16 байтов. Считайте это ходом отчаяния.

4 голосов
/ 23 декабря 2009

Может быть, StructLayoutAttribute - это то, что вы ищете?

0 голосов
/ 23 декабря 2009

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

0 голосов
/ 23 декабря 2009

У вас нет никакого контроля над тем, как .NET размещает ваш класс в памяти.

Как уже говорили другие, StructLayoutAttribute может быть использован для принудительного задания определенного макета памяти для структуры НО , обратите внимание, что целью этого является взаимодействие C / C ++, а не попытка тонкой настройки производительности вашего приложения .NET.

Если вы беспокоитесь о проблемах выравнивания памяти, то C #, вероятно, является неправильным выбором языка.


РЕДАКТИРОВАТЬ - Отключил WinDbg и посмотрел на кучу, выполняющую приведенный выше код на 32-битной Vista и .NET 2.0.

Примечание: я не получаю изменения во времени, показанном выше.

0:003> !dumpheap -type Sample+Variable
 Address       MT     Size
01dc2fec 003f3c48       16     
01dc54a4 003f3c48       16     
01dc58b0 003f3c48       16     
01dc5cbc 003f3c48       16     
01dc60c8 003f3c48       16     
01dc64d4 003f3c48       16     
01dc68e0 003f3c48       16     
01dc6cd8 003f3c48       16     
01dc70e4 003f3c48       16     
01dc74f0 003f3c48       16     
01dc78e4 003f3c48       16     
01dc7cf0 003f3c48       16     
01dc80fc 003f3c48       16     
01dc8508 003f3c48       16     
01dc8914 003f3c48       16     
01dc8d20 003f3c48       16     
01dc912c 003f3c48       16     
01dc9538 003f3c48       16     
total 18 objects
Statistics:
      MT    Count    TotalSize Class Name
003f3c48       18          288 TestConsoleApplication.Sample+Variable
Total 18 objects
0:003> !do 01dc9538 
Name: TestConsoleApplication.Sample+Variable
MethodTable: 003f3c48
EEClass: 003f15d0
Size: 16(0x10) bytes
 (D:\testcode\TestConsoleApplication\bin\Debug\TestConsoleApplication.exe)
Fields:
      MT    Field   Offset                 Type VT     Attr    Value Name
6f5746e4  4000001        4        System.Double  1 instance 1655149.000000 Value

Мне кажется, что адреса размещения классов выглядят выровненными, если я не читаю это неправильно?

0 голосов
/ 23 декабря 2009

Использование структуры вместо класса, делает постоянную времени. также рассмотрите возможность использования StructLayoutAttribute. Это помогает определить точное расположение памяти структур. Что касается КЛАССОВ, я не думаю, что у вас есть какие-либо гарантии того, как они расположены в памяти.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...