Как обойти strict aliasing rule?
От: Eeel Россия  
Дата: 16.06.16 22:40
Оценка: 3 (1)
У меня есть некая структурка данных, и к ней надо обращаться то как к двум 32-битным беззнаковым интам, то как к одному 64-битному беззнаковому инту.

По сути, это union. Но, насколько я понимаю, через union по стандарту C++ этого делать нельзя, так как это нарушение strict aliasing и UB (записывать один тип, а читать другой, несовместимый, тип).

struct Foo
{
    union
    {
        std::uint64_t value64;

        struct 
        {
            std::uint32_t value32Lo;

            std::uint32_t value32Hi;           
        };
    };
};


Есть очевидное решение через битовые операции, но я бы не хотел просто надеяться на компилятор — на то, что он избавится от битовых операций самостоятельно (и не будет делать сдвиги и маски, а будет загружать либо 64-битное слово в 64-битный регистр, либо два 32-битных слова в 32-битные регистры, или применит векторизацию), а хотел бы какое-то гарантированно эффективное решение, соответствующее при этом стандарту.

Еще у меня была мысль про буфер char'ов: по стандарту любой тип можно алиазить массивом чаров, тогда что если записать этот в массив std::uint64_t, а прочитать два std::uint32_t — будет ли это нарушением strict aliasing rule? Очевидный ответ — да, поскольку читается один тип, а записывается другой. А может и нет. Этот момент мне неясен.

struct Foo
{
    alignas(std::uint64_t) char buffer[sizeof(std::uint64_t)];
};
Отредактировано 27.06.2016 9:56 S. Schlongberg . Предыдущая версия . Еще …
Отредактировано 21.06.2016 16:20 S. Schlongberg . Предыдущая версия .
Отредактировано 21.06.2016 9:55 S. Schlongberg . Предыдущая версия .
Отредактировано 21.06.2016 9:55 S. Schlongberg . Предыдущая версия .
Отредактировано 17.06.2016 21:01 S. Schlongberg . Предыдущая версия .
Re: Как обойти strict aliasing rule?
От: Erop Россия  
Дата: 17.06.16 00:43
Оценка: +1
Здравствуйте, Eeel, Вы писали:

E>У меня есть некая структурка данных, и к ней надо обращаться то как к двум 32-битным беззнаковым интам, то как к одному 64-битному беззнаковому инту.


А зачем? В том смысле, что на разных платформах могут быть разные эндианы, и одно и тоже 64-битное число таким образом будет мапиться на разные пар 32-битных.
Тебе надо физически с битиками памяти работать, или таки нужны логически старшие и младшие биты?

А ещё есть второй вопрос, зачем ты хочешь, обязательно читать/писать память, а не двигать битики? Вдруг двигать битики быстрее? Например, во многих x86 процах смесь 32-битных и 64-битных инструкций дорогая...
Зачем ты хочешь мешать оптимизатору?

В общем, если ты хочешь работать с памятью напрямую, то есть речь о чём-то железячном, то пиши, читай прямо по адресам. Всё равно это никак не переносимо, а если таки речь идёт об операциях с числами, то бери сдвиги/маски и вперёд...
Только не ясно, зачем тебе маски...
struct Foo
{
    std::uint32_t value32Lo;

    std::uint32_t value32Hi;    

    enum { BITS_IN_LO = 32 };
    std::uint64_t value64() const { return value32Lo|(((std::uint64_t)value32Hi)<<BITS_IN_LO); }
    void set( std::uint64_t v )
    {
        value32Lo = (std::uint32_t)v;
        value32Hi = (std::uint32_t)(v>>BITS_IN_LO);
    } 
};


E>Еще у меня была мысль про буфер char'ов: по стандарту любой тип можно алиазить массивом чаров, тогда что если записать этот в массив std::uint64_t, а прочитать два std::uint32_t — будет ли это нарушением strict aliasing rule? Очевидный ответ — да, поскольку читается один тип, а записывается другой. А может и нет. Этот момент мне неясен.



Конечно, потому, что есть разыне endians, кроме того, ещё и с выравниванием могут быть проблемы, при таком подходе...
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Отредактировано 17.06.2016 9:28 Erop . Предыдущая версия .
Re: Как обойти strict aliasing rule?
От: placement_new  
Дата: 17.06.16 04:21
Оценка:
Здравствуйте, Eeel, Вы писали:

E>По сути, это union. Но, насколько я понимаю, через union по стандарту C++ этого делать нельзя, так как это нарушение strict aliasing и UB (записывать один тип, а читать другой, несовместимый, тип).


Strictly speaking, reading a member of a union different from the one written to is undefined in ANSI/ISO C99 except in the special case of type-punning to a char*, similar to the example below: Casting to char*. However, it is an extremely common idiom and is well-supported by all major compilers. As a practical matter, reading and writing to any member of a union, in any order, is acceptable practice.

http://cellperformance.beyond3d.com/articles/2006/06/understanding-strict-aliasing.html
Re: Как обойти strict aliasing rule?
От: Videoman Россия https://hts.tv/
Дата: 17.06.16 08:35
Оценка:
Интересно, вопрос вроде по тойже теме, а тут:
struct Foo
{
    union
    {
        std::uint32_t value[2];

        struct 
        {
            std::uint32_t value32Lo;

            std::uint32_t value32Hi;           
        };
    };
};


тут такая же проблема с выравниванием, или, из-за того что типы одинаковые, гарантий больше ?
Re[2]: Как обойти strict aliasing rule?
От: Eeel Россия  
Дата: 17.06.16 10:10
Оценка:
Здравствуйте, Erop, Вы писали:

E>А зачем? В том смысле, что на разных платформах могут быть разные эндианы, и одно и тоже 64-битное число таким образом будет мапиться на разные пар 32-битных.


Это в теории, а на практике будет только x86/64 — либо i7, либо Xeon. А big endian я просто не буду поддерживать, как как для этого нужно во всем коде специально делать поддержку и тщательно его весь именно для этого тестировать, что совершенно не стоит потраченных усилий. Так же, как я не собираюсь поддерживать системы меньше 64 бит.

Просто мне не хочется писать код, который в явном виде нарушает strict aliasing и, таким образом, зависит от опций компиляции.
E>Тебе надо физически с битиками памяти работать, или таки нужны логически старшие и младшие биты?

Ну мне надо, например, параллельно сложить нижнее и верхнее 32-битные слова (нижнее с нижним, верхнее с верхним), так, чтобы при сложении нижнего не было переноса в верхнее. Но потом использовать это все как одно 64-битное слово.

E>А ещё есть второй вопрос, зачем ты хочешь, обязательно читать/писать память, а не двигать битики? Вдруг двигать битики быстрее? Например, во многих x86 процах смесь 32-битных и 64-битных инструкций дорогая...

E>Зачем ты хочешь мешать оптимизатору?

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

Меня просто еще интересует концептуальная возможность обходить strict aliasing rule, так как оно является очень большим ограничением.

E>В общем, если ты хочешь работать с памятью напрямую, то есть речь о чём-то железячном, то пиши, читай прямо по адресам. Всё равно это никак не переносимо, а если таки речь идёт об операциях с числами, то бери сдвиги/маски и вперёд...


Ну отмена переносимости кода мало помогает, так как сама по себе не отменяет правила strict aliasing в компиляторах. А от опций и версий конкретных компиляторов не хочется зависеть.

На самом деле, вопрос "зачем" — это несколько другой вопрос (как обычно) и слегка оффтопик.

Меня интересует принципиальный вопрос обхода strict aliasing rule и, прежде всего, ответ на следующий вопрос:

Еще у меня была мысль про буфер char'ов: по стандарту любой тип можно алиазить массивом чаров, тогда что если записать этот в массив std::uint64_t, а прочитать два std::uint32_t — будет ли это нарушением strict aliasing rule? Очевидный ответ — да, поскольку читается один тип, а записывается другой. А может и нет. Этот момент мне неясен.

struct Foo
{
    alignas(std::uint64_t) char buffer[sizeof(std::uint64_t)];
};
Отредактировано 27.06.2016 18:05 S. Schlongberg . Предыдущая версия . Еще …
Отредактировано 21.06.2016 16:20 S. Schlongberg . Предыдущая версия .
Отредактировано 21.06.2016 9:53 S. Schlongberg . Предыдущая версия .
Отредактировано 17.06.2016 10:30 S. Schlongberg . Предыдущая версия .
Отредактировано 17.06.2016 10:23 S. Schlongberg . Предыдущая версия .
Re[3]: Как обойти strict aliasing rule?
От: Erop Россия  
Дата: 17.06.16 10:44
Оценка:
Здравствуйте, Eeel, Вы писали:

E>Ну мне надо, например, параллельно сложить нижнее и верхнее 32-битные слова (нижнее с нижним, верхнее с верхним), так, чтобы при сложении нижнего не было переноса в верхнее. Но потом использовать это все как одно 64-битное слово.


Ну, например
a + b - (1<<32)&((a+b)^~(a^b))
И пусть себе там компилятор разбирается, как оптимизировать?
Ещё всякие векторные инструкции для этого рулят...

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

Его надо делать на всех платформах... Стоит ли выигрыш затрат?

E>Меня просто еще интересует концептуальная возможность обходить strict aliasing rule, так как оно является очень большим ограничением.

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

E>Ну отмена переносимости кода мало помогает, так как сама по себе не отменяет правила strict aliasing в компиляторах. А от опций и версий конкретных компиляторов не хочется зависеть.

Если бы тебе нужна была именно работа с битиками памяти, например в конце стояло бы устройство отображённое на память, то как раз от опций компилятора ты бы тогда не зависел. Возможно, твой код в разных случаях интерпретировал бы ОДНИ И ТЕ ЖЕ битики по ОДНИМ И ТЕМ ЖЕ адресам, как разные числа, но когда дело доходило бы до аппаратных битиков, всё было бы правильно.

А тебе таки нужны числа, а не аппаратные битики. Вот с числами и работай. Так ндёжно, а обходить — мешать оптимизатору.
Иногда оптимизаторы лажают, конечно, но редко. И там, под конкретный оптимизатор, если задача очень нагруженная, можно сделать отдельную ветку кода, завязанную на особенности конкретного транслятора в конкретных настройках. Только это никак со strict aliasing rule не связано, оптимизаторы могут лажать сотнями способов...

E>

E>Еще у меня была мысль про буфер char'ов: по стандарту любой тип можно алиазить массивом чаров, тогда что если записать этот в массив std::uint64_t, а прочитать два std::uint32_t — будет ли это нарушением strict aliasing rule? Очевидный ответ — да, поскольку читается один тип, а записывается другой. А может и нет. Этот момент мне неясен.

Очевидный ответ верный. Просто сравни поведение на двух системах, где разный эндиан, и сразу поймёшь, что это так и есть...
Кроме того, при таком подходе ещё и проблемы с выравниванием добавятся...
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[2]: Как обойти strict aliasing rule?
От: Erop Россия  
Дата: 17.06.16 10:46
Оценка:
Здравствуйте, placement_new, Вы писали:

_>Strictly speaking, reading a member of a union different from the one written to is undefined in ANSI/ISO C99 except in the special case of type-punning to a char*, similar to the example below: Casting to char*. However, it is an extremely common idiom and is well-supported by all major compilers. As a practical matter, reading and writing to any member of a union, in any order, is acceptable practice.


Только оптимизацию может нагнуть...
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re: Как обойти strict aliasing rule?
От: T4r4sB Россия  
Дата: 17.06.16 19:17
Оценка: 2 (1) +1
memmove позволяет битово копировать данные разных типов без УБЭ. Но от огребания от индейца не спасёт, как уже сказали.
Нет такой подлости и мерзости, на которую бы не пошёл gcc ради бессмысленных 5% скорости в никому не нужном синтетическом тесте
Re[2]: Как обойти strict aliasing rule?
От: Erop Россия  
Дата: 18.06.16 07:29
Оценка:
Здравствуйте, T4r4sB, Вы писали:

TB>memmove позволяет битово копировать данные разных типов без УБЭ. Но от огребания от индейца не спасёт, как уже сказали.


Тока это нагнёт оптимизацию ещё мощнее...
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[3]: Как обойти strict aliasing rule?
От: watchmaker  
Дата: 18.06.16 08:10
Оценка: 2 (1)
Здравствуйте, Erop, Вы писали:

E>Здравствуйте, T4r4sB, Вы писали:


TB>>memmove позволяет битово копировать данные разных типов без УБЭ. Но от огребания от индейца не спасёт, как уже сказали.


E>Тока это нагнёт оптимизацию ещё мощнее...


