The C# Memory Model in Theory and Practice
От: SergeyT. США http://sergeyteplyakov.blogspot.com/
Дата: 06.12.12 21:10
Оценка: 73 (5) +1
В последнем выпуске MSDN Magazine вышла отличная статья Игоря Островского "The C# Memory Model in Theory and Practice".

Это первая часть из серии статей о модели памяти в языке C#; все кратко и по делу. В частности, там дается объяснение, почему в правильной реализации синглтона с двойной проверкой (double-checked lock Singleton) нужно, чтобы статическое поле было с ключевым словом volatile.
Re: The C# Memory Model in Theory and Practice
От: Nikolay_Ch Россия  
Дата: 07.12.12 04:03
Оценка: -1
Здравствуйте, SergeyT., Вы писали:

ST>В последнем выпуске MSDN Magazine вышла отличная статья Игоря Островского "The C# Memory Model in Theory and Practice".


ST>Это первая часть из серии статей о модели памяти в языке C#; все кратко и по делу. В частности, там дается объяснение, почему в правильной реализации синглтона с двойной проверкой (double-checked lock Singleton) нужно, чтобы статическое поле было с ключевым словом volatile.

А на русском статьи нет? Английский, конечно, хорошо, но пятница... утро... что-то тяжко.
Re[2]: The C# Memory Model in Theory and Practice
От: SergeyT. США http://sergeyteplyakov.blogspot.com/
Дата: 07.12.12 06:23
Оценка:
Здравствуйте, Nikolay_Ch, Вы писали:

ST>>Это первая часть из серии статей о модели памяти в языке C#; все кратко и по делу. В частности, там дается объяснение, почему в правильной реализации синглтона с двойной проверкой (double-checked lock Singleton) нужно, чтобы статическое поле было с ключевым словом volatile.

N_C>А на русском статьи нет? Английский, конечно, хорошо, но пятница... утро... что-то тяжко.

На русском обычно стать выходят с некоторой задержкой. Кажется, где-то в месяц.
Re[3]: The C# Memory Model in Theory and Practice
От: Nikolay_Ch Россия  
Дата: 07.12.12 06:42
Оценка:
Здравствуйте, SergeyT., Вы писали:

ST>На русском обычно стать выходят с некоторой задержкой. Кажется, где-то в месяц.

О... Через месяц будет еще актуальнее... На праздниках, боюсь, точно аглицкий не прокатит.
Re: The C# Memory Model in Theory and Practice
От: koodeer  
Дата: 07.12.12 07:18
Оценка:
Здравствуйте, SergeyT., Вы писали:

ST>В последнем выпуске MSDN Magazine вышла отличная статья Игоря Островского "The C# Memory Model in Theory and Practice".


ST>Это первая часть из серии статей о модели памяти в языке C#; все кратко и по делу. В частности, там дается объяснение, почему в правильной реализации синглтона с двойной проверкой (double-checked lock Singleton) нужно, чтобы статическое поле было с ключевым словом volatile.


Прочитал. Хотелось бы обсудить.

Рихтер в CLR via C# (основываюсь на втором издании) писал, что в CLR принята модель памяти Microsoft, которая не полностью соответствует модели ECMA. То есть в модели памяти Microsoft двойной проверки с блокировкой вполне достаточно и без указания volatile.

Так как быть, писать более строгий код, соответствующий ECMA, или ограничиться менее строгим, если приложение рассчитано только на Microsoft CLR?

Жду второй части статьи, там обещано рассказать именно о различиях с учётом разных архитектур.
Re: The C# Memory Model in Theory and Practice
От: Sinix  
Дата: 07.12.12 07:39
Оценка: 6 (1)
Здравствуйте, SergeyT., Вы писали:

ST>В последнем выпуске MSDN Magazine вышла отличная статья Игоря Островского "The C# Memory Model in Theory and Practice".


Шикарная статья!

Заинтересовал вот этот момент:

class BoxedInt2
{
  public readonly int _value = 42;
  void PrintValue()
  {
    Console.WriteLine(_value);
  }
}


Now, it’s possible — at least in theory — that PrintValue will print “0” due to a memory-model issue.
Here’s a usage example of BoxedInt that allows it:

class Tester
{
  BoxedInt2 _box = null;
  public void Set() {
    _box = new BoxedInt2();
  }
  public void Print() {
    var b = _box;
    if (b != null) b.PrintValue();
  }
}


Because the BoxedInt instance was incorrectly published (through a non-volatile field, _box), the thread that calls Print may observe a partially constructed object!


