Re[5]: Произвол компилятора
От: k55 Ниоткуда  
Дата: 19.06.14 08:33
Оценка:
Здравствуйте, Qbit86, Вы писали:

Q>Ты полагаешь, что последовательность рассуждений компилятора над разными частями кода привязана к тому порядку, в котором предполагается выполнение в рантайме. А это не так, компилятор может хоть задом наперёд твой код анализировать.


Q>В частности он навешивает атрибут статически вычесленных возможных значений на переменные во время этого анализа. Вот он видит переполнение, там кругом литералы, он может статически вычислить возможные значения переменной, при которых переполнения нет, запоминает. При генерации кода для «i < 10» схлопывает его до «true» на основании этого атрибута. При генерации кода для «++i» так его и оставляет, например.


Т.е. компилятор ошибочно исходит из того что "программист не хотел переполнения".
Если есть желание — найдется 1000 возможностей.
Если нет желания — найдется 1000 причин.
Re[2]: каждый раз, когда вы пишете i++ + ++i...
От: Qbit86 Россия
Дата: 19.06.14 08:34
Оценка: 6 (1)
Здравствуйте, Tilir, Вы писали:

T>gcc 4.8.2 вычисляет цикл до i = 111, это несколько смешней, но бесконечного цикла воспроизвести не удалось.


Это потому что «if(x==1) break;»
Глаза у меня добрые, но рубашка — смирительная!
Re[5]: каждый раз, когда вы пишете i++ + ++i...
От: Tilir Россия http://tilir.livejournal.com
Дата: 19.06.14 08:38
Оценка:
Здравствуйте, netch80, Вы писали:

N>Не должно быть никакого "что угодно". Должно быть примерно следующее (и это должно быть в стандарте): в случае, если результат не помещается в целевой тип целого со знаком, компилятор и среда исполнения могут, по выбору:

