1. C++ / Говнокод #5286

    +181

    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
    18. 18
    19. 19
    void MultipleSquareMatrix(Matrix*rres, Matrix*mul1, Matrix* mul2)
    {	
    	int N = mul1->height();
    	Matrix rmul1(N,N);
    	Matrix rmul2(N,N);
    
    	#define SM (CLS / sizeof (double))
    
    	for (i = 0; i < N; i += SM)
    		for (j = 0; j < N; j += SM)
    			for (k = 0; k < N; k += SM)
    				for (i2 = 0, rres = &res[i][j],
    					  rmul1 = &mul1[i][k]; i2 < SM;
    					++i2, rres += N, rmul1 += N)
    					for (k2 = 0, rmul2 = &mul2[k][j];
    						k2 < SM; ++k2, rmul2 += N)
    						for (j2 = 0; j2 < SM; ++j2)
    							rres[j2] += rmul1[k2] * rmul2[j2];
    }

    Перемножение квадратных матриц.....

    Запостил: nsa_a1, 15 Января 2011

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

    • Как при простом перемножении матриц можно получить столько циклов? о_О
      Ответить
      • Вот я тоже смотрю и не могу понять...
        Ответить
      • как один человек мог столько выдохнуть? (C)
        Ответить
        • Тут не в выдохе дело. Тут вдохнули и долго не выдыхали, так что всё, в принципе, логично.
          Ответить
      • Кто-то минуснул. Наверное, автор. ))
        Автор, откликнитесь, каким мегаоптимизированным алгоритмом пользовались? Предлагаю закопирайтить эту интелектуальную собственность под своим именем "Метод ***(фамилия)".
        Ответить
    • i, j, k... а дальше что? Кэп подсказывает: i2, k2, j2
      upd: они и ещё и глобальные?! 0_о
      Ответить
    • Простите за тупость, но где здесь?
      Ответить
      • Что Вы не находите?
        С++?
        Вот:
        Matrix rmul2(N,N);
        - класс или структура с конструктором и перегруженым оператором operator[].
        Ответить
        • И прочими перегруженными операторами, естественно.
          Ответить
    • Директива препроцессора здесь ВАЩЕ в тему!
      Ответить
    • Это перемножение матриц оптимизированное под упреждающую выборку процессора, с учетом размера строки кеша.
      Ответить
      • Можно пруфлинк?
        Ответить
        • Попробуйте скомпилировать такую программу, и сравнить скорость работы с классическим способом (два вложенных цикла)

          #define CLS 64
          #define N 1000

          double res[N][N];
          double mul1[N][N];
          double mul2[N][N];

          int main()
          {

          #define SM (CLS / sizeof (double))

          double *rmul1;
          double *rmul2;
          double *rres;

          int i,j,k,i2,j2,k2;

          for (i = 0; i < N; i += SM)
          for (j = 0; j < N; j += SM)
          for (k = 0; k < N; k += SM)
          for (i2 = 0, rres = &res[i][j],
          rmul1 = &mul1[i][k]; i2 < SM;
          ++i2, rres += N, rmul1 += N)
          for (k2 = 0, rmul2 = &mul2[k][j];
          k2 < SM; ++k2, rmul2 += N)
          for (j2 = 0; j2 < SM; ++j2)
          rres[j2] += rmul1[k2] * rmul2[j2];
          return 0;
          }

          в данном случае длина строка кеша выставлена в 64 байта, такая длина сейчас на большинстве современных Intel Core 2 и Quad.
          в линейке процов AMD придется посмотреть в спецификации Cashe line size )
          Ответить
          • >int i,j,k,i2,j2,k2;
            Это часть оптимизации?
            Ответить
          • А зачем тогда в говнокод запостили, коли это так?
            Большой прирост хоть?
            Ответить
            • если не выйдем за рамки кэша (матрица меньше размера кеша) то прирост составляет почти в 10 раз, если матрицы не влезают в кеш, то прирост в среднем может быть 6-7раз (зависит от размера матриц)
              Ответить
          • А где можно почитать про этот алгоритм? Как хоть называется?
            Ответить
            • читайте про упреждающую выборку процессов, про устройство кеша (как делятся на уровни, когда строки помечаются как неправильные, про протокол MESI, также есть смысл знать как утроена шина, что висит на северном и южном мосту, про NUMA ну и т.д.
              Ответить
              • белые люди SIMD кодогенерируют (предварительно проведя алгоритмические оптимизации, разумеется), а не с кэшем в пукан долбятся.
                Ответить
                • вы можете ещё ускорить процесс работы добавив например XMM инструкции, однако, можете сравнить, если мы в лоб напишем перемножение матриц с SIMD, то прирост не столь значителен. Ведь вы будете подряд проходить только одну матрицу (перемножаем строку на столбец) однако вторую матрицу мы прыгаем по столбцам, что практически в каждом случае порождает ошибку обращения в кеш, после чего вместо 10-12 тактов на эл-т мы имеем от 300 до 1500 тактов, что суммарно сильно замедляет работу программы, и SIMD вам здесь не поможет
                  Так что, Вам в любом случае придется уменьшать количество промахов при обращении в кеш, конечно мы не можем напрямую влиять на кеш, однако можем подстроится под внутреннюю оптимизацию процессора и использовать её в своих целях.
                  Ответить
                  • столько пафоса и столько фейлов

                    >перемножаем строку на столбец
                    не так, золотце, вторая матрица транспонируется, не знал? ну вот поэтому я и говорю - байтоебство без знания алгоритмов - как минимум смешно.
                    Ответить
                    • с транспонированием будет немного медленнее, ведь нам приходится транспонировать матрицу,на это уходит время, сдесь этот шаг пропускается, и работает быстрее чем с транспонированием.
                      Ответить
                      • деточка, ну слил ты и слил, что с того?
                        проверь матрицы не 10х10, а 50000х50000 и удивись

                        а раз этот алгоритм здесь, то какой вывод?
                        Ответить
                        • У вас есть столь производительный компьютер чтобы быстро подсчитать матрицы 50к на 50к? о_О
                          Ответить
                          • намек, что все упрется далеко не в кэш проца так и не дошел до адресата.
                            Ответить
                            • Собственно тут оптимизация под упреждающую выборку, а не под размер кеша. А что касается кода, то он тут, потому что даже матрицы умножать быстрее через вот такую вот жопу )
                              Ответить
                            • я думаю вам стоит почитать очень известную статью
                              lwn.net/Articles/250967
                              Ответить
                    • мужик, минусанул за незнание теории.. сейчас кеш самое узкое место в любом алгоритме...
                      на PS3 уже давно проще матрицу из кеша перемножить еще раз, чем читать ее из памяти (линия 128 байт)... поищи инфу в блоге Micke Aston...
                      Ответить
                      • показать все, что скрыто>в любом
                        в твоих
                        Ответить
                        • показать все, что скрытов любом...
                          Ответить
                          • показать все, что скрытотолько в твоем
                            Ответить
                            • ну если ты пишешь алгоритмы которые умножают число на само себя и не пишут в память, тогда да, только в моем...
                              задумайся когда нибудь, что тормозит в твоих программах, и в 99% случаем это будет память... доступ в память мимо кеша это несколько сотен тактов процессора, что равно сотне команд работающих в пределах кеша...
                              Ответить
                              • и SIMD не выход, так как он весть такой быстрый вре равно лезет в медленную память...
                                Ответить
                              • пушкофф итить тебя налево. в моих алгоритмах вообще-то тормозила сеть. забудь про гей-дев - это ооочень маленький сегмент it.
                                Ответить
                              • Вы правы, и именно потому лучше стараться влезать в кеш, а современные контроллеры в проце стараются угадать паттерны использования памяти, и делать упреждающее чтение.

                                Еще К.К. в "оптимизации программ по памяти" писал про этов 2003м году.
                                Ответить
                  • дайте ссылку на инфу об алгоритме...
                    Ответить
                • Помоему, если действительно нужно оптимизировать, то стоит применить и то и то, если скорости всё ещё после алгоритмической оптимизации не хватает.
                  Ответить
                  • Обычно, первым делом оптимизируют код под кеш (размер, строки, уровни, возможно под NUMA, если это AMD Opteron) а потом уже используют SIMD, ну или все сразу, если последовательно не получается. В данном случае мы перемножаем матрицы, алгоритм нам некуда оптимизировать.
                    Ответить
                    • >мы перемножаем матрицы, алгоритм нам некуда оптимизировать.
                      К примеру, иногда можно это сделать на сопроцессорах, например на видюхе.
                      Ответить
                      • на сервере обычно нет видюхи со всякими CUDA
                        Ответить
                        • Если есть шейдеры на потребительской видяхе на сервере, то и на них можно посчитать, хотя это нужно использовать сторонние библиотеки или самому писать велосипед... Ну и конечно это будет медленее...
                          Ответить
                • > а не с кэшем в пукан долбятся.
                  зачот
                  Ответить
              • Послушайте, какой к черту мост? мосты в 1999-м году остались
                Ответить
                • Ну не совсем. Северный ещё лет десять просуществовал, хотя и менял названия.

                  Про южный не знаю. Возможно, и сейчас встречается в неинтегрированном виде под другими названиями.

                  Кстати, я застал технику без Super I/O («южного моста») на материнке. Ещё на 486 обычно Super I/O выполняли в виде отдельной карточки для шины ISA (даже не PCI и не VLB, ибо скорости ISA для контроллеров тех лет хватало).
                  Ответить
                  • Мемори хаб назывался, да. К нему цеплялся CPU по FSB, из него же росла AGP, и оперативка.

                    Южный переименовался в IO Hub, и из него рос корень PCI (не экспресс!), USB хост адаптер, и вероятно (S)ATA.

                    А всякую мелочевку типа биоса, FDC и прочих 16550 UART и i8042 (PS/2) выселили в Super IO, который связали с IO хабом через LPC

                    https://image.slidesharecdn.com/chap1chipset-120302093847-phpapp02/95/chap1-chipset-7-728.jpg?cb=1330681329


                    Топология менялась несметное число раз: от прямой "системной шины" в 5150 (просто никаких мостов: все на одной шине сидят от памяти до LPT) впоследствии неазванной ISA, до уезда всего вклюая рут комплекс PCI-Express в проц. Когда мы используем nvme, мы реально включаем его в проц, лол.

                    вот относительно новая хуйня (чипсет зачем-то сверху, хотя он какбы южный)
                    https://www.pugetsystems.com/wp-content/uploads/2025/03/Intel-Chipset-Comparison-Z890-vs-B860-bs-H810.png

                    PCI-E два: четвертые в чипсете, пятые в проце.
                    На "юге" осталась USB, сеть, звук, TPM для битлокера, и всякие SMBus, нужные чтобы там скоростю вентиляторов и температурными зонами рулить через ACPI
                    Ответить
                    • Теперь понятно, что у Интела означают MCH и ICH.

                      А SMBus вроде поверх I2C работает?
                      Ответить
                      • Мне кажется, SMBus это раcширение I2C. I2C более древний, и для более тупых устройств типа сенсора, а по SMBus упомянутый сегодня SPD работает например. Но надо вики глянуть, я давно читал.

                        Интересно, в какой момент появился термин "north bridge".
                        Вот еще пара схемок из книжки про PCI:
                        https://postimg.cc/FYthyYL1
                        https://postimg.cc/SnWkDm6r

                        тут видно, что PCI уже сразу назывался host bridge, и уже сразу в память надо было ходить через него (это логично, так как на этой шине всякое уйстройство -- басмастер, и все могут срать в память без CPU, каждый сам себе DMA), а PCI-ISA bridge всего лишь адаптер на шине PCI, и там еще в BIOS setup хостбриджу надо было рассказывать, какие адреса не трогать, чтб они достались ISA.

                        Отдельная ебля с прерываниями (они были свои в PCI и они шарились) и их надо было мапить на прервания x86. Когда в PCI-Express завезли MSI, все выдохнули
                        Ответить
      • Это "оптимизированное" умножение работает за кубическое время (от размера матрицы).
        Ответить
        • Вы можете предложить более простой алгоритм умножения матриц?
          Ответить
          • штрассен
            Ответить
            • штрассен будет менее эффективным. у него сложность ~n^2.81
              Ответить
              • Но ведь 2.81 < 3.
                Ответить
                • Вика говорит, что существует алгоритм Копперсмита — Винограда, у которого сложность ~n^2.37, но множитель больше, чем у метода Штрассена. Причём настолько больше, что показатель степени уже не имеет никакого значения для вменяемых задач.
                  Ответить
                • < 3.

                  хахаха, да ведь это же ХУЙ вместе с яйцами!
                  Ответить
              • Вот это некропост - задержка больше 5 лет!

                Прямо связь поколений.
                Ответить
                • зато коротко и по делу
                  Ответить
                  • Но неверно :(
                    Ответить
                    • блядь, не на ту дату посмотрел
                      минусуйте меня
                      Ответить
                    • На самом деле верно или неверно зависит от стоимости умножения на конкретной машине. Вдруг у нас умножение выполняется за то же время, что и сложение?
                      Ответить
                      • тут речь шла про асимптотику (про сферические бесконечные матрицы в вакууме)
                        big O определяется исходя из предпосылки, что время умножения и сложения плавающих петухов константно
                        Ответить
            • Штрассен хорош. Но он не позволяет Царям жонглировать порядком операций, чтобы «с кэшем в пукан долбиться».
              Ответить
      • >перемножение матриц оптимизированное под упреждающую выборку процессора
        Я скорее всего уже безнадёжно отстал от этой области, поэтому не спорю, но:

        Где ассемблерные комманды процессора по упреждающему чтению данных в кэш?

        Коли так, то это перемножение матриц оптимизированное под кэш конкретного размера.

        Процессор сам сделает упреждающую выборку, если сможет. И это заслуга процессора, а не алгоритма.
        Ответить
        • Вы правильно написали "если сможет".
          Как говорится проще перечислить, когда упреждающая выборка работает хорошо, чем когда она работает плохо. Наша цель сделать так, чтобы она работала на нас, а не против нас.
          Ответить
          • Вы так пишете, словно бы вы написали книгу Касперски про оптимизацию памяти, где он в разные банки попасть старался. В реальности же это всё из серии "как правильно обойти двумерный массив"
            Ответить
            • Что за книга?
              Ответить
              • https://pda.coollib.in/b/539229-kris-kasperski-tehnika-optimizatsii-programm-effektivnoe-ispolzovanie-pamyati/read


                там немного двадцатый век (EDO, BEDO, все прелести асинхронной DRAM), но много и годноты вроде борьбы с переменной на границе кеш линеек
                Ответить
                • К слову, coollib сменил несколько доменов. Он был на коме, потом ком РКН забанил. Потом был на хузе, но РКН и хуз забанил. Теперь вот, значит, ин.
                  Ответить
                  • Открылась книжка-то?
                    Ответить
                    • А почему нет? Это же дежавю, а не какой-то хитрый формат.
                      Ответить
                • FSB 100 МГц. Новому поколению не понять...

                  Я, кстати, ставил планки PC-100 и PC-133 на машину с частотой шины 66 МГц.
                  Ответить
                  • Но это уже, хотя бы, SDRAM, кажется первый DDR. ЕМНИП, частоту памяти лучше бы было подбирать к частоте FSB, и собссно это было развлечение оверклокеров.

                    Оверклокеры остались, но теперь у них есть SPD, XMP и пр
                    Ответить
    • дайте пожалуйста ссылку на описание алгоритма если он общедоступен!!!
      Ответить
      • lwn.net/Articles/250967
        пожалуй лучше ссылки не придумать, здесь довольно подробно все рассмотрено.
        Ответить
        • я имел ввиду математическое обоснование эффективности вашего алгоритма (я в нем не разбирался, так как его наверное проще написать чем разобраться)...
          Ответить
          • собственно алгоритм то здесь обычный (строка на столбец), только здесь я без транспонирования матрицы обеспечил последовательный доступ к эл-там матриц, которые мы перемножаем, но конечно запись в результирующую матрицу оставляет желать лучшего. Я просто добился многократного снижения кол-ва миссов при обращении в кеш.
            Ответить
            • как я понимаю для матриц 4х4 эта оптимизация не годится, так как первое же чтение из нее, затаскивает ее полностью в кеш, и единственное за чем нужно следить это выравнивание на границу кеша...
              Ответить
              • да, здесь речь идет о случаях, когда рабочий объем превышает размер кеша
                Ответить
            • господи, какой длинный способ сказать очевидное: "читать из памяти последовательно лучше, чем непоследовательно"
              Ответить
        • дайте-ка еще разок ссылочку, а то не прочитают
          Ответить
    • Что ж, проверил. Действительно, матрицы 1000x1000 перемножаются таким образом немного быстрее (на заявленных процессорах возможно будет много быстрее).

      Но!

      Стиль ужасен. Нет комментариев. Ничего не говорящие имена переменных. #define посреди кода. Какая-то чушь с C++. Наконец, всё это работает только если N кратно SM (восьми).

      Итого -- таки говнокод.
      Ответить
      • Советую вам тестировать приложение с REALTIME_PRIORITY_CLASS, иначе данные не совсем корректны. А код полнейший ГК )
        Ответить
    • Хелло, здесь кто-то спрашивал, с
      Ответить

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