т.е. в теории
// Thread 1
temp = new BoxedInt2()
temp._value = 42;
_box = temp;
// Thread 2
Console.WriteLine(_box._value);

может превратиться в
// Thread 1
temp = new BoxedInt2()
_box = temp;
// Thread 2
Console.WriteLine(_box._value);
// Thread 1
temp._value = 42;


Я никак не соображу, как это возможно на практике (рассматриваем только реализацию рантайма от МС))? Для x86/x64 с их strong memory model volatile по сути отключает оптимизации компилятора:

The mainstream x86 and x64 processors implement a strong memory model where memory access is effectively volatile. So, a volatile field forces the compiler to avoid some high-level optimizations like hoisting a read out of a loop, but otherwise results in the same assembly code as a non-volatile read.


На итаниумах (и, подозреваю на армах) — запись всё равно volatile даже у обычных полей (оттуда жа):

A non-volatile write could just update the value in the thread’s cache, and not the value in main memory
...
However, in C# all writes are volatile (unlike say in Java), regardless of whether you write to a volatile or a non-volatile field.

т.е вот этот сценарий невозможен:
 public void Set() {
    // в переменную _box записывается ссылка на созданный объект, но _box._value оказывается в другой линейке кэша и ещё не скинута в оперативку.
    _box = new BoxedInt2(); 
    // Поток 2 вызывает _box.Print()
    // в этот момент в память скидывается другая линейка кэша, в оперативке по адресу *_box._value оказывается 42
  }


Собственно, как???

  Набросал пример
        class BoxedInt2
        {
            public readonly int _value = 42;
            public void PrintValue()
            {
                PrintValue2(_value);
            }

            private void PrintValue2(int value)
            {
                if (value == 0)
                {
                    throw new Exception();
                }
                //Console.WriteLine(value);
            }
        }
        class Tester
        {
            BoxedInt2 _box = null;
            public void Set()
            {
                _box = new BoxedInt2();
            }
            public void Print()
            {
                var b = _box;
                if (b != null) b.PrintValue();
            }
        }

        private static void Main()
        {
            var tester = new Tester();

            var setThread = new Thread(() =>
            {
                while (true)
                {
                    tester.Set();
                }
            });
            setThread.Start();

            while (true)
            {
                tester.Print();
            }
        }

Релизная сборка пока что не завалилась.
Re[2]: The C# Memory Model in Theory and Practice
От: Константин Л. Франция  
Дата: 07.12.12 12:45
Оценка:
Здравствуйте, Sinix, Вы писали:

[]

мне тоже интересно

S>Because the BoxedInt instance was incorrectly published (through a non-volatile field, _box), the thread that calls Print may observe a partially constructed object!

S>[/q]

это же ссылки. создание объекта и присвоение ссылки на него совершенно разные операции. что это за лажа?

[]
Re[3]: The C# Memory Model in Theory and Practice
От: Sinix  
Дата: 07.12.12 13:11
Оценка:
Здравствуйте, Константин Л., Вы писали:

КЛ>это же ссылки. создание объекта и присвоение ссылки на него совершенно разные операции. что это за лажа?


У меня только одна версия — в статье речь не о рантайме МС, а об абстрактной модели памяти по ECMA. Вот тут у Игоря приведён похожий пример, но там явно оговаривается что речь — о модели с nonvolatile write:

You may find it surprising that a volatile read refreshes the entire cache, not just the read value. Similarly, a volatile write (i.e., every C# write) flushes the entire cache, not just the written value. These semantics are sometimes referred to as “strong volatile semantics”.

The original Java memory model designed in 1995 was based on weak volatile semantics, but was changed in 2004 to strong volatile. The weak volatile model is very inconvenient. One example of the problem is that the “safe publication” pattern is not safe. Consider this example:

volatile string[] _args = null;
public void Write() {
    string[] a = new string[2];
    a[0] = "arg1";
    a[1] = "arg2";
    _args = a;
    ...
}

public void Read() {
    if (_args != null) {
        // Under weak volatile semantics, this assert could fail!
        Debug.Assert(_args[0] != null);
    }
}


Under strong volatile semantics (i.e., the .NET and C# volatile semantics), a non-null value in the _args field guarantees that the elements of _args are also not null. The safe publication pattern is very useful and commonly used in practice.

* Разумеется, Write() и Read() должны выполняться в разных потоках.
Re[2]: The C# Memory Model in Theory and Practice
От: drol  
Дата: 07.12.12 16:03
Оценка: 9 (1)
Здравствуйте, Sinix, Вы писали:

S>Я никак не соображу, как это возможно на практике (рассматриваем только реализацию рантайма от МС))? Для x86/x64 с их strong memory model volatile по сути отключает оптимизации компилятора:

S>
S>...
S>
S>На итаниумах (и, подозреваю на армах) — запись всё равно volatile даже у обычных полей (оттуда жа):

Всё очень просто: модели памяти процессоров устроены сложнее, чем термин volatile в определении C#\CLR.

В частности, на x86 последовательность store\load может реордериться, если участвующие команды обращаются к разным участкам памяти. Есть несколько форм реордеринга и для store\store.
Re[3]: The C# Memory Model in Theory and Practice
От: drol  
Дата: 07.12.12 16:15
Оценка: 9 (1)
Здравствуйте, drol, Вы писали:

D>В частности, на x86 последовательность store\load может реордериться, если участвующие команды обращаются к разным участкам памяти.


Кстати, volatile спецификаций C#\CLI в этом месте аналогичен. Последовательность store\load разных volatile-полей тоже может реордериться.
Re[4]: The C# Memory Model in Theory and Practice
От: Константин Л. Франция  
Дата: 07.12.12 16:16
Оценка:
Здравствуйте, Sinix, Вы писали:

[]

да, но этот пример нормальный. но как мы можем получить ссылку на еще не полностью созданный объект как в примере?
Re[3]: The C# Memory Model in Theory and Practice
От: Константин Л. Франция  
Дата: 07.12.12 16:24
Оценка:
Здравствуйте, drol, Вы писали:

[]

какое это имеет отношение к примеру Sinix'а?

Неужели возможна такая ситуация, что

1. создается объект
2. ссылка на него возвращается выполняющемуся коду
3. другой тред получает значение его поля
4. поле инициализируется значением 42

в моем представлении порядок такой — 1-4-2-3. То-есть нельзя ни в каком случае получить ссылку на еще не созданный полностью (с отработавшим ctor)
объект
Re[4]: The C# Memory Model in Theory and Practice
От: Sinix  
Дата: 07.12.12 16:52
Оценка:
Здравствуйте, drol, Вы писали:

D>Кстати, volatile спецификаций C#\CLI в этом месте аналогичен. Последовательность store\load разных volatile-полей тоже может реордериться.


Это точно справедливо для того, что генерит jit? Дело в том, что спецификация шарпа (10.5.3) тут совершенно однозначна:

A write of a volatile field is called a volatile write. A volatile write has “release semantics”; that is, it is guaranteed to happen after any memory references prior to the write instruction in the instruction sequence.


т.е. по спецификации
_a = 1; // volatile write 1
_b = 2; // volatile write 2 не может выполниться до "_a = 1"



Плюс (см цитату в предыдущем посте) Игорь утверждает, что любой volatile write сбрасывает весь кэш процессора в память, т.е. реордеринга для store тут быть не должно.

Что я упустил?
Re[5]: The C# Memory Model in Theory and Practice
От: drol  
Дата: 07.12.12 16:58
Оценка:
Здравствуйте, Sinix, Вы писали:

S>Что я упустил?


Вы упустили то, что у Вас последовательность store\store. Тогда как речь идёт про store\load.
Re[4]: The C# Memory Model in Theory and Practice
От: drol  
Дата: 07.12.12 18:13
Оценка:
Здравствуйте, Константин Л., Вы писали:

КЛ>какое это имеет отношение к примеру Sinix'а?


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

КЛ>Неужели возможна такая ситуация


Согласно спецификации C#\CLI — возможна. Причём даже без участия процессора, а лишь на уровне компилятора\JIT'а...

КЛ>в моем представлении порядок такой — 1-4-2-3


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

КЛ> То-есть нельзя ни в каком случае получить ссылку на еще не созданный полностью (с отработавшим ctor) объект


"Я плакаль" (с) не мой

Внутри конструктора отдайте this кому-нибудь...
Re[5]: The C# Memory Model in Theory and Practice
От: drol  
Дата: 07.12.12 20:50
Оценка: 18 (1)
Здравствуйте, Sinix, Вы писали:

S>Плюс (см цитату в предыдущем посте) Игорь утверждает, что любой volatile write сбрасывает весь кэш процессора в память


Если бы Вы внимательно прочли тот древний текст Игоря, то заметили бы, что "кэш" в нём это некоторый виртуальный\воображаемый механизм, предложенный автором для упрощения — "упрощения" с его точки зрения на тот момент — понимания вопроса.

В реальности же кэш не имеет никакого отношения к реордерингу. И полный комплект развлечений с оным можно получить даже на неоснащённым кэшем одноядерном процессоре.

Собственно, за прошедшее с того поста время Игорь, похоже, всё это осознал. И именно поэтому в его новой статье слова cache — и прочих ужосов — нет как класса
Re[5]: The C# Memory Model in Theory and Practice
От: Константин Л. Франция  
Дата: 08.12.12 13:16
Оценка: -1
Здравствуйте, drol, Вы писали:

D>Здравствуйте, Константин Л., Вы писали:


КЛ>>какое это имеет отношение к примеру Sinix'а?


D>Самое прямое. Показывается, что x86 может делать кучу реордерингов на ровном месте.


Никакого

КЛ>>Неужели возможна такая ситуация


D>Согласно спецификации C#\CLI — возможна. Причём даже без участия процессора, а лишь на уровне компилятора\JIT'а...


КЛ>>в моем представлении порядок такой — 1-4-2-3


D>Вы забываете, что потоков два. И, соответственно, последовательностей операций тоже две. Вот напишите их отдельно и рядом.


КЛ>> То-есть нельзя ни в каком случае получить ссылку на еще не созданный полностью (с отработавшим ctor) объект


D>"Я плакаль" (с) не мой


Продолжай плакать дальше

D>Внутри конструктора отдайте this кому-нибудь...


Этого нет в примере
Re[6]: The C# Memory Model in Theory and Practice
От: drol  
Дата: 09.12.12 10:44
Оценка:
Здравствуйте, Константин Л., Вы писали:

КЛ>Никакого


И это вся Ваша аргументация ? Как предсказуемо

КЛ>Этого нет в примере


Ага. Однако этот простой момент показывает, что в конструкторах нет ничего сакрального. И с точки зрения подсистемы исполнения они есть обычные методы, которые оптимизируются согласно обычным правилам.
Re: The C# Memory Model in Theory and Practice
От: koodeer  
Дата: 10.12.12 06:01
Оценка:
Насколько я могу судить, модель памяти ECMA принята в Mono. На главной страничке проекта так и написано. Стало быть, если нужен полностью переносимый код, то нужно следовать советам из статьи.

Меня интересует, во всех ли продуктах Microsoft принята их собственная модель памяти? Скажем, в CompactFramework, в WinPhone? Очень хочется на это надеяться, но боюсь подложенной свиньи.
Re: The C# Memory Model in Theory and Practice
От: Философ Ад http://vk.com/id10256428
Дата: 10.12.12 06:46
Оценка: -1
Здравствуйте, SergeyT., Вы писали:

ST>дается объяснение, почему в правильной реализации синглтона с двойной проверкой (double-checked lock Singleton) нужно, чтобы статическое поле было с ключевым словом volatile.


ECMA, на который ссылается автор опиcывает модель памяти C#, где для volatile описаны следующие разрешённые перестановки:

The effect of applying volatile to fields can be summarized as follows:
First instruction Second instruction Can they be swapped?
Read Read No
Read Write No
Write Write No (The CLR ensures that write-write operations are never swapped, even without the volatile keyword)
Write Read Yes!

т.е. это касается самого компилятора, но в мультитрединге есть ещё одно действующее лицо — Процессор, он тоже делает оптимизации чтения/записи и ему совершенно наплевать на то, что там имел ввиду программист.
Пример:



    public class Test
    {
        public volatile bool a;
        public volatile bool b;
        public volatile bool c;


        private void ThreadFuncA()
        {
            a = true;
            if (b)
            {
                c = true;
            }
        }

        private void ThreadFuncB()
        {
            b = true;
            if (a)
            {
                c = true;
            }
        }


В дизасме нет ничего, что бы хоть как-то гарантировало порядок выполнения.

  Скрытый текст
; функция B
            b = true;
00000000  push        ebp 
00000001  mov         ebp,esp 
00000003  push        eax 
00000004  mov         dword ptr [ebp-4],ecx 
00000007  cmp         dword ptr ds:[0068A1C8h],0 
0000000e  je          00000015 
00000010  call        6C86453C 
00000015  mov         eax,dword ptr [ebp-4] 
00000018  mov         byte ptr [eax+21h],1 
            if (a)
0000001c  mov         eax,dword ptr [ebp-4] 
0000001f  cmp         byte ptr [eax+20h],0 
00000023  je          0000002C 
            {
                c = true;
00000025  mov         eax,dword ptr [ebp-4] 
00000028  mov         byte ptr [eax+22h],1 
            }
        }
0000002c  nop 
0000002d  mov         esp,ebp 
0000002f  pop         ebp 
00000030  ret


; Функция А
            a = true;
00000000  push        ebp 
00000001  mov         ebp,esp 
00000003  push        eax 
00000004  mov         dword ptr [ebp-4],ecx 
00000007  cmp         dword ptr ds:[0031A1C8h],0 
0000000e  je          00000015 
00000010  call        6CA644B4 
00000015  mov         eax,dword ptr [ebp-4] 
00000018  mov         byte ptr [eax+20h],1 
            if (b)
0000001c  mov         eax,dword ptr [ebp-4] 
0000001f  cmp         byte ptr [eax+21h],0 
00000023  je          0000002C 
            {
                c = true;
00000025  mov         eax,dword ptr [ebp-4] 
00000028  mov         byte ptr [eax+22h],1 
            }
        }