А что за компилятор, который не умеет memmove?
Например, gcc и clang заменяют вызов функции на простое чтение из памяти даже при полностью выключенной оптимизации. Так что каст через memcpy может использоваться для борьбы с алиасингом. https://godbolt.org/g/mtEgIY
Re[4]: Как обойти strict aliasing rule?
От: Erop Россия  
Дата: 18.06.16 08:26
Оценка:
Здравствуйте, watchmaker, Вы писали:

W>А что за компилятор, который не умеет memmove?

W>Например, gcc и clang заменяют вызов функции на простое чтение из памяти даже при полностью выключенной оптимизации. Так что каст через memcpy может использоваться для борьбы с алиасингом. https://godbolt.org/g/mtEgIY

ТС'у нужны операции, вроде сложить два 64-х числа без переноса между 31-м и 32-м битом.

Ты правда думаешь, что реализация через memmove не напряжёт оптимизатор?
Я, например, не в курсе, но как-то сомневаюсь, то оно хорошо соптимизируется...

Хотя вопрос, конечно, интересный.

Я думаю, что самый дружественный к оптимизации подход будет где-то тут:
a + b - ((1ULL<<32)&((a+b)^~(a^b)))

Но может, конечно, и через химию с памятью быстрее, но как-то мало верится. Например, не понятно, что скорее будет векторизовано...
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[5]: Как обойти strict aliasing rule?
От: watchmaker  
Дата: 18.06.16 08:56
Оценка: +1
Здравствуйте, Erop, Вы писали:

E>Здравствуйте, watchmaker, Вы писали:


W>>А что за компилятор, который не умеет memmove?

W>>Например, gcc и clang заменяют вызов функции на простое чтение из памяти даже при полностью выключенной оптимизации. Так что каст через memcpy может использоваться для борьбы с алиасингом. https://godbolt.org/g/mtEgIY

E>ТС'у нужны операции, вроде сложить два 64-х числа без переноса между 31-м и 32-м битом.


Извини, но я отвечал только на выпад в сторону memcpy/memmove. Насколько я понял, ТС больше интересуется правилами алиасинга, а не конкретной задачей.

Так то, конечно, для задачи сложения двух чисел, такое использование memcpy выглядит избыточном. Особенно в свете ориентации ТС на архитектуру x86, ибо там уже лет 20 есть инструкция paddd, которая делает ровно то что нужно — складывает у двух 64-х битных чисел нижние и верхние половинки без переноса между ними. Если уж была бы важна скорость, то стоит просто этой инструкцией воспользоваться (например через соответствующий интринсик).
Re[3]: Как обойти strict aliasing rule?
От: T4r4sB Россия  
Дата: 18.06.16 11:12
Оценка:
Здравствуйте, Erop, Вы писали:

E>Тока это нагнёт оптимизацию ещё мощнее...


Нет, нормальные компиляторы нормально это воспринимают, а вот хвалёный ИЦЦ начинает нести бред при виде меммува, это да. Это гениально, оптимизировать только те способы, которые по стандарту работать не обязаны, а на то, что по стандарту — самый честный способ — вообще забить.
Нет такой подлости и мерзости, на которую бы не пошёл gcc ради бессмысленных 5% скорости в никому не нужном синтетическом тесте
Re[4]: Как обойти strict aliasing rule?
От: Erop Россия  
Дата: 18.06.16 11:23
Оценка:
Здравствуйте, T4r4sB, Вы писали:

TB>Нет, нормальные компиляторы нормально это воспринимают, а вот хвалёный ИЦЦ начинает нести бред при виде меммува, это да. Это гениально, оптимизировать только те способы, которые по стандарту работать не обязаны, а на то, что по стандарту — самый честный способ — вообще забить.


Стоит знать не только стандарт, но и причины заложенных в него ограничений и популярные реализации...
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[4]: Как обойти strict aliasing rule?
От: Eeel Россия  
Дата: 18.06.16 11:52
Оценка: 5 (1)
Здравствуйте, Erop, Вы писали:

E>Ну, например a + b — (1<<32)&((a+b)^~(a^b)) И пусть себе там компилятор разбирается, как оптимизировать?


MSVC 2015 Update 2, похоже, не разобрался, а просто дубово выполняет битовые операции.

    mov    rax, QWORD PTR [rcx]
    lea    rcx, QWORD PTR [rax+rdx]
    xor    rax, rdx
    not    rax
    xor    rax, rcx
    and    rax, rcx
    ret    0
