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

    Комментарии (74) 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
                        Ответить
                        • Если есть шейдеры на потребительской видяхе на сервере, то и на них можно посчитать, хотя это нужно использовать сторонние библиотеки или самому писать велосипед... Ну и конечно это будет медленее...
                          Ответить
      • Это "оптимизированное" умножение работает за кубическое время (от размера матрицы).
        Ответить
        • Вы можете предложить более простой алгоритм умножения матриц?
          Ответить
          • штрассен
            Ответить
            • штрассен будет менее эффективным. у него сложность ~n^2.81
              Ответить
              • Но ведь 2.81 < 3.
                Ответить
                • Вика говорит, что существует алгоритм Копперсмита — Винограда, у которого сложность ~n^2.37, но множитель больше, чем у метода Штрассена. Причём настолько больше, что показатель степени уже не имеет никакого значения для вменяемых задач.
                  Ответить
              • Вот это некропост - задержка больше 5 лет!

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

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

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

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

      Но!

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

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

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