0000002c  nop 
0000002d  mov         esp,ebp 
0000002f  pop         ebp 
00000030  ret


т.е. volatile бесполезен, его недостаточно — вокруг барьеров памяти реордер запрещён и комплятору и процессору, а volatile ничего не гарантирует (процессору он реордер не запрещает).
Всё сказанное выше — личное мнение, если не указано обратное.
Re[2]: Всё ещё хуже, чем я думал (заглянул в сабжевую спецификацию)
От: Философ Ад http://vk.com/id10256428
Дата: 10.12.12 07:02
Оценка: -1

17.4.3 Volatile fields
When a field-declaration includes a volatile modifier, the fields introduced by that declaration are
volatile fields. For non-volatile fields, optimization techniques that reorder instructions can lead to
unexpected and unpredictable results in multi-threaded programs that access fields without synchronization
such as that provided by the lock-statement (§15.12). These optimizations can be performed by the compiler,
by the runtime system, or by hardware. For volatile fields, such reordering optimizations are restricted:

• A read of a volatile field is called a volatile read. A volatile read has “acquire semantics”; that is, it is
guaranteed to occur prior to any references to memory that occur after it in the instruction sequence.

• A write of a volatile field is called a volatile write. A volatile write has “release semantics”; that is, it is
guaranteed to happen after any memory references prior to the write instruction in the instruction
sequence.