Отредактировано 27.06.2016 18:06 S. Schlongberg . Предыдущая версия . Еще …
Отредактировано 21.06.2016 16:20 S. Schlongberg . Предыдущая версия .
Отредактировано 21.06.2016 9:53 S. Schlongberg . Предыдущая версия .
Re[5]: Как обойти strict aliasing rule?
От: Erop Россия  
Дата: 18.06.16 12:24
Оценка:
Здравствуйте, Eeel, Вы писали:

E>>Ну, например
a + b - (1ull<<32)&((a+b)^~(a^b))
И пусть себе там компилятор разбирается, как оптимизировать?


E>MSVC 2015 Update 2, похоже, не разобрался, а просто дубово выполняет битовые операции.


E>
E>    mov    rax, QWORD PTR [rcx]
E>    lea    rcx, QWORD PTR [rax+rdx]
E>    xor    rax, rdx
E>    not    rax
E>    xor    rax, rcx
E>    and    rax, rcx
E>    ret    0
E>


А где тут минус

и and с чем-то не тем...
Тут или какой-то хитрый рюх или 1<<32 не в то посчитался, (лучше, конечно, 1ULL<<32 писать) или ещё чего...
Если таки это эквивалентный код, во что трудно поверить, то может он и быстрее, кстати...
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[6]: Как обойти strict aliasing rule?
От: Eeel Россия  
Дата: 18.06.16 13:08
Оценка:
Здравствуйте, Erop, Вы писали:

E>А где тут минус

E>и and с чем-то не тем...
E>Тут или какой-то хитрый рюх или 1<<32 не в то посчитался, (лучше, конечно, 1ULL<<32 писать) или ещё чего...

Наверно, потому что имелось в виду

a + b — ( (1ull<<32)&((a+b)^~(a^b)) )

(В оригинале суффикса ull не было.)

Теперь получается

    mov    rax, QWORD PTR [rcx]
    mov    r8, rax
    xor    r8, rdx
    not    r8
    lea    rcx, QWORD PTR [rax+rdx]
    xor    r8, rcx
    mov    rcx, 4294967296                ; 0000000100000000H
    and    r8, rcx
    sub    rax, r8
    add    rax, rdx
    ret    0
Отредактировано 27.06.2016 18:07 S. Schlongberg . Предыдущая версия . Еще …
Отредактировано 21.06.2016 9:52 S. Schlongberg . Предыдущая версия .
Отредактировано 18.06.2016 15:08 S. Schlongberg . Предыдущая версия .
Отредактировано 18.06.2016 13:09 S. Schlongberg . Предыдущая версия .
bit_cast
От: Eeel Россия  
Дата: 18.06.16 13:13
Оценка:
Здравствуйте, T4r4sB, Вы писали:

TB>memmove позволяет битово копировать данные разных типов без УБЭ. Но от огребания от индейца не спасёт, как уже сказали.


Похоже, memcpy/memmove самый годный способ. Clang, GCC и MSVC его оптимизируют на mov. Хотя, окончательное решение, что использовать, буду принимать на основе бенчмарков.

В проекте Chromium даже есть функция bit_cast, которая именно так и сделана.

https://chromium.googlesource.com/chromium/src/+/1587f7d/base/macros.h#76

template <class Dest, class Source>
inline Dest bit_cast(const Source& source) {
  COMPILE_ASSERT(sizeof(Dest) == sizeof(Source), VerifySizesAreEqual);
  Dest dest;
  memcpy(&dest, &source, sizeof(dest));
  return dest;
}


А вот с memmove еще интересней. Согласно пунктам 3.9.2 и 3.9.3 стандарта C++ 14, получается, я могу сделать так:

template <class Dest, class Source>
inline Dest& bit_cast(Source& source) {
    // Проверки
    
    return *((Dest*)std::memmove(&source, &source, sizeof(Dest)));
}


То есть обход strict aliasing rule заключается в вызове ничего не делающей memmove.

Update: Хотя нет, не получается. Там написано:
For any trivially copyable type T, if two pointers to T point to distinct T objects obj1 and obj2

