struct Count
{
int val;
explicit Count(PtrLen<const int> r) CCORE_NOINLINE // no aliasing
{
val=0;
for(int v : r ) val+=v;
}
void sum(PtrLen<const int> r) CCORE_NOINLINE // possible aliasing with val
{
val=0;
for(int v : r ) val+=v;
}
};
int Sum(PtrLen<const int> r) // no aliasing
{
int val=0;
for(int v : r ) val+=v;
return val;
}
Здесь GCC при генерации кода делает предположения о возможном алиасинге данных.
В конструкторе он предполагает отсутствие такового. Поэтому он хорошо раскручивает код с использованием SIMD инструкций.
В методе sum() он предполагает возможность алиасинга. Поэтому код получается менее оптимальным.
В функции Sum() он опять предполагает отсутствие алиасинга и генерирует более оптимальный код.
Мораль.
1) Следите за алиасингом при программировании на скорость.
2) Локальные переменные лучше членов класса.
Здравствуйте, Шахтер, Вы писали: Ш>Небольшой пример.
Код
Ш>
Ш>struct Count
Ш> {
Ш> int val;
Ш> explicit Count(PtrLen<const int> r) CCORE_NOINLINE // no aliasing
Ш> {
Ш> val=0;
Ш> for(int v : r ) val+=v;
Ш> }
Ш> void sum(PtrLen<const int> r) CCORE_NOINLINE // possible aliasing with val
Ш> {
Ш> val=0;
Ш> for(int v : r ) val+=v;
Ш> }
Ш> };
Ш>int Sum(PtrLen<const int> r) // no aliasing
Ш> {
Ш> int val=0;
Ш> for(int v : r ) val+=v;
Ш> return val;
Ш> }
Ш>
Ш>Здесь GCC при генерации кода делает предположения о возможном алиасинге данных. Ш>В конструкторе он предполагает отсутствие такового. Поэтому он хорошо раскручивает код с использованием SIMD инструкций. Ш>В методе sum() он предполагает возможность алиасинга. Поэтому код получается менее оптимальным. Ш>В функции Sum() он опять предполагает отсутствие алиасинга и генерирует более оптимальный код.
Небольшой вопрос, если бы в примере копили в private член класса, посчитал бы компилятор, что на него внешний указатель алиасить не может? В теории вроде бы как должен.
A>Небольшой вопрос, если бы в примере копили в private член класса, посчитал бы компилятор, что на него внешний указатель алиасить не может? В теории вроде бы как должен.
Почему это не может?
A>>Небольшой вопрос, если бы в примере копили в private член класса, посчитал бы компилятор, что на него внешний указатель алиасить не может? В теории вроде бы как должен. W>Почему это не может?
W>
Щито ты этим хотел сказать? Что ты типа можешь `Count ca[100]` использовать как `int ca[100]`, если `int val` — единственный мембер `Count` (и он во всех остальных отношениях standard-layout class)? Не можешь.
Что можно — это
Count c { ... };
c.sum({ reinterpret_cast<int*>(&c), 1 });
Здравствуйте, watchmaker, Вы писали:
W>Здравствуйте, andyp, Вы писали:
A>>Небольшой вопрос, если бы в примере копили в private член класса, посчитал бы компилятор, что на него внешний указатель алиасить не может? В теории вроде бы как должен. W>Почему это не может?
W>
A>>>Небольшой вопрос, если бы в примере копили в private член класса, посчитал бы компилятор, что на него внешний указатель алиасить не может? В теории вроде бы как должен. W>>Почему это не может?
W>>
W>>Это даже не хак для StandardLayoutType
Ш>Только если sizeof(Count) == sizeof (int) . Ш>Можно, конечно, сделать массив длинны 1.
Ш>Ну и, конечно, надо вызывать вот так
Ш>
σ>>UB даже при `sizeof(Count) == sizeof (int)`.
Ш>Это почему? Каст (int *)&ca полностью легален. Хотя лучше сделать так &ca->val . Здесь то уж точно никакого криминала.
Потому что [expr.add]/4.
Для легальности каста `sizeof(Count) == sizeof (int)` не нужен.
А для легальности арифметики с полученным указателем `sizeof(Count) == sizeof (int)` значения не имеет. Она нелегальна в любом случае.
Здравствуйте, σ, Вы писали:
Ш>>>>Ну и, конечно, надо вызывать вот так
Ш>>>>
Ш>>>>ca[53].sum( {(int *)&ca,DimOf(ca)} );
Ш>>>>
σ>>>UB даже при `sizeof(Count) == sizeof (int)`.
Ш>>Это почему? Каст (int *)&ca полностью легален. Хотя лучше сделать так &ca->val . Здесь то уж точно никакого криминала.
σ>Потому что [expr.add]/4. σ>Для легальности каста `sizeof(Count) == sizeof (int)` не нужен. σ>А для легальности арифметики с полученным указателем `sizeof(Count) == sizeof (int)` значения не имеет. Она нелегальна в любом случае.
Бред. Она полностью легальна. Если стандарт в этом месте плохо прописан -- это проблема кривых рук авторов стандарта.
Здравствуйте, σ, Вы писали:
Ш>>Бред. Она полностью легальна. Если стандарт в этом месте плохо прописан -- это проблема кривых рук авторов стандарта. σ>
Если в памяти лежат подряд объекты одного типа, вы совершенно законно можете ездить по ним указателем.
И это не хак, а базовое свойство С++ как языка. Если вы этого не понимаете, то вам в этом мире делать нечего, отправляйтесь лучше в Яву.
Без этого свойства, например, невозможно реализовать vector (или любой аналогичный класс), или более навороченные контейнеры объектов.
А что касается стандарта, то он не создаёт язык, а лишь формализует его. Не всегда достаточно правильно.
Ш>Если в памяти лежат подряд объекты одного типа, вы совершенно законно можете ездить по ним указателем.
Бред.
Ш>Без этого свойства, например, невозможно реализовать vector (или любой аналогичный класс), или более навороченные контейнеры объектов.
Бред.
Ш>А что касается стандарта, то он не создаёт язык, а лишь формализует его.
Формализует разумные действия. К которым удаление гланд через жопу не относится.
Ш> Не всегда достаточно правильно.
Пруф, что в том месте стандарт неправилен.
Здравствуйте, Шахтер, Вы писали:
Ш>Это почему? Каст (int *)&ca полностью легален.
В плюсах не легален. Это ж как раз aliasing. Точнее, сам каст-то легален, но пользоваться им нельзя.
Ш>Хотя лучше сделать так &ca->val . Здесь то уж точно никакого криминала.
Так да, но там речь шла про private член, так что просто не сможешь.
Но всегда можно геттер запилить, чтобы он этот адрес возвращал, так что приватность не роялит никак.
Здравствуйте, andrey.desman, Вы писали:
AD>Здравствуйте, Шахтер, Вы писали:
Ш>>Это почему? Каст (int *)&ca полностью легален.
AD>В плюсах не легален. Это ж как раз aliasing. Точнее, сам каст-то легален, но пользоваться им нельзя.
Aliasing объекта и его члена легален.
Для Standard Layout Types можно получить указатель на первый член класса явным кастом.
struct Alpha
{
[private:]
int val;
};
Alpha obj{};
int *ptr=(int *)&obj; // ptr == &obj.val
A>2. В конструкторе — память формально ещё не доступна для остальной программы, если эта программа не ill formed. A>3. В стеке. Все, что пришло в качестве аргументов не должно указывать на текущий стек фрейм, опять же, если прога не ill formed
Щас бы, в 2k20, путать well-formed/ill-formed и (un)defined behavior.
Кстати, насчёт третьего пункта. Был в C++ (с 03 до 17, ЕМНИП, влом проверять) такой пассаж
Скрытый текст
который некоторые "гении" вроде ОП-а использовали как оправдание бредням типа Ш>Если в памяти лежат подряд объекты одного типа, вы совершенно законно можете ездить по ним указателем.
Почему, например, вызываемая функция не могла угадать адрес аргумента или локальной переменной вызывающей функции, без прямой передачи ей указателя на них или без записи такого указателя в глобальную переменную и т.п.? Вроде этот пассаж разрешает. Хоть из /dev/random считать биты и слепить из них значение указателя.
Здравствуйте, σ, Вы писали:
σ>Почему, например, вызываемая функция не могла угадать адрес аргумента или локальной переменной вызывающей функции, без прямой передачи ей указателя на них или без записи такого указателя в глобальную переменную и т.п.? Вроде этот пассаж разрешает. Хоть из /dev/random считать биты и слепить из них значение указателя.
Почему ж не могла? Могла. Только вот компилятор не обязан на такое не закладываться, когда код функции генерит. И правильно делает имхо, предположение о том, что в функцию приходит указатель на объект, а не хитро сконструированный мусор, достаточно разумно.