All streams
Search
Write a publication
Pull to refresh
214
0
gribozavr @gribozavr

Пользователь

Send message
-fstrict-aliasing включен в Clang по умолчанию. Если его выключить -fno-strict-aliasing, то метаданные TBAA (type-based alias analysis) не будут сгенерированы и TBAA не сможет доказать что float *a и int gi не алиасят друг друга, соответственно векторизации не будет.
Я ваши циклы разнёс по отдельным функциям и скормил LLVM. Вот что с вашими циклами сделал экспериментальный векторизатор (ну и остальные оптимизации):
1. заменён на memset
2. векторизован + хвост memset
3. выброшен за ненадобностью, так как b[10] он не инициализирует :)
4. не векторизирован
5. не векторизирован
6. векторизован
7. не векторизирован
8. векторизован
9. векторизован
Вы не поверите…
$ cat /tmp/z.c
int xpow8(int x) {
  return x*x*x*x*x*x*x*x;
}

int xpow8_loop(int x) {
  int r = x;
  for (int i = 0; i < 6; i++)
    r *= x;
  return r;
}
$ clang -O2 -S -emit-llvm /tmp/z.c
$ cat z.s
...
define i32 @xpow8(i32 %x) nounwind uwtable readnone {
entry:
  %0 = mul i32 %x, %x
  %1 = mul i32 %0, %0
  %2 = mul i32 %1, %1
  ret i32 %2
}

define i32 @xpow8_loop(i32 %x) nounwind uwtable readnone {
entry:
  %mul = mul nsw i32 %x, %x
  %mul.1 = mul nsw i32 %mul, %x
  %mul.2 = mul nsw i32 %mul.1, %x
  %mul.3 = mul nsw i32 %mul.2, %x
  %mul.4 = mul nsw i32 %mul.3, %x
  %mul.5 = mul nsw i32 %mul.4, %x
  ret i32 %mul.5
}
> В данный момент только при -O3

Забыли добавить «в моей версии компилятора, которым я пользуюсь»

… а каст приведёт к нарушению strict aliasing rules.
Каждый шаг — не может. Это раз.

А во-вторых, возьмите сами и попробуйте.

В-третиьх, вот можете посмотреть у Страуструпа описана интересная задача, в которой по асимптотике выигрывает связный список, но на практике std::vector быстрее из-за константы.

www.stroustrup.com/Software-for-infrastructure.pdf

Depending on the machine architecture and the programming language, the answer will be that the vector is best for small to medium values of N. When I ran the experiment on my 8-Gbyte laptop, I found N to be much larger than 500,000.

> Да, есть случаи, когда память нужно затереть из соображений безопасности

>> Соответсвенно проблема «обнуление памяти «здесь и сейчас», вне зависимости от того, кто там дальше будет (или не будет) в неё писать» имеет чисто академический интерес.

Мне кажется вы немного себе противоречите.
Пересмотрел пост — теперь вижу. Да, там есть кусок кода с volatile char* и я скопировал не тот кусок. Да, в моём комментарии имелось ввиду volatile char*.
С точки зрения программиста — это массив. А с точки зрения машинного кода это скорее всего будет только два регистра. Понимаете, инициализация не-volatile данных не наблюдаема и поэтому может быть что угодно пока с точки зрения программиста всё выглядит неотличимо.
Edited: +1, вполне может и заинлайнить.

Первый кусок кода скопирован из статьи как есть. Но очевидно что автор имел ввиду volatile.
Всё ещё недостаточно условий. Если массив не убегает (escapes), то компилятор всё равно может соптимизировать, например, при помощи SROA:
$ cat /tmp/a.c
int g();
int f()
{
  int x[4] = {0};
  x[1] = g();
  x[2] = g();
  return x[1] + x[2] + x[3];
}
$ clang -emit-llvm -S -O2 /tmp/a.c
$ cat a.s
[...]
define i32 @f() nounwind uwtable {
entry:
  %call = tail call i32 (...)* @g() nounwind
  %call1 = tail call i32 (...)* @g() nounwind
  %add = add nsw i32 %call1, %call
  ret i32 %add
}

declare i32 @g(...)

Как видно, массива как таквого в промежуточном коде нет.
for( char* ptr = start; ptr < start + size; ptr += MemoryPageSize ) {
     *ptr;
}


Ага, да. Внезапно DR 1054:
www.open-std.org/jtc1/sc22/wg21/docs/cwg_defects.html#1054

C and C++ differ in the treatment of an expression statement, in particular with regard to whether a volatile lvalue is fetched. For example,


    volatile int x;
    void f() {
        x;    // Fetches x in C, not in C++
    }


В C++11 исправлено.
Это не наблюдаемое поведение. Компилятор имеет право сделать ну, например, SROA (scalar replacement of aggregates), и разбить ваш массив на несколько различных переменных, лежащих даже не в соседних ячейках памяти, а несипользуемые ячейки вообще не распределять в памяти (конечно же если адрес этого массива никуда не убегает (escapes)).

Потому что это mov r/m16, Sreg. А mov Sreg, Sreg вообще нет. Видите, даже тут выходит что-то похожее на типы.
> Граница в том, что mov ax, bx и mov bx, ax это две разные команды, а не одна команда с двумя аргументами, куда что-то можно подставить, а что-то нет.

Вы не поверите, но это одна и та же команда с двумя полями, куда подставляется любой номер регистра. (Посмотрите в интеловском мануале: mov r/m16, r16.)
> Все что делает ассемблер — ищет по таблице.

Вы тоже не можете провести точно границу. Разве граница в том, что «безтиповые» ищут по таблице?

Лексическая/синтаксическая/семантическая проверка в компиляторе ЯВУ тоже может быть реализована таблично. А в ассемблере может быть реализована и нетаблично, а алгоритмически.
> Между прочим, в C есть совершенно легальное средство работать с одной и той же памятью и как с int и как с double без преобразования указателей. Называется union. [...] но компиляторы его до сих пор поддерживают.

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

Information

Rating
Does not participate
Location
Украина
Registered
Activity