А у меня объекты-то не различные.
Отредактировано 27.06.2016 18:10 S. Schlongberg . Предыдущая версия . Еще …
Отредактировано 21.06.2016 9:52 S. Schlongberg . Предыдущая версия .
Отредактировано 18.06.2016 14:47 S. Schlongberg . Предыдущая версия .
Отредактировано 18.06.2016 14:43 S. Schlongberg . Предыдущая версия .
Отредактировано 18.06.2016 13:55 S. Schlongberg . Предыдущая версия .
Отредактировано 18.06.2016 13:54 S. Schlongberg . Предыдущая версия .
Отредактировано 18.06.2016 13:53 S. Schlongberg . Предыдущая версия .
Отредактировано 18.06.2016 13:53 S. Schlongberg . Предыдущая версия .
Отредактировано 18.06.2016 13:27 S. Schlongberg . Предыдущая версия .
Re: bit_cast
От: T4r4sB Россия  
Дата: 18.06.16 14:10
Оценка:
Здравствуйте, Eeel, Вы писали:

E>То есть обход strict aliasing rule заключается в вызове ничего не делающей memmove.


Интересное кино, не думал о таком. Но проверять бы не рискнул, т.к. стандарт до запятых не знаю, если что и знаю, то только в целом и общем.
Нет такой подлости и мерзости, на которую бы не пошёл gcc ради бессмысленных 5% скорости в никому не нужном синтетическом тесте
Re[2]: bit_cast
От: Eeel Россия  
Дата: 18.06.16 14:42
Оценка:
Здравствуйте, T4r4sB, Вы писали:

TB>Интересное кино, не думал о таком. Но проверять бы не рискнул, т.к. стандарт до запятых не знаю, если что и знаю, то только в целом и общем.


Я неправ, так как там написано:

For any trivially copyable type T, if two pointers to T point to distinct T objects obj1 and obj2


А у меня объекты-то не различные, так что нет, так нельзя, видимо.
Отредактировано 27.06.2016 18:11 S. Schlongberg . Предыдущая версия . Еще …
Отредактировано 21.06.2016 9:52 S. Schlongberg . Предыдущая версия .
Re[7]: Как обойти strict aliasing rule?
От: Erop Россия  
Дата: 18.06.16 16:52
Оценка:
Здравствуйте, Eeel, Вы писали:

E>Здравствуйте, Erop, Вы писали:


E>>А где тут минус

E>>и and с чем-то не тем...
E>>Тут или какой-то хитрый рюх или 1<<32 не в то посчитался, (лучше, конечно, 1ULL<<32 писать) или ещё чего...

E>(В оригинале суффикса ull не было.)

Ясен пень, что не было, это же был псевдокод, всё-таки...

E>Но, что интересно, Clang 3.8 заменяет два независимых сложения на нечто подобное с битовыми операциями:


E>
E>        movabs  rax, -4294967296
E>        mov     rcx, rdi
E>        and     rcx, rax
E>        add     edi, esi
E>        lea     rcx, [rcx + rsi]
E>        and     rcx, rax
E>        or      rdi, rcx
E>        mov     rax, rdi
E>        ret
E>


Ну так же предупреждал, что так, скорее всего, быстрее на актуальный x86
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re: Как обойти strict aliasing rule?
От: flаt  
Дата: 18.06.16 18:56
Оценка:
Здравствуйте, Eeel, Вы писали:

E>По сути, это union. Но, насколько я понимаю, через union по стандарту C++ этого делать нельзя, так как это нарушение strict aliasing и UB (записывать один тип, а читать другой, несовместимый, тип).


Объясните мне, пожалуйста, а для чего ещё union, в таком случае? И как же раньше жили с LARGE_INTEGER?
Re[2]: Как обойти strict aliasing rule?
От: T4r4sB Россия  
Дата: 19.06.16 02:05
Оценка: 4 (1) +1
Здравствуйте, flаt, Вы писали:

F>Объясните мне, пожалуйста, а для чего ещё union, в таком случае?


Чтобы экономно хранить данные разных типов при условии, что они не нужны одновременно.
Нет такой подлости и мерзости, на которую бы не пошёл gcc ради бессмысленных 5% скорости в никому не нужном синтетическом тесте
Re[2]: Как обойти strict aliasing rule?
От: alexanius  
Дата: 20.06.16 13:47
Оценка:
Здравствуйте, placement_new, Вы писали:

_>Здравствуйте, Eeel, Вы писали:


E>>По сути, это union. Но, насколько я понимаю, через union по стандарту C++ этого делать нельзя, так как это нарушение strict aliasing и UB (записывать один тип, а читать другой, несовместимый, тип).