N>
  • выполнить операцию в соответствии с правилами платформы
    N>
  • выполнить операцию в соответствии с общепринятой арифметикой в дополнительном коде
    N>
  • выполнить операцию, отдав в качестве результата зависящее от реализации значение
    N>
  • по зависящим от реализации правилам выполнить действие, предусмотренное для особой ситуации типа "целочисленное переполнение"

    Без возможности сделать на этапе компиляции arithmetical reassociations (а ваши условия их убивают) вы платите производительностью за то, чего не заказывали. Это противоречит философии C++. Убирать такие вещи под опции -- верное решение. Если вы сомневаетесь и производительность не критична -- подавайте -fwrapv и будет счастье.
  • Re[3]: каждый раз, когда вы пишете i++ + ++i...
    От: Tilir Россия http://tilir.livejournal.com
    Дата: 19.06.14 08:41
    Оценка:
    Здравствуйте, Qbit86, Вы писали:

    Q>Это потому что «if(x==1) break;»


    Спасибо, протупил.
    Re[6]: Произвол компилятора
    От: Qbit86 Россия
    Дата: 19.06.14 09:11
    Оценка:
    Здравствуйте, k55, Вы писали:

    k55>Т.е. компилятор ошибочно исходит из того что "программист не хотел переполнения".


    Компилятор резонно исходит из того, что «программист не хотел переполнения». Ведь обычно программисты не хотят переполнения, когда пишут код типа «x + 15» или «y * 2». И более того, обычно знают, что переполнения там нет, исходя из недоступной компилятору информации об исполнении в рантайме (например, x означает минуты, y — скорость). В данном случае компилятор решил, что программист знает больше него об условиях выхода из цикла; скажем, программист рассчитывает, что цикл завершится по break раньше переполнения, и поэтому можно упразднить условие в шапке.
    Глаза у меня добрые, но рубашка — смирительная!
    Re[7]: Произвол компилятора
    От: Marty Пират https://www.youtube.com/channel/UChp5PpQ6T4-93HbNF-8vSYg
    Дата: 19.06.14 09:21
    Оценка:
    Здравствуйте, Qbit86, Вы писали:

    Q>Компилятор резонно исходит из того, что «программист не хотел переполнения». Ведь обычно программисты не хотят переполнения, когда пишут код типа «x + 15» или «y * 2». И более того, обычно знают, что переполнения там нет, исходя из недоступной компилятору информации об исполнении в рантайме (например, x означает минуты, y — скорость). В данном случае компилятор решил, что программист знает больше него об условиях выхода из цикла; скажем, программист рассчитывает, что цикл завершится по break раньше переполнения, и поэтому можно упразднить условие в шапке.


    break вставил Кодт во избежание, как я понимаю, оригинал скорее всего был без break'а и успешно форматировал диск в бесконечном цикле — т.е. компилятор не мог видеть никакого другого условия выхода из цикла, кроме i<10, но не капли не сомневаясь, сделал цикл бесконечным.
    Маньяк Робокряк колесит по городу
    Re[8]: Произвол компилятора
    От: Qbit86 Россия
    Дата: 19.06.14 09:31
    Оценка:
    Здравствуйте, Marty, Вы писали:

    M>break вставил Кодт во избежание, как я понимаю, оригинал скорее всего был без break'а... т.е. компилятор не мог видеть никакого другого условия выхода из цикла, кроме i<10


    У Винни в оригинале код был без брейка, но с «std::cout <<». Компилятор не может знать, не выпадет ли оттуда исключение.
    Глаза у меня добрые, но рубашка — смирительная!
    Re[13]: каждый раз, когда вы пишете i++ + ++i...
    От: Кодт Россия  
    Дата: 19.06.14 09:35
    Оценка:
    Здравствуйте, watchmaker, Вы писали:

    К>>Я на работе изредка на 16-гектарном компьютере расходую 14 гектар на один процесс.

    К>>Система, только-только проваливаясь в своп, виснет на полминуты.
    W>Это всё понятно. Просто аналогичный эффект и на древнем 32-битном PentiumPro можно получить — в нём же тоже 64 гигабайта адресовать можно. Просто сложно найти человека, который бы поставил столько памяти в такую древнюю машину. Тут проблема со своппингом связана в первую очередь не с тем, что разрядность процессора поменялась, а с тем что банально память стала дешевле, её у компьютеров стало больше, а программы стали прожорлевее. А вместе с тем скорость работы дисковой подсистемы не так значительно выросла — из-за этого несоответствия между увеличивающимися объёмами и непоспевающей за ними скоростью и возникает ощущение что swapping тормозит, равно как и запись многогигабайтного core-файла Но вот адаптируй свой процесс под PentiumPro — уверяю, тормозить и своппится будет куда как заметнее. Так что я бы наоборот сказал, что переход на 64-бита дал тут ускорение или может это от частоты зависит?

    Это зависит от политики свопа, особенно — с хитрым виндовским префетчем.

    W>>>Ну и да, для предотвращения вот этого вот всего в современных ОС есть способы ограничить число процессов, число файловых дескрипторов, объёмы памяти, и разные другие ресурсы. И для устойчивой работы системы это стоит делать (опять же вне зависимости от разрядности ОС).


    К>>Кстати, не знаешь, как в виндах устанавливать из командной строки квоту конкретному процессу?

    W>Не... Вот в linux есть cgroups, и там как раз через команду cgexec можно процесс с потомками всячески попритеснять, в том числе и по объёму памяти. Это, кстати, вполне удобно для запуска всяких ненадёжных программ с непомерными требованиями — в случае нехватки ресурсов процесс будет заперт внутри контейнера, практически не затронув работу остальной системы.

    Теоретически, можно написать утилиту, которая будет создавать Job, выдавать квоты, всячески SetProcessMemoryLimit для свежезапущенного CreateProcess... Но это ещё надо писать.
    Я думал, а вдруг есть какая-нибудь готовая, типа юниксовой nice или виндовой start /affinity-goes-here /priority-goes-here
    http://files.rsdn.org/4783/catsmiley.gif Перекуём баги на фичи!
    Re: ругающим компилятор
    От: jazzer Россия Skype: enerjazzer
    Дата: 19.06.14 09:42
    Оценка: 57 (5) +1
    что он-де предполагает чего не должен.
    Т.е. предполагает, что знаковые целые никогда не переполняются.
    Так вот, он делает это на основании вашего же обещания. Это вы сами при помощи опции -O2/O3/Os (это просто (почти) комбинации других опций) пообещали ему, что переполнений нет и что он может, исходя из этого обещания, оптимизировать программу как хочет:

    -fstrict-overflow

    Allow the compiler to assume strict signed overflow rules, depending on the language being compiled.
    For C (and C++) this means that overflow when doing arithmetic with signed numbers is undefined, which
    means that the compiler may assume that it does not happen. This permits various optimizations.
    For
    example, the compiler assumes that an expression like "i + 10 > i" is always true for signed "i". This
    assumption is only valid if signed overflow is undefined, as the expression is false if "i + 10"
    overflows when using twos complement arithmetic. When this option is in effect any attempt to
    determine whether an operation on signed numbers overflows must be written carefully to not actually
    involve overflow.

    This option also allows the compiler to assume strict pointer semantics: given a pointer to an object,
    if adding an offset to that pointer does not produce a pointer to the same object, the addition is
    undefined. This permits the compiler to conclude that "p + u > p" is always true for a pointer "p" and
    unsigned integer "u". This assumption is only valid because pointer wraparound is undefined, as the
    expression is false if "p + u" overflows using twos complement arithmetic.

    See also the -fwrapv option. Using -fwrapv means that integer signed overflow is fully defined: it
    wraps. When -fwrapv is used, there is no difference between -fstrict-overflow and -fno-strict-overflow
    for integers. With -fwrapv certain types of overflow are permitted. For example, if the compiler gets
    an overflow when doing arithmetic on constants, the overflowed value can still be used with -fwrapv,
    but not otherwise.

    The -fstrict-overflow option is enabled at levels -O2, -O3, -Os.


    Так что, если вы хотите все оптимизации, кроме этой, надо либо
    1) отозвать обещание явно при помощи -fno-strict-overflow (т.е. переполнения возможны); либо
    2) определить арифметику для знаковых так же, как и для беззнаковых, чтобы переполнения не происходило (а все вычисления просто шли по модулю 2), при помоши -fwrapv.

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

    ЗЫ Правильной практикой является изучение того, какие оптимизации включаются на каждом уровне, и проверка, что ваш код требованиям этих оптимизаций соответствует. Либо неиспользование "сборных" уровней оптимизаций и вместо этого использование ручного списка индивидуальных опций — тогда апгрейд компилятора вам ничего не поломает.

    ЗЗЫ Беда только в том, что "сборные" опции — это не только индивидуальные опции, там есть еще некий темный набор действий, опциями не покрытый — я на это напарывался: включаешь -O2, отключаешь все опции, из которых он, вроде бы, состоит — и все равно поведение компилятора отличается.
    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[5]: каждый раз, когда вы пишете i++ + ++i...
    От: Кодт Россия  
    Дата: 19.06.14 09:44
    Оценка:
    Здравствуйте, netch80, Вы писали:

    N>Не должно быть никакого "что угодно". Должно быть примерно следующее (и это должно быть в стандарте): в случае, если результат не помещается в целевой тип целого со знаком, компилятор и среда исполнения могут, по выбору:


    То есть, поменять undefined на unspecified, а если заплатить комитету стандартизации много пожертвований, то вообще на implementation-defined (чтобы авторы компиляторов были вынуждены внести эффекты ещ1 и в свою документацию).


    К>>Горе от ума какое-то. Такая агрессивная преждевременная оптимизация, точно по заветам Дейкстры, вымостила дорогу в ад.


    N>Что ты назвал "оптимизацией"? Такая опция, наоборот, устраняет оптимизации. Если ты её имел в виду, то используй, пожалуйста, более адекватные термины.

    N>Далее, с чего это тут дорога в ад?

    Оптимизация, конечно: вместо test+jnz сделал jmp
    Выточил трансформатор из дуба, потому что никто читать не будет... наивный.
    А дорога в ад — согласно поговорке, вымощена благими намерениями. И преждевременными оптимизациями.
    http://files.rsdn.org/4783/catsmiley.gif Перекуём баги на фичи!
    Re[2]: каждый раз, когда вы пишете i++ + ++i...
    От: Кодт Россия  
    Дата: 19.06.14 09:46
    Оценка:
    Здравствуйте, Tilir, Вы писали:

    T>gcc 4.8.2 вычисляет цикл до i = 111, это несколько смешней, но бесконечного цикла воспроизвести не удалось. Я должен взять нечто совсем древнее для этого?


    Я этому крэю палки в колёса воткнул. Убери break, будет тебе до бесконечности.
    http://files.rsdn.org/4783/catsmiley.gif Перекуём баги на фичи!
    Re[14]: каждый раз, когда вы пишете i++ + ++i...
    От: visual_wind  
    Дата: 19.06.14 09:48
    Оценка:
    Здравствуйте, Кодт, Вы писали:

    К>Теоретически, можно написать утилиту, которая будет создавать Job, выдавать квоты, всячески SetProcessMemoryLimit для свежезапущенного CreateProcess... Но это ещё надо писать.

    К>Я думал, а вдруг есть какая-нибудь готовая, типа юниксовой nice или виндовой start /affinity-goes-here /priority-goes-here

    Насколько мне известно, подобное в данный момент существует только для серверных версий виндов. Вот здесь чел перечислил некоторые и даже описал работу с ThreadMaster.
    Re[5]: каждый раз, когда вы пишете i++ + ++i...
    От: jazzer Россия Skype: enerjazzer
    Дата: 19.06.14 09:55
    Оценка:
    Здравствуйте, netch80, Вы писали:

    N>-O2 это не один уровень на всех, это описанный в документации набор опций, которыми можно управлять и отдельно. Данная конкретная опция называется "strict-overflow", вот описание из info:


    о, ты уже написал про это Какой удар от классика
    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[7]: Произвол компилятора
    От: k55 Ниоткуда  
    Дата: 19.06.14 09:58
    Оценка:
    Здравствуйте, Qbit86, Вы писали:

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


    k55>>Т.е. компилятор ошибочно исходит из того что "программист не хотел переполнения".


    Q>Компилятор резонно исходит из того, что «программист не хотел переполнения». Ведь обычно программисты не хотят переполнения, когда пишут код типа «x + 15» или «y * 2».


    Я бы ожидал от компилятора предупреждения о переполнении при заданных условиях, в конце концов он же "надзорный орган".
    Если есть желание — найдется 1000 возможностей.
    Если нет желания — найдется 1000 причин.
    Re[4]: каждый раз, когда вы пишете i++ + ++i...
    От: monah_tuk Пират http://htrd.su
    Дата: 19.06.14 10:00
    Оценка:
    Здравствуйте, Marty, Вы писали:

    M>Здравствуйте, Кодт, Вы писали:


    M>>>Лопата в переполнении?


    К>>В переполнении только черенок от лопаты, а вот лезвие — в шапке цикла.


    M>Мой cl v14 все нормально переполнил и напечатал


    M>Надо было написать, каким компилятором компилить


    Та же фигня на gcc 4.8.2

    UB на то и UB.
    Re[4]: каждый раз, когда вы пишете i++ + ++i...
    От: Fagin http://radius-server.livejournal.com
    Дата: 19.06.14 10:35
    Оценка:
    Здравствуйте, Кодт, Вы писали:

    N>>Непонятно, почему это UB вообще ломает всё, а не только одно вычисленное выражение.


    К>Ну вот смотри, итерации 0, 1, 2 проходят успешно.

    К>На итерации 3 случилась беда. Дальше может быть всё, что угодно.
    К>Компилятору было угодно не форматировать винчестер, а крикнуть IDDQD.

    Хуже. Если в программе где-либо UB то "всё, что угодно" может произойти со старта программы.

    N>>Вот за такие шутки авторов gcc бить по наглым рыжим мордам.


    К>Горе от ума какое-то. Такая агрессивная преждевременная оптимизация, точно по заветам Дейкстры, вымостила дорогу в ад.


    Я так понимаю ты поддерживаешь "по наглым рыжим мордам". Такой же вывод я делаю из заголовка. И присоединяюсь.
    Я давно подозреваю, что нейтрино имеет спин 3/2 ...
    Re: каждый раз, когда вы пишете i++ + ++i...
    От: Xeor Россия  
    Дата: 19.06.14 10:51
    Оценка:
    Здравствуйте, Кодт, Вы писали:

    К>winnie надыбал потрясающий пример, как из пня сделать крэй-1.


    Обещают в 4.8.3 пофиксить: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=58143
    Re[6]: каждый раз, когда вы пишете i++ + ++i...
    От: netch80 Украина http://netch80.dreamwidth.org/
    Дата: 19.06.14 13:07
    Оценка:
    Здравствуйте, Don Reba, Вы писали:

    N>>Не должно быть никакого "что угодно". Должно быть примерно следующее (и это должно быть в стандарте): в случае, если результат не помещается в целевой тип целого со знаком, компилятор и среда исполнения могут, по выбору:

    N>>• выполнить операцию в соответствии с правилами платформы
    N>>• выполнить операцию в соответствии с общепринятой арифметикой в дополнительном коде
    N>>• выполнить операцию, отдав в качестве результата зависящее от реализации значение
    N>>• по зависящим от реализации правилам выполнить действие, предусмотренное для особой ситуации типа "целочисленное переполнение"
    DR>Предположение, что переполнения быть не может в принципе позволяет комиляютору соптимизировать выражение x * 2 / 2. А ограничения выше — нет.

    Это решается банально, если в выражениях следить за доменами возможных значений параметров. При этом x*2 может, в зависимости от того, откуда взялось, иметь разную семантику. Если это x*2 от пользователя, то оно само по себе уже типа int. Если же оно возникло от индексации массива short'ов, то оно является чем-то вроде int33_t.
    Для компилятора уровня gcc следить за такими вещами тривиально — он и в значительно более сложных материях много гитик.
    Re[5]: Произвол компилятора
    От: TarasB  
    Дата: 19.06.14 13:07
    Оценка:
    Здравствуйте, Qbit86, Вы писали:

    Q>При генерации кода для «++i» так его и оставляет, например.


    При генерации кода "++i" он мог бы запомнить этот атрибут и воткнуть туда проверку, чтоб i не стало больше 2. А, ну да, это ж оптимизация...
    Re[4]: каждый раз, когда вы пишете i++ + ++i...
    От: TarasB  
    Дата: 19.06.14 13:08
    Оценка:
    Здравствуйте, watchmaker, Вы писали:

    W>Так это же суть UB. Или ты не видел тот пример с форматированием диска и clang?


    Не могу вообще понять логику компилятора.
    Подождите ...
    Wait...
    Пока на собственное сообщение не было ответов, его можно удалить.