В шарпе VolatileRead() & VolatileWrite() реализуются вот так:



        [MethodImplAttribute(MethodImplOptions.NoInlining)] // disable optimizations
        public static int VolatileRead(ref int address)
        { 
            int ret = address;
            MemoryBarrier(); // Call MemoryBarrier to ensure the proper semantic in a portable way. 
            return ret; 
        }

        [MethodImplAttribute(MethodImplOptions.NoInlining)] // disable optimizations
        public static void VolatileWrite(ref int address, int value)
        {
            MemoryBarrier(); // Call MemoryBarrier to ensure the proper semantic in a portable way. 
            address = value;
        } 
 
        [MethodImplAttribute(MethodImplOptions.NoInlining)] // disable optimizations
        public static void VolatileWrite(ref long address, long value) 
        {
            MemoryBarrier(); // Call MemoryBarrier to ensure the proper semantic in a portable way.
            address = value;
        }



т.е. либо спецификация врёт, либо в мелкософте решили ей не следовать, из чего можно заключить, что статья написанная по спецификации бесполезна.
Всё сказанное выше — личное мнение, если не указано обратное.
Re[7]: The C# Memory Model in Theory and Practice
От: Константин Л. Франция  
Дата: 10.12.12 13:12
Оценка: -1
Здравствуйте, drol, Вы писали:

[]

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

именно об этом говорит автор. никакие reorderings и потоки тут вообще ни при чем.

еще раз копирую пример для наглядности


To further emphasize the point, consider this class:
class BoxedInt2
{
  public readonly int _value = 42;
  void PrintValue()
  {
    Console.WriteLine(_value);
  }
}

Now, it’s possible—at least in theory—that PrintValue will print “0” due to a memory-model issue. Here’s a usage example of BoxedInt that allows it:
class Tester
{
  BoxedInt2 _box = null;
  public void Set() {
    _box = new BoxedInt2();//*1
  }
  public void Print() {
    var b = _box;
    if (b != null) b.PrintValue();
  }
}

