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

    −43

    1. 01
    2. 02
    3. 03
    4. 04
    5. 05
    6. 06
    7. 07
    8. 08
    9. 09
    10. 10
    11. 11
    12. 12
    13. 13
    14. 14
    15. 15
    16. 16
    17. 17
    #include <inttypes.h>
    
    uint64_t pow_(uint64_t num, uint8_t pow)
    {
      static const void *array_pow[] = { &&l0, &&l1, &&l2, &&l3, &&l4, &&l5, &&l6,/* ....*/ };
      uint64_t ret = 1;
      goto *array_pow[pow];
      /* ... */
      l6: ret *=num;
      l5: ret *=num;
      l4: ret *=num;
      l3: ret *=num;
      l2: ret *=num;
      l1: ret *=num;
      l0:
      return ret;
    }

    Царский анролл возведения в степень через gcc-шные goto. Сам придумал. Но тут еще вот какая западня. Оптимизируется он хреново. Например, если взять возведение в степень 6 http://goo.gl/6SK2et
    то можно обойтись 3 инструкциями imul в то время как в хрени с метками их целых 6.

    Запостил: j123123, 10 Февраля 2016

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

    • Вот так намного круче получается
      int64_t pow_(uint64_t num, uint8_t pow)
      {
        static const void *array_pow[] = { &&l0, &&l1, &&l2, &&l3, &&l4, &&l5, &&l6,/* ....*/ };
        goto *array_pow[pow];
        /* ... */
        l6: return num*num*num*num*num*num;
        l5: return num*num*num*num*num;
        l4: return num*num*num*num;
        l3: return num*num*num;
        l2: return num*num;
        l1: return num;
        l0: return 1;
      }
      Ответить
      • Но тут же компилятор сам что-то делает. Негоже оставлять компилятору работу.
        Ответить
    • Анролл ради анролла? По идее анроллы нужны, чтобы:
      1. Избавиться от "лишней" инструкции перехода (и dec)
      2. Избавиться от инструкции перехода, ломающей бранч-предиктор
      3. Использовать в разных ветках цикла разные регистры чтобы команды бежали параллельно

      goto не позволяет выполнить ни первое ни второе условие :) Да и не думаю, что jmp *rax также хорошо реализован в предсказателях
      А общая переменная ret ломает третье (хотя его, наверное, можно как-то соблюсти).
      Ответить
      • ну если тупо в цикле делать imul то это будет медленно т.к. количество "вызванных" инструкций imul будет равно тому, в какую степень мы возводим. А если например надо возвести в степень 16 то количество imul оказывается значительно меньшим
        uint64_t pow_16_(uint64_t num)
        {
          return num*num*num*num*num*num*num*num*num*num*num*num*num*num*num*num;
        }

        pow_16_:
                mov     rax, rdi
                imul    rax, rdi   # num2
                imul    rax, rax   # num4
                imul    rax, rax   #num8
                imul    rax, rax   #num16
                ret

        Всего 4 штуки.
        Ответить
        • компилер умнее разраба - это очень смешно. но вместо этого почему то хочется плакать...
          Ответить
          • очень часто компилер тупее разрабов. Не так давно я avr ковырял, там это хорошо видно
            Ответить
            • avr - это нишевая платформа с кучей ограничение. чего ты еще ожидал от портабельного фришного компилятора.
              Ответить
              • я вот что-то почти уверен, что даже нефришные компиляторы там выдадут полный бред. Тот же IAR под ARM например всосал на тех примерах. Думаю что под AVR он тоже всосет.
                Ну не верю я в крутизну компиляторов, глядя на все это безобразие
                Ответить
                • "Тот же IAR под ARM например всосал на тех примерах."

                  Я тебе попытался объяснить что на IAR своя конвенция, и трахания с байт-свопом можно просто полностью обойти. Во-первых. Во-вторых, сила компилера не в том как он отдельно-стоящую функцию оптимизирует - а как он целую программу оптимизирует.
                  Ответить
              • Да, я в свое время сравнивал оптимизации MSVC и GCC и знаете что? MSVC сосет
                То, что коммерческие компиляторы однозначно всегда круче опенсорсных - миф. Да, наверняка есть какие-то там коммерческие компиляторы под экзотические платформы, на которых опенсорсных компилей или нет вообще, или они там плохо оптимизируют (например какой-нибудь SDCC), но это просто потому, что никто этими опенсорсными компиляторами под эмбеддед не занимается плотно
                Ответить
                • Зато MSVC компилил заметно быстрее... Как сейчас - не сравнивал.
                  Ответить
    • А свитч -- это не тру анролл?
      Ответить
      • А компилятор может switch развернуть в кучу if через дерево, а с метками компилятор так уже скорее всего не сделает
        https://web.archive.org/web/20130203094813/http://www.insidepro.com/kk/031/031r.shtml
        Оптимизация switch
        Балансировка логического древа
        Ответить
    • идиотизм.

      1. зачем лейблы если можно по индексу свитч делать? но это не самое главное.

      2. почитай про алгоритмы возведения в степень. проблема не новая. тупое гугление даёт например:
      http://stackoverflow.com/questions/101439/the-most-efficient-way-to-implement-an-integer-based-power-function-powint-int
      Ответить
      • >идиотизм
        Именно поэтому я этот говнокод тут разместил

        >1. зачем лейблы если можно по индексу свитч делать?
        Свич не гарантирует что это будет переведено в нужную форму. Возможно что какой-то комплиятор налепит там кучу cmp je инструкций. Если через метки на лейблы нельзя, можно сделать кучу функций для возведения в степень от 1 до n и сделать массив указателей на функции, потом вызывать их в зависимости от того, в какую степень возводится

        >2. почитай про алгоритмы возведения в степень. проблема не новая.
        Надо специализированный JIT писать для возведения в степень для бигинтов короче
        Ответить
        • > > 1. зачем лейблы если можно по индексу свитч делать?
          > Свич не гарантирует что это будет переведено в нужную форму.

          потому что пара тиков для предсказуемого джампа + префетч будут таким большим тормозом по сравнению с умножением... *roll eyes* *premature optimization is root of all evil*

          > > 2. почитай про алгоритмы возведения в степень. проблема не новая.
          > Надо специализированный JIT писать для возведения в степень для бигинтов короче

          зачем тебе вообще возведение в степень надо?
          Ответить
          • >зачем тебе вообще возведение в степень надо?
            Ну, может я пишу свою CAS и у меня бигинты и надо чтоб большие степени быстро возводить всякие там
            Ответить
        • сам написал ерунду, сам порадовался, еще и для других выложил
          Ответить
        • > Возможно что какой-то комплиятор
          Поэтому используется GCC-изм, чтобы гарантировать, какой компилер и что будет лепить? :)
          Ответить
          • как я уже сказал, этот GCC-изм можно заменить массивом из указателей на функции
            Ответить
    • Байтоёбил-байтоёбил. И всё зря. Потому что нормальный алгоритм юзает log(N) умножений, а не N...
      uint64_t pow_(uint64_t num, uint8_t pow) {
          uint64_t res = 1;
          while (1) {
              if (pow & 1)
                  res *= num;
              pow >>= 1;
              if (!pow)
                  break;
              num *= num;
          }
          return res;
      }
      Ответить
      • как я и упоминал выше -
        http://stackoverflow.com/questions/101439/the-most-efficient-way-to-implement-an-integer-based-power-function-powint-int
        - "squaring".

        даже компилер ему выше (возведение в константную степень 16) это как бы пытается подсказать.
        Ответить
      • А как-нибудь само умножение можно заоптимизировать? Компиляторы умеют оптимизировать умножение через всякие там сдвиги. В этом достаточно легко убедиться, зайдя на этот http://gcc.godbolt.org/ и понаписывав всяких примитивных функций, умножающих что-то на константу. Например, если надо возвести число 2056 в степень 50 (2056 в двоичной системе счисления это 1000 0000 1000) можно операцию умножения 2056 свести ко всяким там сдвигам (через специальный JIT). Хотя опять таки не факт, что такая оптимизация будет эффективней, надо детальней изучать вопрос
        Ответить
        • > как-нибудь само умножение можно заоптимизировать
          Дык умножение на современных процах и так выполняется примерно как сложение... У аппаратного умножителя, емнип, самый тормоз в том же месте, где и у сумматора - в распространении переноса на всю длину регистра в самом-самом конце.

          Вот умножение чисел произвольной размерности - да, серьёзно можно оптимизнуть. Почитай в GMP как они это делают. Там очень нетривиальные алгоритмы, никак не связанные с байтоёбством.
          Ответить
          • И что там нетривиального? Они не за O(количество регистров в первом числе * количество во втором) умножают?
            Ответить
            • Да, есть быстрые алгоритмы, например https://ru.wikipedia.org/wiki/Метод_умножения_Шёнхаге_—_Штрассена
              Но там не рассматривается сценарий, когда мы неким хитрым образом анализируем само число и делаем через JIT некую специализированную функцию, которая потом это все очень быстро умножает через сдвиги какие-нибудь
              Ответить
            • Да, меньше чем за N*M. Причём, емнип, уже для чисел, которые в RSA.

              Ну а для пачек подряд идущих умножений (возведение в степень) всякие монтгомери есть, которые тоже делают меньше действий. На RSAшных масштабах профит, емнип, тоже есть.
              Ответить
        • Можно, но оно окупается начиная с разрядности, которых сегодня даже в RSA нет.
          Ответить
          • Вот, посмотрел в исходниках. Для x86 MUL_TOOM22_THRESHOLD = 18. Т.е. Toom 2 (аля умножение Карацубы) со сложностью O(N^1.585) вместо O(N^2) врубают уже на 18 блоков по 32 бита (т.е. ~600 бит).
            Ответить
            • Ну найди мне это тогда в modpow в других языках, если оно настолько полезное.
              Ответить
              • https://github.com/python/cpython/blob/1e9c2985544751a65d8f597f302fc3b2b7cffbd8/Doc/whatsnew/2.3.rst

                Multiplication of large long integers is now much faster thanks to an implementation of Karatsuba multiplication, an algorithm that scales better than the O(n*n) required for the grade-school multiplication algorithm. (Original patch by Christopher A. Craig, and significantly reworked by Tim Peters.)

                #define MPD_KARATSUBA_BASECASE 16
                Ответить
                • И да, оно используется в реализации modpow.
                  Ответить
                  • Хз, ну найди его в жаве. Biginteger.modPow()
                    Ответить
                    • А если найду?

                      The version of BigInteger in jdk8 switches between the naive algorithm, The Toom-Cook algorithm, and Karatsuba depending on the size of the input to get excellent performance.

                      Умножение-в-столбик-бляди соснули?

                      http://hg.openjdk.java.net/jdk8/jdk8/jdk/file/tip/src/share/classes/java/math/BigInteger.java#l1471

                      P.S. Ну вот FFT не завезли, но это и понятно, числа по 30000+ бит на практике хуй найдёшь.
                      Ответить
                      • P.S. А, ну да, в самом modPow только в столбик умножают почему-то. Видимо из-за того, что из-за Монтгомери там полное умножение не нужно, только обрезок на младшие N бит.
                        Ответить
                        • Я это монтгомери так и не понял
                          Ответить
                          • Да его реально трудно понять. Я его даже реализовывал ради интереса, а вот объяснить на пальцах не могу...
                            Ответить
                      • >А если найду?
                        Получишь конфетку красный богатырь

                        А вот хер тебе

                        private static final int KARATSUBA_SQUARE_THRESHOLD = 128;
                        4 кибилота

                        Можешь еще тут почитать http://hg.openjdk.java.net/jdk8/jdk8/jdk/file/tip/src/share/classes/java/math/BigInteger.java#l1883

                        Он когда появился, кстати, неужто в 8? Жавабляди и тут соснули? (я в старой версии изучал)
                        Ответить
                        • SQUARE это для возведения в квадрат. А просто для умножения - 80 (т.е. 2400 бит).
                          Ответить
                          • За пределами modpow многоразрядная арифметика практически не используется же (криптография в поле простого числа)
                            Ответить
                    • http://www.tutorialspoint.com/java/math/biginteger_modpow.htm
                      Ответить
                      • Что ты хотел этим показать?
                        Ответить
                        • Что он нашелся в жабе.
                          Ответить
                          • А кто-то в этом сомневался? 3.14dar предлагал найти умножение Карацубы и прочие продвинутые алгоритмы умножения в реализации modPow, а не само modPow...
                            Ответить
                            • Так в modpow вроде самое сложное - это взятие по модулю, а не умножение.
                              Ответить
                              • Сложное в смысле сложности алгоритма или вычислительной сложности? Если второе, то, пожалуйста, с пруфами.
                                Ответить
                                • Я думал это одно и тоже. Имею в виду О-большое.

                                  Никогда не слышал о более-менее вменяемых хотябы за N^2
                                  Ответить
                                  • Гуглятся методы, которые с какой-то точностью считают 1/y, потом умножают на x.

                                    И я не думаю, что это будет работать так же быстро, как и умножение.
                                    Ответить
                                  • Сложный алгорифм без циклов имеет O(1), а бесконечный цикл из одной строчки - ... хз сколько, но ты понял идею.
                                    Ответить
                                  • Ты алгоритм montgomery reduction хотя бы осилил?
                                    Ответить
                                    • Не слышал о таком.

                                      Почитал на русской педии - так и не понял что он дает. Один хер там нужно большое число нужно брать по модулю большого числа (см. return у функции MonPro)

                                      Может ты прояснишь?
                                      Ответить
                                      • > большое число нужно брать по модулю большого числа
                                        Там, емнип, деление на n совсем читерское получается - сравнение да вычитание.

                                        З.Ы. На русской вики хуйня какая-то вместо статьи. Посмотри английскую, там с примерами и т.п.
                                        Ответить
                                        • >> На русской вики хуйня какая-то вместо статьи

                                          Так какая-то хуйня вместо русской вики
                                          fixed
                                          Ответить
                                          • Импортозамещение.
                                            Ответить
                                            • Когда Сашка был пацаном с разбитыми коленками он очень любил мороженное "48 копеев". Аппетитный шоколадный чуть подтаявший брикет, зажатый меж двух тонких вафель, мгновенно улучшал настроение. К сожалению стоил этот брикет совсем не 48 копеек, и Сашка не мог радовать себя им каждый день.
                                              Прошло время и мир изменился. Сашка стал Александром Семеновичем, школа сменилась работой, беззаботное детство - ежедневной рутиной. Мороженное незаметно исчезло из его жизни совершенно неожиданно. В студенческие годы его заменило пиво, после - различные суши и роллы. Забылось-то самое ощущение приятной прохлады и сладких липких пальцев
                                              Как-то после работы Александру Семеновичу позвонили. Он снял трубку и услышал голос своей беременной жены
                                              - Саш, купи мороженное.
                                              - Какое опять мороженное?
                                              - Хочется. Пломбира. Шоколадного
                                              - С вафелькой?
                                              - Да, конечно с вафелькой.
                                              - Штукатуркой сверху намазать?
                                              - Дурак ты, Саша. -ответила жена и положила трубку.
                                              По пути домой Александр Семенович заехал в магазин и, подойдя к стойке с мороженным, увидел давно забытое им лакомство. "48 копеек. Шоколадное" - гласила надпись на несколько изменившийся упаковке. В порыве ностальгии он купил сразу все брикеты, коих насчиталось 28 штук.
                                              Ответить
                                            • Выйдя на улицу с пакетом. полным мороженного, он решил не откладывать дело в долгий ящик, и, достав одно из них, частично снял этикетку. Первое что заметил Александр Семенович - запах. Мороженное пахло как то совершенно иначе. Не прохладной сладостью, но замерзшей водой. Мужчина взял брикет за вафли и внимательно осмотрел. Подтеки по его краям были скорее похоже жижу, чем на густую пенку. Александр Семенович предчувствовал недоброе, но все же откусил кусочек. Вкус был просто отвратительным и не имел ничего общего с воспоминаниями детства. Кристалики льда хрустели на зубах и впивались в язык. Он возмущения мужчина негромко выругался и отправился обратно в магазин.
                                              - Я трубую менеджера - сказал он молодой кассирше, которая была так испугана его гневным взглядом, что не смогла ничего возразить.
                                              Менеджер был значительно моложе и гораздо гораздо наглее Александра Семеновича
                                              - Так это не то мороженное. Тут нет надписи "нестле". Это наше, российское. с местного молокозавода.
                                              - А где тогда то само, не российское?
                                              - А нигде. Импортозамещение
                                              Мужчина хотел что то ответить, но замер с открытым ртом. Он поставил пакет на кассу и молча вышел из магазина. впервые за много лет ему стало так обидно, что по щеке покатилась скупая слеза
                                              Ответить
                                          • Российской
                                            fixed #2
                                            Ответить
                                      • >Так в modpow вроде самое сложное - это взятие по модулю, а не умножение.
                                        Вот оно после каждого умножения и берет модуль. Алгоритм не осилил, и, скорее всего, и не осилю, т.к. уже вышел из среды в которой это изучали.

                                        Касательно нашего обсуждения- какая там сложность?
                                        Ответить
            • На данный момент самый быстрый алгоритм умножения длинных чисел - это быстрое преобразование Фурье. O(N*log(N))

              Только хз почему его не юзают в стандартных библиотеках питона и джавы.
              Ответить
              • На какой длине числа он эффективнее хотя бы умножения в столбик?
                Ответить
              • > почему его не юзают
                А если найду?

                https://github.com/python/cpython/blob/1fe0fd9feb6a4472a9a1b186502eb9c0b2366326/Modules/_decimal/libmpdec/mpdecimal.c#L5484

                1024 блока (32000+ бит) получается
                Ответить
                • Увидел там кучу goto
                  Ответить
                  • Это так в сишке обрабатывают "исключения", не обращай внимания. В данном случае goto улучшает читабельность, а не портит её.
                    Ответить
                • https://github.com/python/cpython/blob/1fe0fd9feb6a4472a9a1b186502eb9c0b2366326/Modules/_decimal/libmpdec/mpdecimal.c#L133 - вручную заанролленый двоичный поиск через if

                  Надо короче компилтайм макросы с гомоиконностью в сях лепить, чтоб можно было писать обычный двоичный поиск, и потом анролить его в нужных местах
                  Ответить
    • Пал-палыч, опять вы свое говно код разворачиваете.
      Ответить

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