Со временем компиляторы становятся умнее, и там где раньше UB код работал "предсказуемо", появляются оптимизации, способные привести к "произвольному" поведению.
2 NOTE Possible undefined behavior ranges from ignoring the situation completely with unpredictable results, to behaving during translation or program execution in a documented manner characteristic of the environment (with or without the issuance of a diagnostic message), to terminating a translation or execution (with the issuance of a diagnostic message).
Возможные UB, относятся к диапазону возможностей: от полного игнорирования ситуации с непредсказуемыми результатами, до поведения в процессе трансляции или выполнения программы в соответствии с документированными характеристиками среды (с выдачей или нет, диагностического сообщения), а так же завершения трансляции или исполнения программы. (с выводом диагностического сообщения).
Так что любое UB, может привести как минимум к завершению программы в лучшем случаи, а в худшем нарушение констинстентности внутреннего состояния с произвольными последствиями.
behavior, upon use of a non portable or erroneous program construct or of erroneous data, for which this International Standard imposes no requirements
2 NOTE Possible undefined behavior ranges from ignoring the situation completely with unpredictable results, to behaving during translation or program execution in a documented manner characteristic of the environment (with or without the issuance of a diagnostic message), to terminating a translation or execution (with the issuance of a diagnostic message).
3 EXAMPLE An example of undefined behavior is the behavior on integer overflow.
There is also a list of undefined behaviors in C11 §J.2 Undefined behavior
Вы видимо довольно плохо знаете историю. Негативные проявления UB, появлялись и просто с выходом следующих поколений, более умных компиляторов. Не падает - это не аргумент, против UB, по крайней мере в приличном обществе. UB очевидно есть, то что вам не могут предъявить падения при некоторых конфигурациях абсолютно ничего не доказывает. Добавьте по меньшей мере -fsanitize, упадет. Но для вас это тоже не аргумент. )
Довольно известная шутка, что UB может запустить форматирование диска. В случаи UB никакой семантики нет, есть только везение, или очень большое невезение. В данном случаи мы опираемся на простой факт, что доступ будет происходить в границах валидной страницы. При этом очевидно происходит доступ за границы массива. В самом плохом и неудачном случаи, компилятор может просто сломать код основываяась на том, что UB теоретически возможна, и будет абсолютно прав.
Т. е. именно они должны доказывать наличие этого ub/падает/прочее. Но пока не получилось(и далее также не получится).
Так это вы и пытаетесь доказать отсутствие UB, предоставляя конечный набор тестов. Любой конечный набор тестов не является доказательством отсутствия UB. В данном случаи, корректная работа конечного набора тестов, и является частным случаем UB.
Наличие UB, можно доказать основываясь на аналитические построения стандарта. В данном случаи мы очевидно имеем выход за пределы массива, выход за пределы массива - является UB. Так что совсем не понятно с чем вы спорите.
Адекватный подход: это признать, что код является UB, но при этом работает на некотором распространенном наборе компиляторов и платформ. Это так и есть, и с этим спорить бессмысленно.
Код не падает, на некотором распространенном конечном наборе платформ и компиляторов. Но при этом он очевидно является UB, так как вы признали, что происходит выход за пределы массива.
И вот, что нам говорит стандарт по этому поводу:
Accessing outside the array bounds is undefined behavior, from the c99 draft standard section Annex J.2 J.2 Undefined behavior includes the follow point: An array subscript is out of range, even if an object is apparently accessible with the given subscript (as in the lvalue expression a[1][7] given the declaration int a[4][5]) (6.5.6).
Отсутствие UB, не возможно доказать работоспособностью, на любом конечном наборе платформ и компиляторов.
Так что да: ваш код не падает, но является явным UB.
Вы вообще в курсе, что отсутствие UB, в общем случаи невозможно доказать, конечным количеством тестов, на конечном наборе конфигураций? Пока, что на наиболее распространенных платформах, и версиях компиляторов "не падает".
Но в данном случаи вы даже не отрицаете, что происходит выход за пределы массива, это очевидно. Выход за пределы массива, по стандарту это UB.
На этом можно разговор заканчивать, потому что вы видимо из тех, кто начинает верить в UB. Только когда начинает падать, на какой-то следующей версии компилятора или другой аппаратной платформе.
Конечно, я нагуглил, вы лучше сами погуглите, что такое UB, а то видимо еще нет. Ваш код - UB, и то что он не падает, лишь счастливая случайность страничной организации системы памяти.
Отлично, если вы скажите, что ваш код работает только по одной причине: потому что не происходит пересечения границы страницы, хотя с точки зрения стандарта это очевидное UB. На этом мы договоримся.
Чтение за \0 это UB, очевидно, если вы выделили память ptr = malloc(1000000), то последний указатель это ptr + 1000000, а не ptr + 1 000 047. Не помешает, как и с любым UB, все будет работать чисто случайно на некоторых входных данных и условиях.
Вы упомянули strlen выходящий за пределы массива из glibc, может быть покажете, а то я в его коде этого не увидел?
Что значит не мешает? У вас индексы вылетают за пределы массива, это все не падает только по счастливой случайности, так не влезает на границу страницы.
Да, и код ваш мягко говоря "переусложнен", если знать размер массива, или иметь гарантии, что он кратен размеру simd регистра. То прекрасно векторизуется самый очевидный вариант:
#include <string.h>
int run_switches(const char* input)
{
size_t n = strlen(input);
int res = 0;
for (size_t i = 0; i < n; ++i)
{
res += (input[i] == 's') ? +1 : 0;
res += (input[i] == 'p') ? -1 : 0;
}
return res;
}
К слову несмотря на strlen, он у меня вышел в два раза быстрее варианта с табличкой, потому что strlen из стандартной библиотеки раскочегарен по полной.
Так и есть, внутренний цикл, ожидает кратность массива step_size, и эта строчка легко вылетает за границы:
for (auto n = 0; n != sizeof(rs); ++n) {
rs[n] = i[n] ? 0 : ~0;
step_r += proc(i[n]);
}
Для чистоты эксперимента я туда трассировку индекса добавил, и да при размере входного массива 1000000, идет доступ к диапазонам индекса 1000000 - 1000047. Собственно в этом и вся соль задачи, без векторизации strlen это все бесполезно, а сделать ее автоматически довольно сложно, так как нет гарантии за выход за границы null terminated string, если шагать по размерности simd регистра. @0xd34df00d предложил вариант со специальной инструкцией SSE4, но компилятор самостоятельно так вряд ли умеют.
Речь шла о команде sse через, которую можно потенциально векторизовать данный цикл, эквивалентный strlen. (там в конце вычитание из начального указателя очевидно)
А откуда вы знаете, какие UB приведут к каким бедам?
https://stackoverflow.com/questions/7682477/why-does-integer-overflow-on-x86-with-gcc-cause-an-infinite-loop
Со временем компиляторы становятся умнее, и там где раньше UB код работал "предсказуемо", появляются оптимизации, способные привести к "произвольному" поведению.
Нет, я конечно могу и перевести:
Возможные UB, относятся к диапазону возможностей: от полного игнорирования ситуации с непредсказуемыми результатами, до поведения в процессе трансляции или выполнения программы в соответствии с документированными характеристиками среды (с выдачей или нет, диагностического сообщения), а так же завершения трансляции или исполнения программы. (с выводом диагностического сообщения).
Так что любое UB, может привести как минимум к завершению программы в лучшем случаи, а в худшем нарушение констинстентности внутреннего состояния с произвольными последствиями.
У нас очевидно разные стандарты:
The definition of undefined behavior:
There is also a list of undefined behaviors in C11 §J.2 Undefined behavior
Вы видимо довольно плохо знаете историю. Негативные проявления UB, появлялись и просто с выходом следующих поколений, более умных компиляторов. Не падает - это не аргумент, против UB, по крайней мере в приличном обществе. UB очевидно есть, то что вам не могут предъявить падения при некоторых конфигурациях абсолютно ничего не доказывает. Добавьте по меньшей мере -fsanitize, упадет. Но для вас это тоже не аргумент. )
Довольно известная шутка, что UB может запустить форматирование диска. В случаи UB никакой семантики нет, есть только везение, или очень большое невезение. В данном случаи мы опираемся на простой факт, что доступ будет происходить в границах валидной страницы. При этом очевидно происходит доступ за границы массива. В самом плохом и неудачном случаи, компилятор может просто сломать код основываяась на том, что UB теоретически возможна, и будет абсолютно прав.
Так это вы и пытаетесь доказать отсутствие UB, предоставляя конечный набор тестов. Любой конечный набор тестов не является доказательством отсутствия UB. В данном случаи, корректная работа конечного набора тестов, и является частным случаем UB.
Наличие UB, можно доказать основываясь на аналитические построения стандарта. В данном случаи мы очевидно имеем выход за пределы массива, выход за пределы массива - является UB. Так что совсем не понятно с чем вы спорите.
Адекватный подход: это признать, что код является UB, но при этом работает на некотором распространенном наборе компиляторов и платформ. Это так и есть, и с этим спорить бессмысленно.
Код не падает, на некотором распространенном конечном наборе платформ и компиляторов. Но при этом он очевидно является UB, так как вы признали, что происходит выход за пределы массива.
И вот, что нам говорит стандарт по этому поводу:
Отсутствие UB, не возможно доказать работоспособностью, на любом конечном наборе платформ и компиляторов.
Так что да: ваш код не падает, но является явным UB.
Так давайте по простому. Ваш код это UB, Да или Нет?
А то эти детские наезды, уже немного поднадоели.
Вы вообще в курсе, что отсутствие UB, в общем случаи невозможно доказать, конечным количеством тестов, на конечном наборе конфигураций? Пока, что на наиболее распространенных платформах, и версиях компиляторов "не падает".
Но в данном случаи вы даже не отрицаете, что происходит выход за пределы массива, это очевидно. Выход за пределы массива, по стандарту это UB.
На этом можно разговор заканчивать, потому что вы видимо из тех, кто начинает верить в UB. Только когда начинает падать, на какой-то следующей версии компилятора или другой аппаратной платформе.
Конечно, я нагуглил, вы лучше сами погуглите, что такое UB, а то видимо еще нет. Ваш код - UB, и то что он не падает, лишь счастливая случайность страничной организации системы памяти.
Отлично, если вы скажите, что ваш код работает только по одной причине: потому что не происходит пересечения границы страницы, хотя с точки зрения стандарта это очевидное UB. На этом мы договоримся.
Чтение за \0 это UB, очевидно, если вы выделили память ptr = malloc(1000000), то последний указатель это ptr + 1000000, а не ptr + 1 000 047. Не помешает, как и с любым UB, все будет работать чисто случайно на некоторых входных данных и условиях.
Вы упомянули strlen выходящий за пределы массива из glibc, может быть покажете, а то я в его коде этого не увидел?
Что значит не мешает? У вас индексы вылетают за пределы массива, это все не падает только по счастливой случайности, так не влезает на границу страницы.
Да, и код ваш мягко говоря "переусложнен", если знать размер массива, или иметь гарантии, что он кратен размеру simd регистра. То прекрасно векторизуется самый очевидный вариант:
https://godbolt.org/z/qx8zGG6do
К слову несмотря на strlen, он у меня вышел в два раза быстрее варианта с табличкой, потому что strlen из стандартной библиотеки раскочегарен по полной.
Так и есть, внутренний цикл, ожидает кратность массива step_size, и эта строчка легко вылетает за границы:
Для чистоты эксперимента я туда трассировку индекса добавил, и да при размере входного массива 1000000, идет доступ к диапазонам индекса 1000000 - 1000047. Собственно в этом и вся соль задачи, без векторизации strlen это все бесполезно, а сделать ее автоматически довольно сложно, так как нет гарантии за выход за границы null terminated string, если шагать по размерности simd регистра. @0xd34df00d предложил вариант со специальной инструкцией SSE4, но компилятор самостоятельно так вряд ли умеют.
Речь шла о команде sse через, которую можно потенциально векторизовать данный цикл, эквивалентный strlen. (там в конце вычитание из начального указателя очевидно)
Автоматически? Нет даже банальный "while (*it != '\0') ++it;" не векторизуется.
У меня ровно противоположный результат char и short, в два раза медленнее int, zen 3.
Насколько я понимаю, это влияет только на ассоциативность в какой-то мере https://coffeebeforearch.github.io/2020/01/12/cache-associativity.html я бы еще понял если речь шла про кэш линии(64 байта).
я взял вариант из поста с гитхаба, и добавил туда эту реализацию.
Не очень понятно в чем разница? Компилятор в праве трансформировать семантически эквивалентные конструкции пока это не нарушает стандарт.