2009-12-23 2 views
14

(. Кстати это относится к 32-битной ОС)Выравнивание памяти классов в C#?

Некоторые обновления:

  • Это, безусловно, проблема выравнивания

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

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

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


Есть ли способ памяти выравнивать распределение классов в C#?

Следующие демонстрируют (я думаю!), Что плохое совпадение партией правильное. Он выполняет некоторую простую математику в двойном хранилище, хранящемся в классе, каждый раз запустив, запустив 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(); 
     } 
    } 
} 

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

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

Спасибо, Paul.

+0

Я не могу воспроизвести проблему (на x64 с рамками 3.5 и 4B2). Все проходы имеют схожую скорость, за исключением очень случайного медленного хода, где, по-видимому, GC делает свои вещи. – LukeH

+0

Это было на 32-битной машине - я пробовал пару разных машин с одинаковыми результатами. Я дам ему капельку на 64-битной машине ... –

+0

Как вы определяете ее выравнивание, это проблема, а не прочее (например, gc или подобное)? – nos

ответ

4

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

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

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

+0

Похоже на интересные планы! Я могу дать отчаяние двигаться завтра;) –

4

Возможно, StructLayoutAttribute является тем, что вы ищете?

+0

Ах, я должен прочитать документы! Он говорит, что это также для макета классов! (глупое имя атрибута!) Я дам ему кусок ... –

+0

Игра с этими полями, похоже, не влияет на результат? Может быть, проблема вызвана какой-то другой причиной? –

+0

Этот атрибут влияет только на макет типов, используемых неуправляемым кодом. –

0

Использование структуры вместо класса делает постоянство времени. также рассмотрите использование StructLayoutAttribute. Это помогает указать точное расположение макетов структуры. Для CLASSES я не думаю, что у вас есть какие-то гарантии того, как они отображаются в памяти.

+0

Я был после ассигнований на занятия (о котором говорилось в сообщении). У меня эти классы встроены в лямбда-функции, где я хочу иметь возможность изменять значения, и я не могу сделать это со структурой. –

0

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

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

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


EDIT - вспыхнуло 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 

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

+4

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

+0

Интересно. Я играю на 32-битной версии XP, скомпилированной с помощью .net 3,5 (на нескольких разных машинах, с моего старого домашнего ноутбука dell D800, на то, что работает на моей нынешней относительно новой настольной машине), и увидели проблему на все они. Мне нужно дать ему бурю на вид? –

+0

+1 Если вы беспокоитесь о проблемах выравнивания памяти, то C#, вероятно, является неправильным выбором языка. Используйте правильный инструмент для работы. C++ или ASM. – Andrew

0

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

+0

дальнейшее исследование доказало, что это ПОЛНОСТЬЮ проблема выравнивания. –

+1

Я не верю в это, если у нас нет другого определения выравнивания. Мое определение выравнивания состоит в том, что примитив должен находиться на адресе, который кратен его размеру (например, адрес 32-разрядного номера должен быть делимым на 4 и т. Д.). Я не могу поверить, что объекты не всегда правильно выровнены, и если вы установите определенные флаги процессора на x86 и всегда на x64, вы получите исключения, если вы используете недопустимые значения. Тогда есть проблема кеширования, но это отдельная проблема. – erikkallen

+0

Вероятнее всего, причиной вашей проблемы является то, что иногда ваш поток получает предупреждение. – erikkallen

6

Чтобы доказать концепцию несоосности объектов в куче в .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(); 
     } 

    } 
} 
+1

Зло. Чистое зло. –

+0

:) Вы вызываете 100x накладных расходов при создании нового объекта таким образом :) Не так злой. –

+0

Конечно, злой, но вполне изобретательный. Однако это только частично работает. Если GC решает переместить ваш объект позже, вы можете увидеть ухудшение производительности. Невозможно предотвратить это. Также для больших объектов Т или массивов объектов это может вызвать серьезные проблемы с распределением памяти. –