Because the BoxedInt instance was incorrectly published (through a non-volatile field, _box), the thread that calls Print may observe a partially constructed object! Again, making the _box field volatile would fix the issue.


в *1 ты не можешь получить ссылку на объект, пока не отработал конструктор. и потоки тут совершенно ни при чем.
поэтому там должен быть либо _box._value == 42 либо _box == null.

все остальное — ересь.
Re[2]: The C# Memory Model in Theory and Practice
От: drol  
Дата: 10.12.12 14:11
Оценка:
Здравствуйте, Философ, Вы писали:

Ф>т.е. это касается самого компилятора, но в мультитрединге есть ещё одно действующее лицо — Процессор,


Это касается всей системы исполнения. Разумеется, включая процессор.

Ф>В дизасме нет ничего, что бы хоть как-то гарантировало порядок выполнения.


Конечно же есть. Архитектура Intel 64\IA-32 называется. Операции с памятью в оных почти полностью соответствуют определению volatile-полей спецификации C#\ECMA, и в дополнительной обвязке на тему нуждаются крайне редко.
Re[8]: The C# Memory Model in Theory and Practice
От: drol  
Дата: 10.12.12 14:50
Оценка:
Здравствуйте, Константин Л., Вы писали:

КЛ>именно об этом говорит автор. никакие reorderings и потоки тут вообще ни при чем.


Это Вы просто читать не умеете.

Обсуждаемый код относится к разделу статьи Thread Communication Patterns. И в первом же абзаце оного написано о чём идёт речь:

The purpose of a memory model is to enable thread communication. When one thread writes values to memory and another thread reads from memory, the memory model dictates what values the reading thread might see.

Далее эти аспекты уточняются относительно конкретных patterns. И в частности для Publication via Volatile Field:

has two methods, Init and Print; both may be called from multiple threads. If no memory operations are reordered, Print can only print “Not initialized” or “42,” but there are two possible cases when Print could print a “0”

Обсуждаемый же код даётся как предельный пример Publication via Volatile Field:

To further emphasize the point, consider this class:

class BoxedInt2
{
  public readonly int _value = 42;
...
}

КЛ>в *1 ты не можешь получить ссылку на объект, пока не отработал конструктор. и потоки тут совершенно ни при чем.

Ссылку на недоконструированный объект получает не поток вызвавший конструктор, а какой-то другой поток, который дёргает Print() в период пока в первом идёт отработка Set():

he thread that calls Print may observe a partially constructed object

Re[3]: The C# Memory Model in Theory and Practice
От: Философ Ад http://vk.com/id10256428
Дата: 10.12.12 14:57
Оценка: -1
Здравствуйте, drol, Вы писали:

D>Здравствуйте, Философ, Вы писали:


Ф>>т.е. это касается самого компилятора, но в мультитрединге есть ещё одно действующее лицо — Процессор,


D>Это касается всей системы исполнения. Разумеется, включая процессор.


Ф>>В дизасме нет ничего, что бы хоть как-то гарантировало порядок выполнения.


D>Конечно же есть. Архитектура Intel 64\IA-32 называется. Операции с памятью в оных почти полностью соответствуют определению volatile-полей спецификации C#\ECMA, и в дополнительной обвязке на тему нуждаются крайне редко.


блин,....


Intel® 64 and IA-32 Architectures Software Developer’s Manual


http://www.intel.com/content/www/us/en/processors/architectures-software-developer-manuals.html

8.2.1 Memory Ordering in the Intel® Pentium® and Intel486™ Processors

The Pentium and Intel486 processors follow the processor-ordered memory model; however, they operate as
strongly-ordered processors under most circumstances. Reads and writes always appear in programmed order at
the system bus—except for the following situation where processor ordering is exhibited. Read misses are
permitted to go ahead of buffered writes on the system bus when all the buffered writes are cache hits and, therefore,
are not directed to the same address being accessed by the read miss.
In the case of I/O operations, both reads and writes always appear in programmed order.
Software intended to operate correctly in processor-ordered processors (such as the Pentium 4, Intel Xeon, and P6
family processors) should not depend on the relatively strong ordering of the Pentium or Intel486 processors.
Instead, it should ensure that accesses to shared variables that are intended to control concurrent execution
among processors are explicitly required to obey program ordering through the use of appropriate locking or serializing
operations (see Section 8.2.5, “Strengthening or Weakening the Memory-Ordering Model”).

8.2.2 Memory Ordering in P6 and More Recent Processor Families

