1. Си / Говнокод #26384

    +1

    1. 1
    2. 2
    3. 3
    4. 4
    5. 5
    6. 6
    // https://github.com/microsoft/PQCrypto-SIDH/blob/ebd1c80a8ac35e9ca2ef9680291a8a43b95a3bfa/src/random/random.c#L22
    
    static __inline void delay(unsigned int count)
    {
        while (count--) {}
    }

    ... guess what?

    Запостил: j123123, 25 Января 2020

    Комментарии (81) RSS

    • https://godbolt.org/z/Snjhn2 Какая задержка)))
      Ответить
    • Без «volatile» такие штуки не работают.
      Ответить
      • Именно поэтому я за "PHP".
        Ответить
        • Починил:
          static __inline void delay(volatile unsigned int count)
          {
              while (count--) {};
          }


          delay(0xFFFF); при -O3 ещё компилируется, а на delay(0xFFFFF); компилятор может зависнуть, потому что он пытается анроллить. Его ещё нужно проинструктировать, чтобы он не анроллил циклы.
          Ответить
          • Какая проблема останова )))
            Ответить
          • > пытается анроллить

            Серьёзно? Какой багор )))
            Ответить
            • Это ещё что! Вот такой вот код:
              struct Res {
                  int arr[1000000];
              };
              
              struct Res foo()
              {
                  struct Res r = { .arr = { [6000] = 22} };
                  return r;
              }

              «Visual Studio» анроллит: https://gcc.godbolt.org/z/Bcbb2B. И если «[6000]» заменить на что-нибудь побольше — получится багор. Старые версии просто намертво зависали, а новые выдают многомегабайтные бинарники или, если переусердствовать, падают:
              1>C:\dev\barop.c(10): fatal error C1002: compiler is out of heap space in pass 2
              Ответить
              • Какой barop )))
                Ответить
              • Перебрал все ключи оптимизации (/O1, /O2, /Og, /Ox, /Os, /Ot и т. п.), результат не меняется.

                Проверил другие компиляторы.

                icc инициализирует не инструкцией процессора, а тупо создаёт гигантскую инициализированную секцию в объектном файле (дохрена директив db).

                clang и gcc выдают компактный код.
                Ответить
              • Проверим другие компиляторы.

                Watcom, Digital Mars, LCC создают гигантскую секцию из директив db.

                Borland не понимает синтаксис { [6000] = 22}.

                Pelles C, Orange C, tcc создают компактный код.
                Ответить
              • Кстати, переменная r создаётся в стеке, поэтому компиляторы, создающие гигантскую секцию данных (icc, Watcom, Digital Mars, LCC), вставляют цикл копирования из секции данных в стек (хорошо хоть, что без анролла).
                Ответить
                • Ничего не понимаю… И это компиляторы? Говно какое-то, заедушники, блядь. Библиотека им дала memset — вызывай! Вызывай memset, блядь! Не хочу, хочу анроллить цикл! Что такое? Это компиляторы?! Это компиляторы?! Суки… Мудачьё — программисты. Оптимизаций нацепляли, циклы анроллят — заедушники, блядь, ёбаные…
                  Ответить
                  • Именно поэтому я за "еду".
                    Ответить
                  • Я теперь понял, почему Нидлесс за «tcc». Мало того, что «tcc» сам компактный, он ещё не пытается анроллить, когда не нужно.
                    Ответить
                    • Всюду компактный?
                      Ответить
                      • Замкнутый и ограниченный.
                        Ответить
                        • tcc менее ограниченный чем остальные, только он позволяет писать так:
                          https://govnokod.ru/25216

                          А ещё у него есть рекурсивные структуры и lvalue-касты.
                          Ответить
              • Компилятор «CompCert» из «INRIA», написанный на «Петухе» («Coq»), на этом примере падает в корку.
                Ответить
          • И это оптимизации? >1млн. декрементов реально заметно быстрее маленького цикла?
            Ответить
          • Вот так можно, без всяких volatile переменных:
            static __inline void delay(unsigned int count)
            {
                while (count--) {asm volatile("");};
            }
            
            void shit(void)
            {
              delay(0xFFFFF);
            }
            Ответить
            • Именно поэтому я за форт, он 1:1 отображается в сконпелированный код. Что ты напейсал, то он и сделает.
              Ответить
              • Именно поэтому я против форта боярда
                Ответить
              • Нихуя. Даже ассемблер не всегда 1:1 отображается.
                Ответить
                • 1:1 последовательность действий. Если напишешь 100500 подряд записей в одну и ту же ячейку они все должны там быть.

                  Это вам не питушарский сишный конпелятор, который считает себя умнее программиста и выкидывает целые ветки кода. Я максимум встречал оптимизацию щеледырок и свёртку констант.
                  Ответить
                  • В теории он тоже мог бы, но придётся добавить volatile семантику. Например слова @V и !V, эффекты от которых нельзя выкидывать и переставлять.
                    Ответить
                  • > питушарский сишный конпелятор, который считает себя умнее программиста и выкидывает целые ветки кода
                    Но за этим будущее. Правда, если выкидывает он эти ветки потому, что код эквивалентен, а не потому, что хочет наказать программиста за невнимательность. Именно поэтому я без языки без UB.
                    Ответить
                  • > 1:1 последовательность действий.

                    https://en.wikipedia.org/wiki/Assembly_language#Assembler
                    Some assemblers may also be able to perform some simple types of instruction set-specific optimizations. One concrete example of this may be the ubiquitous x86 assemblers from various vendors. Most of them are able to perform jump-instruction replacements (long jumps replaced by short or relative jumps) in any number of passes, on request. Others may even do simple rearrangement or insertion of instructions, such as some assemblers for RISC architectures that can help optimize a sensible instruction scheduling to exploit the CPU pipeline as efficiently as possible.[citation needed]
                    Ответить
                    • >> [citation needed]

                      Я правильно понял, что реального примера не будет?
                      Ответить
                      • Реальный пример - некий OSF/1 assembler, который выполняет некий instruction scheduling - см ниже
                        Ответить
                      • http://bitsavers.informatik.uni-stuttgart.de/pdf/dec/osf/2.0/AA-PS31B-TE_Assembly_Language_Programmers_Guide_Feb94.pdf


                        Стр. 100 :
                        > The volatile option instructs the assembler that subsequent load and store instructions may not be moved in relation to each other or removed by redundant load removal or other optimization. The volatile option is less restrictive than noreorder; it allows the assembler to move other instructions (that is, instructions other than load and store instructions) without restrictions.
                        Ответить
                        • Даже ассемблеры теперь умнее программиста. Какой ко-ко-ко-кошмар (((
                          Ответить
                          • Поэтому надо в машинных кодах писать - там точно все четко будет
                            Ответить
                  • https://web.eecs.umich.edu/~prabal/teaching/eecs373-f11/readings/Assembler.pdf#Alpha%20Directives

                    https://sourceware.org/binutils/docs/as/Alpha-Directives.html

                    .setfeature
                    Enables or disables various assembler features. Using the positive name of thefeature enables while using ‘nofeature’ disables
                    ...
                    volatile These control whether and how the assembler may re-order instructions. Accepted for compatibility with the OSF/1 assembler, but as does not do instruction scheduling, so these features are ignored.
                    Ответить
    • В общем-то не фатально, просто чаще будет дрочить open() и read(). Но почему не просто usleep()? Один хуй этот кусок только для линукса.
      Ответить
      • В том-то и дело что не будет. Компилятор это выкинет как ненужный хлам
        Ответить
        • > выкинет

          Да и хуй с ним. Я про внешний цикл, который всё равно будет работать.
          Ответить
      • > Но почему не просто usleep()?

        Может usleep() это слишком долго, ну там системный вызов, переключение процесса, а продрочить цикл можно без всяких системных вызовов
        Ответить
        • Кстати, в многозадачной среде вообще бывает точный usleep()?
          Ответить
        • >usleep() это слишком долго

          Я бы сделал в спин-локе yield
          #elif defined(__LINUX__)
          ...
          pthread_yield()
          Ответить
          • Pthreads — вроде кроссплатформенная библиотека. Только на многих платформах нити будут создаваться внутри текущего процесса, т. е. другим процессам вне очереди отдаваться ничего не будет.
            Ответить
            • Что-то я сомневаюсь, что где-то он будет использовать ненативные потоки, это ж уже гринтреды какие-то получаются. А у нативных потоков никаких «процессов» нет, их ОС планирует исключительно по приоритету (во всяком случае в «Windows»).
              С «pthread_yield()» другой багор есть:
              If the calling thread is the only thread in the highest priority list
              at that time, it will continue to run after a call to sched_yield().

              То есть если у нас есть десять ждущих потоков с приоритетом 5, «sched_yield()» из потока с 6 не даст ни одного кванта ждущим тредам. Для таких ситуаций надо юзать «sleep(0)».
              Ответить
              • Вспомнил, где использовались ненативные потоки: в порте «pthreads» для «DOS». Да, был такой страшный порт. Он устанавливал свой обработчик аппаратного прерывания таймера и по нему переключал нити.
                Ответить
                • Именно поэтому я за «DOS».
                  Ответить
                  • Зато у тебя царское управление приоритетами! Не то, что в заедушных многозадачных системах, которые сами забирают кванты времени.
                    Ответить
              • Really, it's that simple.

                This has absolutely nothing to do with cache coherence latencies or anything like that. It has everything to do with badly implemented locking.

                I repeat: do not use spinlocks in user space, unless you actually know what you're doing. And be aware that the likelihood that you know what you are doing is basically nil.

                There's a very real reason why you need to use sleeping locks (like pthread_mutex etc).

                In fact, I'd go even further: don't ever make up your own locking routines. You will get the wrong, whether they are spinlocks or not. You'll get memory ordering wrong, or you'll get fairness wrong, or you'll get issues like the above "busy-looping while somebody else has been scheduled out".

                And no, adding random "sched_yield()" calls while you're spinning on the spinlock will not really help. It will easily result in scheduling storms while people are yielding to all the wrong processes.

                Sadly, even the system locking isn't necessarily wonderful. For a lot of benchmarks, for example, you want unfair locking, because it can improve throughput enormously. But that can cause bad latencies. And your standard system locking (eg pthread_mutex_lock() may not have a flag to say "I care about fair locking because latency is more important than throughput".
                Ответить
                • > I repeat: do not use spinlocks in user space, unless you actually know what you're doing.

                  В STM32 нет никакого «user space». Поэтому я за STM32.
                  Ответить
                  • Есть.
                    Ответить
                    • Насколько я знаю, максимум что там есть - MPU (Memory Protection Unit). Нормального MMU туда не завозили
                      Ответить
                      • Ну да, только MPU. Но это уже разделение на user/supervisor.
                        Ответить
                        • Ну такое себе разделение. В ARM можно особой инструкцией отрубать прерывания http://we.easyelectronics.ru/STM32/atomic-makrosy-dlya-arm.html , и MPU это никак не запретит. Если можно отключать прерывания, вряд ли это можно юзерспейсом назвать. Чтоб можно было про это говорить как про юзерспейс, надо такие опасные инструкции уметь блокировать.
                          Ответить
                          • А ничего, что CPS - привилегированная инструкция и в юзермоде не работает?
                            Ответить
                            • А, ну да. Только я эти непривилегированные режимы на STM32 никогда не использовал, впрочем как и MPU. Лучше уметь по-царски писать в порты из любого места, чем еще какие-то системные вызовы анскильные делать
                              Ответить
                              • В «DOS» нет никаких непривилегированных режимов. Именно поэтому я за «DOS».
                                Ответить
              • >То есть если у нас есть десять ждущих потоков с приоритетом 5, «sched_yield()» из потока с 6 не даст ни одного кванта ждущим тредам. Для таких ситуаций надо юзать «sleep(0)».

                Да, это известно. Мало того yield может полностью игнорироваться шедулером.

                A hint to the scheduler that the current thread is willing to yield its current use of a processor. The scheduler is free to ignore this hint.

                Но это всяко лучше чем busy-wait.
                Ответить
          • там же Линус писал, что это ебаная хуета
            Ответить
            • Про спинлоки и мудаков которые используют их в юзерспейсе?
              Да, было недавно такое.
              Ответить
        • в шарпе кстати есть специальный SpinWait, который сначала пытается просто поистязать процессор небольшие промежутки времени, а если к этому моменту не наступает требуемого события - все-таки уйти в слип
          Ответить
          • Если уж писать M$-only код, то я бы предпочёл YieldProcessor

            #define YieldProcessor() __asm { rep nop }
            
            #define YieldProcessor _mm_pause
            
            #define YieldProcessor __yield

            https://docs.microsoft.com/en-us/windows/win32/api/winnt/nf-winnt-yieldprocessor?redirectedfrom=MSDN

            Хотя вполне допускаю что люди просто пытаются согреться, запуская холодными зимними вечерами busy wait.
            Ответить
    • > while (count--) {}
      > -O3
      А ожидалось что-то другое?

      -O1
      shit:
              movl    $1048576, %eax
      .L2:
              subl    $1, %eax
              jne     .L2
              ret
      Ответить
      • Компилятор нигде не гарантирует, что он такой цикл не выкинет и с флагом -O0. Стандартом вообще эти флаги и разрешенные для них оптимизации не описываются
        Ответить
        • Сразу видно, что ты анскильный. Питушарский стандарт засунь туда, откуда взял. Любой с образованием выше птушника знает как это будет работать.
          Ответить
          • Царь считает, что любые флаги, кроме -O2, не нужны.
            Ответить
            • А «-O2» зачем? Для анскиллябр, не способных сразу в оптимальный код?
              Ответить
      • Если нужно сохранить цикл, нужно использовать модификатор volatile, который гарантирует, что переменная пробежит все промежуточные значения, а не флаги оптимизации, которые ничего не гарантируют.
        Ответить
    • static __inline void delay(unsigned int count)
      {
          while (count) {
            printf("%d sausages frying on the pan\n", count);
            printf("sizzle sizzle sizzle\n");
            printf("one goes bang\n");
            printf("%d sausages now frying on the pan\n", --count);
          }
      }
      Ответить
      • Да, такой цикл компилятор выкинуть не может: тут побочный эффект, а именно вывод.
        Ответить
        • Только надо вывод в /dev/null перенаправить, и можно будет ичпользовать в продакшене.
          Ответить
          • Открыть /dev/null через fopen и заменить printf на fprintf.
            Ответить
            • int dnull = open("/dev/null", O_WRONLY);
              close(1);
              close(2);
              dup2(dnull, 1);
              dup2(dnull, 2);
              Ответить
              • Надеюсь когда-нибудь выйдет компилятор, который оптимизирует ваши говна в простой
                REP NOP ; F3 90
                Ответить
    • Именно поэтому я против "СИ"
      Ответить
    • Там лучше есть, в КОНТРЕБУТИНГ.md5

      If your contribution is more than 15 lines of code, you will need to complete a Contributor License Agreement (CLA). Briefly, this agreement testifies that you are granting us permission to use the submitted change according to the terms of the project's license, and that the work being submitted is under appropriate copyright.

      Please submit a Contributor License Agreement (CLA) before submitting a pull request. You may visit https://cla.microsoft.com to sign digitally. Alternatively, download the agreement (Microsoft Contribution License Agreement.docx or Microsoft Contribution License Agreement.pdf), sign, scan, and email it back to [email protected] Be sure to include your github user name along with the agreement. Once we have received the signed CLA, we'll review the request.
      Ответить

    Добавить комментарий