Здравствуйте, VladD2, Вы писали:
VD>Здравствуйте, Serginio1, Вы писали:
VD>Я вот тоже слышал этот термин несклько раз, но он как-то упоминается в сколь. Типа "обсуждение этой фичи выходит за рамки...".
Во блин нашел аж за 1993 год http://research.sun.com/research/self/papers/write-barrier.html
Каким то образом наверное write barier связан с GC но это возможно если учет ссылок GC был произведен, но обычно это происходит при выделении определенного количества памяти. С другой стороны на Array.Copy write-barrier практически не влияет (вернее есть небольшое замедление по сравнению с валуе типами, но не такое катострофическое, причем в видби это практически не заметно).
... << RSDN@Home 1.1.0 stable >>
и солнце б утром не вставало, когда бы не было меня
Здравствуйте, Блудов Павел, Вы писали:
БП>Здравствуйте, Дарней, Вы писали:
Д>>всё-таки не понял. А зачем тогда MemoryBarrier вызывать?
БП>Затем, что (читайте выше) Singleton.value может быть уже не null БП>и другой процессор уже может попытаться его исползовать. В этом БП>случае
БП>lock(syncRoot)
БП>НЕ ПРОИСХОДИТ. Так вот, для того, чтобы Singleton.value был отправлен БП>в память __последним__ а не каким-то, нужно и сбросить кеш. БП>Раньше это называли FlushProcessorCache. А теперь Memory barrier.
Там даже еще похитрее будет. Memory barrier перед записью value не мешает другому потоку не увидеть данные, на которые указывает этот value. Как замечено в одном из комментариев http://blogs.msdn.com/brada/archive/2004/05/12/130935.aspx, там нужен ещё один memory barrier сразу после первого чтения Singleton.value. Тонкий момент. Многие не могут просечь (в том числе и я до конца, вероятно, не просекаю). В данном случае нужно полагаться на экспертов, которые этот вопрос разобрали и выяснили, как нужно делать правильно. Scott Meyers с этими экспертами помыкался с полгода и выдал резюме: http://www.nwcpp.org/Downloads/2004/DCLP_notes.pdf
Здравствуйте, mihailik, Вы писали:
M>>>volatile and MemoryBarrier() Brad Adams Blog S>> Спасибо. Нужно будет попобовать.
M>Я вот вчитался сегодня с утра. По-моему там какой-то немного другой барьер. Так ничего чётко Адамс не сакзал.
Естественно другой. Это hardware write barrier. Нужен для того, чтобы кэшы синхронизировать и делать flush processor pipeline, например.
А в начале речь шла о barrier как о технике применяемой в generational GCs, которая помогает разделять generations.
M>Вообще впервые узнал, что в C# есть слово "volatile".
Есть. volatile в C# работает почти как в C++, только в добавление чтение volatile переменных имеет acquire semantics, а запись — release semantics. Очень полезно, когда процессор может производить изменение порядка чтения/записи в память (reads & writes reordering).
Здравствуйте, Дарней, Вы писали:
Д>почему мусор? ИМХО, там должен быть 0 — поскольку сброс кэша еще не выполнен, а Singleton.value тоже находится в нем\]
Конечно, там не мусор. Другое дело, что конструктор Singleton'а тоже может что-то писать в память.
И тут-то начинается чехарда. В IA32, где Strong Memory Model все нормально.
У IA64 (и CRL) все плохо — Weak Memory Model, т.е процессор _не_обязан_ класть в память сначала
переменные, проинициализированные в конструкторе Singleton'а, а потом его самого. Пример:
public Singleton {
this.str = new string("blabla");
}
public static Singleton Value {
get {
if (Singleton.value == null) {
lock (syncRoot) {
if (Singleton.value == null) {
Singleton newVal = new Singleton();
Singleton.value = newVal;
}
}
}
return Singleton.value;
}
}
Тут у IA64 в какой-то момент времени Singleton.value уже перешел из кеша процессора в память,
а Singleton.str еще null!!! Хотя конструктор уже отработал
Такое может произойти, если
1. Процессоров больше чем один
2. Singleton.value и Singleton.str окажутся в разных страницах памяти.
3. Со страницей памяти Singleton.str работает другой процессор.
Вот этот самый другой процессор может словить Singleton.value != null и Singleton.str == null.
Д>есть немаленькая вероятность, что будет выделено несколько объектов singleton (по одному на каждый процессор )
Нулевая. Ну-ле-ва-я. Об этом позаботится
lock (syncRoot) {
}
[c#]
Замечу, что если переписать код вот так:
[c#]
public static Singleton Value {
get {
lock (syncRoot) {
if (Singleton.value == null) {
Singleton newVal = new Singleton();
Singleton.value = newVal;
}
}
return Singleton.value;
}
}
То все будет очень даже корректно и для IA64 и для IA32.
Здравствуйте, Дарней, Вы писали:
Д>Здравствуйте, alexkro, Вы писали:
A>>Допустим, делается так как ты предлагаешь. Тогда есть следующий вариант развития событий: первый поток входит в lock выделяет Singleton и инициализирует Singleton.value, но замещается вторым потоком прям перед вызовом MemoryBarrier(). Второй поток проверяет Singleton.value, его значение уже не null, и оно тут-же возвращается из функции. Далее второй поток пытается использовать тот объект Singleton, который он получил, но так как сконструированный объект Singleton еще находится в кэше первого потока (пусть эти потоки выполняются на разных процессорах), то второй поток, думая, что он обращается к памяти Singleton'а, на самом деле читает память, в которой еще мусор. Вот так.
Д>почему мусор? ИМХО, там должен быть 0 — поскольку сброс кэша еще не выполнен, а Singleton.value тоже находится в нем
Ты не понял модель памяти CLR. Запись в память для переменных, доступных глобально, является release. Значит присвоение Singleton.value сразу видимо для всех потоков. А вот сам объект — нет. Это я и имею в виду. На месте объекта для другого потока ещё сырая память — мусор.
Д>а в оригинальном варианте есть немаленькая вероятность, что будет выделено несколько объектов singleton (по одному на каждый процессор )
Нет. В оригинальном варианте тот же самый случай: Singleton.value проинициализирован, а сам объект — нет.
Здравствуйте, Дарней, Вы писали:
Д>Здравствуйте, alexkro, Вы писали:
A>>lock(syncRoot) — это full fence. Все кэши в этом месте синхронизируются, и никаких read/write reordering не может произойти.
Д>всё-таки не понял. А зачем тогда MemoryBarrier вызывать?
Это из другой оперы. Твоя логика была следующей:
1. Singleton.value == null -- true (Singleton.value in cache of another proc, not sync'd)
2. lock(syncRoot)
3. Singleton.value == null -- true (Singleton.value still in cache not sync'd)
Так вот, третье неверно. lock засинхронизирует кэш для данного потока (на данном процессоре) так, что Singleton.value получит текущее значение. Вообще, оно даже и на шаге 1 будет иметь текущее значение вследствие того, что запись в переменные, видимые глобально, является release, так же, как и запись в volatile переменные (а с volatile, как ты понимаешь, вообще всё без memory barrier работает).
Normal loads and stores can be freely reordered, as seen by other CPUs.
БП>Я из этого сделал вывод, что JIT-компилятор для IA64 может гонерить код, выполняя БП>который IA64 процессоры будут писать в память в удобном им порадке.
Хотелось бы расставить точки над i. Процессоры Itanium 2 никогда не выпоняют ни аппаратного реодеринга чтения и записи в память, ни вообще как-либо операций реордеринга. "The Itanium 2 processor hardware makes no attempt to reorder instructions to avoid stalls.". (На всякий случай отправляю к документу 25111002.pdf "Intel® Itanium® 2 Processor Reference Manual"). Все операции спекуляции выполняются только посредством программного кода (концепция EPIC). Поэтому, если и происходит какое-либо видимое со стороны другого процессора переупорядочивание чтение и записи, то только потому что такой код сгенерировал JIT: а именно "The family of instructions composed of ld.a/ldf.a/ldfp.a, ld.c/ldf.c/ldfp.c, and chk.a provide the capability to dynamically disambiguate memory addresses between loads and stores."
Здравствуйте, VladD2, Вы писали:
VD>Здравствуйте, Serginio1, Вы писали:
VD>Я вот тоже слышал этот термин несклько раз, но он как-то упоминается в сколь. Типа "обсуждение этой фичи выходит за рамки...".
Интересно что, при инлайне тормозов нет (веренее есть но только в 2 раза по ставнению с int)
public class IntClass
{
public int value;
public IntClass(int val)
{
value = val;
}
}
public static void Run8(System.Windows.Forms.TextBox _log, int Count)
{
_log.AppendText("Сортировка вставка IntClass массива Count=" + Count.ToString() + Environment.NewLine);
Utils.PerfCounter timer = new PerfCounter();
int r = 666;
IntClass[] IntClassArray = new IntClass[Count];
IntClass[] IntClassArray1 = new IntClass[Count];
while (r < 666000)
{
IntRandim F = new IntRandim(r, 0, Count);
timer.Start();
for (int i = 0; i < Count; i++)
{
IntClassArray1[i] = new IntClass(i);
}
// перемешаем массивfor (int i = 0; i < Count; i++)
{
int temp = F.Next();
int temp2 = F.Next();
IntClass temp3 = IntClassArray1[temp];
IntClassArray1[temp] = IntClassArray1[temp2];
IntClassArray1[temp2] = temp3;
}
// сортировка вставками
IntClassArray[0] = IntClassArray1[0];
for (int i = 1; i < Count; i++)
{
Int32 L = 0;
Int32 R = i-1;
IntClass Current=IntClassArray1[i];
int key = Current.value;
//int key = IntClassArray1[i].value;while (L < R)
{
int Midl = (L + R) >>1;
if (IntClassArray[Midl].value <= key)
L = Midl + 1;
else
R = Midl;
}
for (int j = i; j > L; j--)
IntClassArray[j] = IntClassArray[j - 1];
IntClassArray[L] = IntClassArray1[i];
}
_log.AppendText(string.Format(" Время={0}" + Environment.NewLine, timer.Finish()));
for (int i = 1; i < Count; i++)
if (IntClassArray[i].value != i)
{
_log.AppendText(string.Format(" i={0} <> {1}", i, IntClassArray[i].value));
return;
}
r *= 10;
}
}
Но такой вариант безбожно тормозит пропорционально квадрату занимаемой памяти с применением Add3.
public class Int32AsObjectInsertSort
{
public IntClass[] ar;
public Int32 count;
private Int32 temp;
private Int32 Midl;
public Int32AsObjectInsertSort(Int32 Size)
{
ar = new IntClass[Size];
count = 0;
}
private Int32 ПоискПоловиннымДелением(Int32 key)
{
Int32 i = 0;
Int32 j = count - 1;
while (i <= j)
{
Midl = (i + j) >>1;
if (ar[Midl].value > key) { j = Midl - 1; }
else if (ar[Midl].value < key) { i = Midl + 1; }
else { return i; }
}
return i;
}
public void Add3(IntClass value)
{
if (count == 0)
{
count = 1;
ar[0] = value;
}
else
{
Int32 index = ПоискПоловиннымДелением(value.value);
for (Int32 i = count; i > index; i--)
{
ar[i] = ar[i - 1];
}
ar[index] = value;
count++;
}
}
}
... << RSDN@Home 1.1.0 stable >>
и солнце б утром не вставало, когда бы не было меня
Кстати дома то у меня видби и эти два варианта отрабатывают по разному, на работе провел тест с 1.1 картина и отрабатывают одинаково.
В видби они кое что подправили.
Интересно этот же тест прогнать на 2005.
... << RSDN@Home 1.1.0 stable >>
и солнце б утром не вставало, когда бы не было меня
Здравствуйте, Serginio1, Вы писали:
S>Здравствуйте, Serginio1, Вы писали:
S> Кстати дома то у меня видби и эти два варианта отрабатывают по разному, на работе провел тест с 1.1 картина и отрабатывают одинаково. S> В видби они кое что подправили. S> Интересно этот же тест прогнать на 2005.
А в чем разница?
... << RSDN@Home 1.1.3 beta 2 >>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Здравствуйте, VladD2, Вы писали:
VD>Здравствуйте, Serginio1, Вы писали:
S>>Здравствуйте, Serginio1, Вы писали:
S>> Кстати дома то у меня видби и эти два варианта отрабатывают по разному, на работе провел тест с 1.1 картина и отрабатывают одинаково. S>> В видби они кое что подправили. S>> Интересно этот же тест прогнать на 2005.
VD>А в чем разница?
Разница в том, что в 1.1 эти два алгоритма отрабатывают одинаково, то есть сортировка в одной процедуре и сортировка через добавление (Int32AsObjectInsertSort.Add3)
и writw barier и в обоих случаях.
В видби сортировка в в процедуре в 2 раза медленне чем на интах , но стабильно, при сортировке Int32AsObjectInsertSort.Add3 write barier остается и геометрическая прогрессия замедления от размера данных как в 1.1
... << RSDN@Home 1.1.0 stable >>
и солнце б утром не вставало, когда бы не было меня
Здравствуйте, Serginio1, Вы писали:
S>Здравствуйте, mihailik, Вы писали:
M>>Ещё немного из свежего блога:
M>>volatile and MemoryBarrier() Brad Adams Blog S> Спасибо. Нужно будет попобовать.
По указанной ссылке написано:
public static Singleton Value {
get {
if (Singleton.value == null) {
lock (syncRoot) {
if (Singleton.value == null) {
Singleton newVal = new Singleton();
// Insure all writes used to construct new value have been flushed.
System.Threading.Thread.MemoryBarrier();
Singleton.value = newVal; // publish the new value
}
}
}
return Singleton.value;
}
}
Почему System.Threading.Thread.MemoryBarrier();
Выполняется до Singleton.value = newVal;
а не после него, если мы боимся, что к моменту выполнения
другим потоком (lock (syncRoot) { if (Singleton.value == null) ...)
Singleton.value еще не будет еще присвоено?
Не логичнее ли высвободить кэш после присвоения?
Здравствуйте, kkav, Вы писали:
K>Здравствуйте, Serginio1, Вы писали:
S>>Здравствуйте, mihailik, Вы писали:
M>>>Ещё немного из свежего блога:
M>>>volatile and MemoryBarrier() Brad Adams Blog S>> Спасибо. Нужно будет попобовать.
K>По указанной ссылке написано: K>
K> public static Singleton Value {
K> get {
K> if (Singleton.value == null) {
K> lock (syncRoot) {
K> if (Singleton.value == null) {
K> Singleton newVal = new Singleton();
K> // Insure all writes used to construct new value have been flushed.
K> System.Threading.Thread.MemoryBarrier();
K> Singleton.value = newVal; // publish the new value
K> }
K> }
K> }
K> return Singleton.value;
K> }
K>}
K>
Там в одном из комментариев более правильная версия есть с двумя memory barriers.
K>Почему System.Threading.Thread.MemoryBarrier(); K>Выполняется до Singleton.value = newVal; K>а не после него, если мы боимся, что к моменту выполнения K>другим потоком (lock (syncRoot) { if (Singleton.value == null) ...) K>Singleton.value еще не будет еще присвоено? K>Не логичнее ли высвободить кэш после присвоения?
Допустим, делается так как ты предлагаешь. Тогда есть следующий вариант развития событий: первый поток входит в lock выделяет Singleton и инициализирует Singleton.value, но замещается вторым потоком прям перед вызовом MemoryBarrier(). Второй поток проверяет Singleton.value, его значение уже не null, и оно тут-же возвращается из функции. Далее второй поток пытается использовать тот объект Singleton, который он получил, но так как сконструированный объект Singleton еще находится в кэше первого потока (пусть эти потоки выполняются на разных процессорах), то второй поток, думая, что он обращается к памяти Singleton'а, на самом деле читает память, в которой еще мусор. Вот так.
Вопрос этот вообщем очень интересный. Почитай пост Vance Morrison'а (ссылка в блоге Брада) про моделирование поведения памяти с помощью перестановок чтения/записи. А так же про модель памяти CLR в блоге Chris Brumme'а.
Здравствуйте, alexkro, Вы писали:
A>Допустим, делается так как ты предлагаешь. Тогда есть следующий вариант развития событий: первый поток входит в lock выделяет Singleton и инициализирует Singleton.value, но замещается вторым потоком прям перед вызовом MemoryBarrier(). Второй поток проверяет Singleton.value, его значение уже не null, и оно тут-же возвращается из функции. Далее второй поток пытается использовать тот объект Singleton, который он получил, но так как сконструированный объект Singleton еще находится в кэше первого потока (пусть эти потоки выполняются на разных процессорах), то второй поток, думая, что он обращается к памяти Singleton'а, на самом деле читает память, в которой еще мусор. Вот так.
почему мусор? ИМХО, там должен быть 0 — поскольку сброс кэша еще не выполнен, а Singleton.value тоже находится в нем
а в оригинальном варианте есть немаленькая вероятность, что будет выделено несколько объектов singleton (по одному на каждый процессор )
Здравствуйте, Блудов Павел, Вы писали:
БП>И тут-то начинается чехарда. В IA32, где Strong Memory Model все нормально.
Не тут то было. Strong memory model для одного процессора. А если у тебя их восемь: по четыре в hemisphere? Тут-то синхронизация кэшей начинает играть ту же роль, что и перестановка чтения/записи.
БП>У IA64 (и CRL) все плохо — Weak Memory Model, т.е процессор _не_обязан_ класть в память сначала
У CLR есть своя модель памяти. На IA64 она не будет weak.
IA64 specifies a weaker memory model than X86. Specifically, all loads and stores are normal loads and stores. The application must use special ld.acq and st.rel instructions to achieve acquire and release semantics.
и тут же
In fact, the CLR has done exactly the same thing. Section 12.6 of Partition I of the ECMA CLI specification explains our memory model. This explains the alignment rules, byte ordering, the atomicity of loads and stores, volatile semantics, locking behavior, etc. According to that specification, an application must use volatile loads and volatile stores to achieve acquire and release semantics. Normal loads and stores can be freely reordered, as seen by other CPUs.
Я из этого сделал вывод, что JIT-компилятор для IA64 может гонерить код, выполняя
который IA64 процессоры будут писать в память в удобном им порадке.
Здравствуйте, Блудов Павел, Вы писали:
БП>Нулевая. Ну-ле-ва-я. Об этом позаботится
поправь меня, если я ошибаюсь.
к моменту, когда завершится вот этот код:
public static Singleton Value
{
get
{
if (Singleton.value == null)
{
lock (syncRoot)
{
if (Singleton.value == null)
{
Singleton newVal = new Singleton();
// Insure all writes used to construct new value have been flushed.
System.Threading.Thread.MemoryBarrier();
Singleton.value = newVal; // publish the new value
}
}
}
return Singleton.value;
}
}
обновленное значение Singleton.value будет находиться в кэше процессора, в основную память оно будет сброшено позднее. В этот момент поток на другом процессоре может попытаться получить это значение, вызовется getter. Поскольку Singleton.value для него еще равен null, он создаст новый объект Singleton и запишет его в Singleton.value — в свою копию. (Поскольку на другом потоке геттер уже завершился, он вполне благополучно зайдет в lock).
И в результате мы получим две разные копии Singleton.
Здравствуйте, Блудов Павел, Вы писали:
БП>Здравствуйте, alexkro, Вы писали:
A>>У CLR есть своя модель памяти. На IA64 она не будет weak.
БП>Тогда я чего-то недопонял. Вот тут написано
БП>http://blogs.msdn.com/cbrumme/archive/2003/05/17/51445.aspx
...
БП>Я из этого сделал вывод, что JIT-компилятор для IA64 может гонерить код, выполняя БП>который IA64 процессоры будут писать в память в удобном им порадке.
А после этого описывается реальная модель памяти майкрософтовской реализации CLR, которая гораздо более strong, чем спецификация ECMA CLI, а также предпологается, что следующия версия стандарта ECMA CLI будет следовать этой модели.
Здравствуйте, Дарней, Вы писали:
Д>Здравствуйте, Блудов Павел, Вы писали:
БП>>Нулевая. Ну-ле-ва-я. Об этом позаботится
Д>поправь меня, если я ошибаюсь. Д>обновленное значение Singleton.value будет находиться в кэше процессора, в основную память оно будет сброшено позднее. В этот момент поток на другом процессоре может попытаться получить это значение, вызовется getter. Поскольку Singleton.value для него еще равен null,
lock(syncRoot) — это full fence. Все кэши в этом месте синхронизируются, и никаких read/write reordering не может произойти.
>он создаст новый объект Singleton и запишет его в Singleton.value — в свою копию. (Поскольку на другом потоке геттер уже завершился, он вполне благополучно зайдет в lock). Д>И в результате мы получим две разные копии Singleton.
Здравствуйте, alexkro, Вы писали:
A>lock(syncRoot) — это full fence. Все кэши в этом месте синхронизируются, и никаких read/write reordering не может произойти.
то есть он синхронизирует полностью всё содержимое всех кэшей?
Здравствуйте, alexkro, Вы писали:
A>lock(syncRoot) — это full fence. Все кэши в этом месте синхронизируются, и никаких read/write reordering не может произойти.
всё-таки не понял. А зачем тогда MemoryBarrier вызывать?
Здравствуйте, Дарней, Вы писали:
Д>всё-таки не понял. А зачем тогда MemoryBarrier вызывать?
Затем, что (читайте выше) Singleton.value может быть уже не null
и другой процессор уже может попытаться его исползовать. В этом
случае
lock(syncRoot)
НЕ ПРОИСХОДИТ. Так вот, для того, чтобы Singleton.value был отправлен
в память __последним__ а не каким-то, нужно и сбросить кеш.
Раньше это называли FlushProcessorCache. А теперь Memory barrier.
Здравствуйте, alexkro, Вы писали:
A>А после этого описывается реальная модель памяти майкрософтовской реализации CLR, которая гораздо более strong, чем спецификация ECMA CLI
Это я читал. А в начале сказано, что вся эта дисскуссия всплыла из-за того, что на каком-то
32-головом IA64 при массивном стресс-тесте как раз и произошло что описано выше.
A>, а также предпологается, что следующия версия стандарта ECMA CLI будет следовать этой модели.
Это я тоже читал. Будем подождать. А то действительно получается что все конечно быстро
и круто, только программировать для таких процессоров должны не люди.
Павел.
Чем больше я об этом думаю, тем больше убеждаюсь,
что старую добрую двойную проверку настоятельно
будут рекомендовать к использованию в очень
исключительных случаях. Потому как два барьера
уже приводят фактически к lock(syncRoot) — разница
настолько мала, что овчинка перестает стоить
выделки.
МЮ>Хотелось бы расставить точки над i. Процессоры Itanium 2 никогда не выпоняют ни аппаратного реодеринга чтения и записи в память, ни вообще как-либо операций реордеринга. "The Itanium 2 processor hardware makes no attempt to reorder instructions to avoid stalls.". (На всякий случай отправляю к документу 25111002.pdf "Intel® Itanium® 2 Processor Reference Manual"). Все операции спекуляции выполняются только посредством программного кода (концепция EPIC).
В спецификации на Itanium (245317.pdf) memory access reodering идет полным ходом.В мане по Itanium2 (пунт 1.1) сказано, что он удовлетворяет специфицации Itanium architecture, за исключением явно указанных ограничений, среди которых идет упомяунтый instruction reodering.
Отказа от memory access reodering в 25111002.pdf я не видел. Напротив,пункт 6.7.5 из 25111002.pdf косвенно указывает
на присутствие реодеринга в Itanium2.
Здравствуйте, alexkro, Вы писали:
A>Так вот, третье неверно. lock засинхронизирует кэш для данного потока (на данном процессоре) так, что Singleton.value получит текущее значение.
всё-таки меня сомнения грызут. А где можно про это прочитать?
A>Вообще, оно даже и на шаге 1 будет иметь текущее значение вследствие того, что запись в переменные, видимые глобально, является release, так же, как и запись в volatile переменные (а с volatile, как ты понимаешь, вообще всё без memory barrier работает).
странно — в MSDN написано, что acquire/release поддерживается для volatile, а вот про static ничего такого не написано.
Блин, ничего толком понять не могу
И всё-таки, чем им вариант с volatile не понравился?
Здравствуйте, alexkro, Вы писали:
A>запись в переменные, видимые глобально, является release, так же, как и запись в volatile переменные (а с volatile, как ты понимаешь, вообще всё без memory barrier работает).
вот например тоже. про volatile написано, а про "просто" глобальные переменные — ни слова.
In
particular, however, the compiler will not move memory reads before a lock
aquire (or a volatile read), and will not move memory writes after a lock
release (or a volatile write), and will respect the MemoryBarrier APIs.
Здравствуйте, bw, Вы писали:
bw>В спецификации на Itanium (245317.pdf) memory access reodering идет полным ходом.В мане по Itanium2 (пунт 1.1) сказано, что он удовлетворяет специфицации Itanium architecture, за исключением явно указанных ограничений, среди которых идет упомяунтый instruction reodering.
bw>Отказа от memory access reodering в 25111002.pdf я не видел. Напротив,пункт 6.7.5 из 25111002.pdf косвенно указывает bw>на присутствие реодеринга в Itanium2.
Да, спасибо. Судя по всему реордеринг может происходить (245317.pdf, section 4.4.7), но не из-за динамического планировщика, которого нет, а из-за доступности данных на разных уровнях иерархии памяти.