The Intel Core 2 Duo, Intel Atom, Intel Core Duo, Pentium 4, and P6 family processors also use a processor-ordered
memory-ordering model that can be further defined as “write ordered with store-buffer forwarding.” This model
can be characterized as follows.
In a single-processor system for memory regions defined as write-back cacheable, the memory-ordering model
respects the following principles (Note the memory-ordering principles for single-processor and multipleprocessor
systems are written from the perspective of software executing on the processor, where the term
“processor” refers to a logical processor. For example, a physical processor supporting multiple cores and/or
HyperThreading Technology is treated as a multi-processor systems.):
• Reads are not reordered with other reads.
• Writes are not reordered with older reads.
• Writes to memory are not reordered with other writes, with the following exceptions:
— writes executed with the CLFLUSH instruction;
— streaming stores (writes) executed with the non-temporal move instructions (MOVNTI, MOVNTQ,
MOVNTDQ, MOVNTPS, and MOVNTPD); and
— string operations (see Section 8.2.4.1).
• Reads may be reordered with older writes to different locations but not with older writes to the same location.
• Reads or writes cannot be reordered with I/O instructions, locked instructions, or serializing instructions.
• Reads cannot pass earlier LFENCE and MFENCE instructions.
• Writes cannot pass earlier LFENCE, SFENCE, and MFENCE instructions.
• LFENCE instructions cannot pass earlier reads.
• SFENCE instructions cannot pass earlier writes.
• MFENCE instructions cannot pass earlier reads or writes.
In a multiple-processor system, the following ordering principles apply:
• Individual processors use the same ordering principles as in a single-processor system.
• Writes by a single processor are observed in the same order by all processors.
• Writes from an individual processor are NOT ordered with respect to the writes from other processors.
• Memory ordering obeys causality (memory ordering respects transitive visibility).
• Any two stores are seen in a consistent order by processors other than those performing the stores



11.4.4.3 Memory Ordering Instructions

SSE2 extensions introduce two new fence instructions (LFENCE and MFENCE) as companions to the SFENCE
instruction introduced with SSE extensions.
The LFENCE instruction establishes a memory fence for loads. It guarantees ordering between two loads and
prevents speculative loads from passing the load fence (that is, no speculative loads are allowed until all loads
specified before the load fence have been carried out).
The MFENCE instruction combines the functions of LFENCE and SFENCE by establishing a memory fence for both
loads and stores. It guarantees that all loads and stores specified before the fence are globally observable prior to
any loads or stores being carried out after the fence.


11.6.13 Cacheability Hint Instructions
....
The degree to which a consumer of data knows that the data is weakly ordered can vary for these cases. As a
result, the SFENCE or MFENCE instruction should be used to ensure ordering between routines that produce
weakly-ordered data and routines that consume the data. SFENCE and MFENCE provide a performance-efficient
way to ensure ordering by guaranteeing that every store instruction that precedes SFENCE/MFENCE in program
order is globally visible before a store instruction that follows the fence
.


12.10.3 Streaming Load Hint Instruction

Streaming loads may be weakly ordered and may appear to software to execute out of order with respect to
other memory operations. Software must explicitly use fences (e.g. MFENCE) if it needs to preserve order
among streaming loads or between streaming loads and other memory operations.


MFENCE—Memory Fence

Processors are free to fetch and cache data speculatively from regions of system memory that use the WB, WC, and
WT memory types. This speculative fetching can occur at any time and is not tied to instruction execution. Thus, it
is not ordered with respect to executions of the MFENCE instruction; data can be brought into the caches speculatively
just before, during, or after the execution of an MFENCE instruction.
This instruction’s operation is the same in non-64-bit modes and 64-bit mode.


SFENCE—Store Fence

Description
Performs a serializing operation on all store-to-memory instructions that were issued prior the SFENCE instruction.
This serializing operation guarantees that every store instruction that precedes the SFENCE instruction in program
order becomes globally visible before any store instruction that follows the SFENCE instruction. The SFENCE
instruction is ordered with respect to store instructions, other SFENCE instructions, any LFENCE and MFENCE
instructions, and any serializing instructions (such as the CPUID instruction). It is not ordered with respect to load
instructions.

Всё сказанное выше — личное мнение, если не указано обратное.
Re[9]: The C# Memory Model in Theory and Practice
От: Константин Л. Франция  
Дата: 10.12.12 15:11
Оценка: -1 :)
Здравствуйте, drol, Вы писали:

D>Здравствуйте, Константин Л., Вы писали:


КЛ>>именно об этом говорит автор. никакие reorderings и потоки тут вообще ни при чем.


D>Это Вы просто читать не умеете.


[хамство skipped]

D>Ссылку на недоконструированный объект получает не поток вызвавший конструктор, а какой-то другой поток, который дёргает Print() в период пока в первом идёт отработка Set():

he thread that calls Print may observe a partially constructed object


еще раз прошу ссылку на место в спеке, где написано, что такая ситуация возможна.
еще раз — ни при каких стандартных обстоятельствах ты не можешь получить ссылку на объект, который не создан до конца.
new X() — синхронна и атомарна с точки зрения caller'а и это не должно зависеть ни от каких моделей памяти
Re[4]: The C# Memory Model in Theory and Practice
От: drol  
Дата: 10.12.12 15:57
Оценка:
Здравствуйте, Философ, Вы писали:

Ф>блин,....


Выделенные Вами фрагменты относятся к командам типа MOVNT* и их "соратникам". Ну и где они в Вашем ассемблерном листинге ???
Re[10]: The C# Memory Model in Theory and Practice
От: drol  
Дата: 10.12.12 16:18
Оценка:
Здравствуйте, Константин Л., Вы писали:

D>>Ссылку на недоконструированный объект получает не поток вызвавший конструктор, а какой-то другой поток, который дёргает Print() в период пока в первом идёт отработка Set():

he thread that calls Print may observe a partially constructed object


КЛ>прошу ссылку на место в спеке, где написано, что такая ситуация возможна.


В этом моменте я Вам ничем помочь не могу. Спецификация не занимается перечислением возможных вариантов устройства потрохов исполнительной системы. Она всего лишь определяет правила\требования, которым должна удовлетворять любая реализация оной. И про конструкторы там ничего не написано. Зато написано следующее:

Conforming implementations of the CLI are free to execute programs using any technology that guarantees, within a single thread of execution, that side-effects and exceptions generated by a thread are visible in the order specified by the CIL. For this purpose only volatile operations (including volatile reads) constitute visible side-effects.

...

An optimizing compiler from CIL to native code is permitted to reorder code, provided that it guarantees both the single-thread semantics described in §12.6 and the cross-thread semantics of volatile operations.

КЛ>еще раз — ни при каких стандартных обстоятельствах ты не можешь получить ссылку на объект, который не создан до конца.

Это неправда.

КЛ>new X() — синхронна и атомарна с точки зрения caller'а и это не должно зависеть ни от каких моделей памяти


Тоже ерунда. "Синхронность" и "атомарность" есть понятия concurrency, и сочетание с "с точки зрения caller'а" в этом плане не имеет смысла.
Re[11]: The C# Memory Model in Theory and Practice
От: Константин Л. Франция  
Дата: 11.12.12 11:06
Оценка: -1
Здравствуйте, drol, Вы писали:

[]

спецификация c# language spec обязана этим заниматься. другие меня пока не волнуют
Re[12]: The C# Memory Model in Theory and Practice
От: drol  
Дата: 11.12.12 12:22
Оценка:
Здравствуйте, Константин Л., Вы писали:

КЛ>спецификация c# language spec обязана этим заниматься


Ну раз Вы так считаете, то почему я не вижу в Вашем постинге соответствующие цитаты из оной спецификации на тему ?
Re[4]: The C# Memory Model in Theory and Practice
От: SergeyT. США http://sergeyteplyakov.blogspot.com/
Дата: 22.01.13 02:49
Оценка:
Здравствуйте, Nikolay_Ch, Вы писали:

А вот и русский вариант вышел (хотя не знаю, когда, может это уже и не новость): Модель памяти C# в теории и на практике.
Re[5]: The C# Memory Model in Theory and Practice
От: Sinclair Россия https://github.com/evilguest/
Дата: 24.01.13 15:17
Оценка: +4
Здравствуйте, SergeyT., Вы писали:

ST>А вот и русский вариант вышел (хотя не знаю, когда, может это уже и не новость): Модель памяти C# в теории и на практике.

Перевод как всегда отвратителен. Редактор русского издания зря получает свои деньги: пропускать ляпы типа двойного повторения абзацев — позор.

"Хотя абстрактная модель памяти C# — это то, что вы учитывать при написании нового кода" — позор.
"как и почему эти трансформации происходят на практике, когда мы будет подробно рассматривать" — позор.
В общем, не читайте советских газет. Даже от русского автора оригинал на английском гораздо полезнее.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.