_>Strictly speaking, reading a member of a union different from the one written to is undefined in ANSI/ISO C99 except in the special case of type-punning to a char*, similar to the example below: Casting to char*. However, it is an extremely common idiom and is well-supported by all major compilers. As a practical matter, reading and writing to any member of a union, in any order, is acceptable practice.


_>http://cellperformance.beyond3d.com/articles/2006/06/understanding-strict-aliasing.html


А вот тут один интересный момент. В стандарте не говорится что это UB. Более того, там есть сноска в которой утверждается что в подобной ситуации происходит реинтерпретация данного участка памяти. По идее с точки зрения strict-aliasing здесь тоже всё должно быть нормально.

6.5.2.3 Structure and union members

95) If the member used to read the contents of a union object is not the same as the member last used to
store a value in the object, the appropriate part of the object representation of the value is reinterpreted
as an object representation in the new type as described in 6.2.6 (a process sometimes called ‘‘type
punning’’). This might be a trap representation.


Прошу поправить если я не прав.

PS. да, но всё равно union так лучше не использовать по многим причинам. Как вариант решения проблемы — memcpy или memmove.
Re[3]: Как обойти strict aliasing rule?
От: Eeel Россия  
Дата: 20.06.16 14:09
Оценка:
Здравствуйте, alexanius, Вы писали:

A>А вот тут один интересный момент. В стандарте не говорится что это UB. Более того, там есть сноска в которой утверждается что в подобной ситуации происходит реинтерпретация данного участка памяти. По идее с точки зрения strict-aliasing здесь тоже всё должно быть нормально.


A>

A>6.5.2.3 Structure and union members
A>...

Это секция стандарта C99 ("чистого си"), а не C++. Насколько я понимаю, в C++ этот момент отличается (что плохо).

В C++ там есть только оговорка насчет структур с одинаковым префиксом.

  C++ 14 9.5.1
In a union, at most one of the non-static data members can be active at any time, that is, the value of at
most one of the non-static data members can be stored in a union at any time. [ Note: One special guarantee
is made in order to simplify the use of unions: If a standard-layout union contains several standard-layout
structs that share a common initial sequence (9.2), and if an object of this standard-layout union type
contains one of the standard-layout structs, it is permitted to inspect the common initial sequence of any of
standard-layout struct members; see 9.2. — end note ]...

struct A
{
    int a;
    int b;
    double c;
};
struct B
{
    int a;
    int b;
    float c;
};
union Foo
{
    A a;
    B b;
};
Foo foo;

Здесь допустимо записать foo.a.a и foo.a.b, и прочитать, соответственно, foo.b.a и foo.b.b.
Отредактировано 27.06.2016 18:09 S. Schlongberg . Предыдущая версия . Еще …
Отредактировано 21.06.2016 9:56 S. Schlongberg . Предыдущая версия .
Отредактировано 21.06.2016 9:56 S. Schlongberg . Предыдущая версия .
Отредактировано 21.06.2016 9:52 S. Schlongberg . Предыдущая версия .
Отредактировано 20.06.2016 14:18 S. Schlongberg . Предыдущая версия .
Re[4]: Как обойти strict aliasing rule?
От: alexanius  
Дата: 20.06.16 14:18
Оценка:
Здравствуйте, Eeel, Вы писали:

E>Здравствуйте, alexanius, Вы писали:


A>>А вот тут один интересный момент. В стандарте не говорится что это UB. Более того, там есть сноска в которой утверждается что в подобной ситуации происходит реинтерпретация данного участка памяти. По идее с точки зрения strict-aliasing здесь тоже всё должно быть нормально.


A>>

A>>6.5.2.3 Structure and union members
A>>...


E>Это секция стандарта C99 ("чистого си"), а не C++. Насколько я понимаю, в C++ этот момент отличается (что плохо).


Это 9899:201x, т.е. черновик C11. Но я даже могу более интересную цитаты из старого черновика привести. Вот, например, цитата из 9899:TC2 (черновик C99)

6.5.2.3 Structure and union members

EXAMPLE 3
The following is a valid fragment:

union {
struct {
int alltypes;
} n;

struct {
int type;
int intnode;
} ni;

struct {
int type;
double doublenode;
} nf;
} u;

u.nf.type = 1;
u.nf.doublenode = 3.14;
/* ... */
if (u.n.alltypes == 1)
if (sin(u.nf.doublenode) == 0.0)
/* ... */


Вообще это действительно один из очень неприятных моментов стандарта, который прописан довольно туманно, и с которым постоянно возникают проблемы.
Re[5]: Как обойти strict aliasing rule?
От: alexanius  
Дата: 20.06.16 14:20
Оценка:
Здравствуйте, alexanius, Вы писали:

A>Здравствуйте, Eeel, Вы писали:


E>>Здравствуйте, alexanius, Вы писали:


A>>>А вот тут один интересный момент. В стандарте не говорится что это UB. Более того, там есть сноска в которой утверждается что в подобной ситуации происходит реинтерпретация данного участка памяти. По идее с точки зрения strict-aliasing здесь тоже всё должно быть нормально.


A>>>

A>>>6.5.2.3 Structure and union members
A>>>...


E>>Это секция стандарта C99 ("чистого си"), а не C++. Насколько я понимаю, в C++ этот момент отличается (что плохо).


A>Это 9899:201x, т.е. черновик C11. Но я даже могу более интересную цитаты из старого черновика привести. Вот, например, цитата из 9899:TC2 (черновик C99)


Извиняюсь, ступил и не увидел что ТС про C++ говорит.
Re[2]: Как обойти strict aliasing rule?
От: Mr.Delphist  
Дата: 22.06.16 10:46
Оценка: +1 -2
Здравствуйте, Videoman, Вы писали:

V>Интересно, вопрос вроде по тойже теме, а тут:

V>
V>struct Foo
V>{
V>    union
V>    {
V>        std::uint32_t value[2];

V>        struct 
V>        {
V>            std::uint32_t value32Lo;

V>            std::uint32_t value32Hi;           
V>        };
V>    };
V>};
V>


V>тут такая же проблема с выравниванием, или, из-за того что типы одинаковые, гарантий больше ?


По идее, гарантий никаких. В массиве данные идут подряд (чтобы работала арифметика указателей через sizeof), а в структуре может быть паддинг на 8 байт.

P.S. Товарищи минусующие, не стесняйтесь оставлять свою точку зрения! Ссылки на Стандарт караются плюсом в карму
Отредактировано 08.07.2016 9:01 Mr.Delphist . Предыдущая версия .
Re: Как обойти strict aliasing rule?
От: jazzer Россия Skype: enerjazzer
Дата: 26.06.16 17:01
Оценка:
Здравствуйте, Eeel, Вы писали:

А можно узнать, что за глубокая идея стоит за стиранием своих сообщений? Чтоб геморроя читающим добавить? Восстанови, плиз.
jazzer (Skype: enerjazzer) Ночная тема для RSDN
Автор: jazzer
Дата: 26.11.09

You will always get what you always got
  If you always do  what you always did
Re[3]: Как обойти strict aliasing rule?
От: uzhas Ниоткуда  
Дата: 28.06.16 08:53
Оценка:
Здравствуйте, Mr.Delphist, Вы писали:

MD>По идее, гарантий никаких. В массиве данные идут подряд (чтобы работала арифметика указателей через sizeof), а в структуре может быть паддинг на 8 байт.


кстати, интересное наблюдение
какие есть гарантии, что value32Hi ляжет точно в value[1] ?
Re[4]: Как обойти strict aliasing rule?
От: watchmaker  
Дата: 28.06.16 17:32
Оценка: -1
Здравствуйте, uzhas, Вы писали:

U>Здравствуйте, Mr.Delphist, Вы писали:


MD>>По идее, гарантий никаких. В массиве данные идут подряд (чтобы работала арифметика указателей через sizeof), а в структуре может быть паддинг на 8 байт.


U>кстати, интересное наблюдение

U>какие есть гарантии, что value32Hi ляжет точно в value[1] ?

Предлагаю посмотреть на проблему «массив vs. структура» с этой стороны:

if a is an expression of type cv std::complex<T>* and the expression a[i] is well-defined for an integer expression i, then:
— reinterpret_cast<cv T*>(a)[2*i] shall designate the real part of a[i], and
— reinterpret_cast<cv T*>(a)[2*i + 1] shall designate the imaginary part of a[i].

И попробовать придумать реализацию, где одновременно и std::complex<uint32_t> работает, и value32Hi не попадает в value[1].
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.