Вчера покурил термин SSE и описание сего Intel-овского бЛАГА. Правильно ли я понял, что он(о?) позволяет распараллелить работу с регистрами? (хрень какая-то... ). В Вики сказано, что "Преимущество в производительности достигается в том случае, когда необходимо произвести одну и ту же последовательность действий над разными данными. Реализация блоков SIMD осуществляется распараллеливанием вычислительного процесса между данными. То есть когда через один блок проходит поочерёдно множество потоков данных.". Код (Text): float a[4] = { 300.0, 4.0, 4.0, 12.0 }; float b[4] = { 1.5, 2.5, 3.5, 4.5 }; _asm { movups xmm0, a ; // поместить 4 переменные с плавающей точкой из a в регистр xmm0 movups xmm1, b ; // поместить 4 переменные с плавающей точкой из b в регистр xmm1 mulps xmm1, xmm0 ; // перемножить пакеты плавающих точек: xmm1=xmm1*xmm0 ; // xmm10 = xmm10*xmm00 ; // xmm11 = xmm11*xmm01 ; // xmm12 = xmm12*xmm02 ; // xmm13 = xmm13*xmm03 movups a, xmm1 ; // выгрузить результаты из регистра xmm1 по адресам a }; То есть, сделав: Код (Text): float a[8][4] = { 300.0, 4.0, 4.0, 12.0 }; _asm { movups xmm0, a[0] ; // поместить 4 переменные с плавающей точкой из a в регистр xmm0 movups xmm1, a[1] ; // поместить 4 переменные с плавающей точкой из b в регистр xmm1 movups xmm2, a[2] ; // поместить 4 переменные с плавающей точкой из b в регистр xmm2 movups xmm3, a[3] ; // поместить 4 переменные с плавающей точкой из b в регистр xmm3 movups xmm4, a[4] ; // поместить 4 переменные с плавающей точкой из b в регистр xmm4 movups xmm5, a[5] ; // поместить 4 переменные с плавающей точкой из b в регистр xmm5 movups xmm6, a[6] ; // поместить 4 переменные с плавающей точкой из b в регистр xmm6 movups xmm7 a[7] ; // поместить 4 переменные с плавающей точкой из b в регистр xmm7 mulps xmm1, xmm0 ; // перемножить пакеты плавающих точек: xmm1=xmm1*xmm0 ; // xmm10 = xmm10*xmm00 ; // xmm11 = xmm11*xmm01 ; // xmm12 = xmm12*xmm02 ; // xmm13 = xmm13*xmm03 mulps xmm3, xmm2 ; // перемножить пакеты плавающих точек: xmm1=xmm1*xmm0 mulps xmm5, xmm4 ; // перемножить пакеты плавающих точек: xmm1=xmm1*xmm0 mulps xmm7, xmm6 ; // перемножить пакеты плавающих точек: xmm1=xmm1*xmm0 movups a[0], xmm1 ; // выгрузить результаты из регистра xmm1 по адресам a movups a[2], xmm3 ; // ит.д. movups a[4], xmm5 ; // ит.д. movups a[6], xmm7 ; // ит.д. }; Эт чЁ? всмысле мы можем, мало того, что "прогоняем 4 вещественных за один проход", еще и получить значения наших 4 "проходов" за один "проход"? Просветление не приходит... P.S. Извините за "не ассемблерную" реализацию части программы
PUSH Несовсем. 1. В регистр мы можем поместить 128бит. Допустим у нас переменные float его размер 4байта. Тоесть в один регистр мы можем загрузить 4 числа. 2. Записа в таком виде Код (Text): mulps xmm1, xmm0 ; // перемножить пакеты плавающих точек: xmm1=xmm1*xmm0 mulps xmm3, xmm2 ; // перемножить пакеты плавающих точек: xmm1=xmm1*xmm0 mulps xmm5, xmm4 ; // перемножить пакеты плавающих точек: xmm1=xmm1*xmm0 mulps xmm7, xmm6 ; // перемножить пакеты плавающих точек: xmm1=xmm1*xmm0 Мы тоже получим прирос. Потому что процессор имеет несколько ALU (4-5 штук). На старых процессорах можно было выполнять SSE команды только на 2 ALU. В современных на 4. То есть и тут мы иммем прирост в 2-4раза. Итог 8-16раз прирост скорости.
То есть, все-таки, в данном случае РЕЗУЛЬТАТ в xmm3, к примеру, УЖЕ НАЧНЕТ ВЫЧИСЛЯТЬСЯ, когда ЕЩЕ НЕ ЗАКОНЧИТСЯ вычисление для xmm1? Именно этот момент, я, по сути дела никак не могу вкурить
PUSH Если команды не зависимы. Тоесть как у нас. То они могут выполняться паралельно. Это зависит от многих параметров были ли ALU свободны или нет. Спаренные команды или нет. Если преположить идеальные условия. То 4 команды начнут выполняться одновременно. В неидеальных условиях. У нас может быть так, что какие-то могут начаться раньше. Но так как у нас дальше есть еще команды, то они будут выполняться как только ALU освободиться. Но на паралельно алу тоже освободиться команда и там тоже будет на замену. Так что это отстование не будет увеличиваться уменьшаться и все будет тоже паралельно. Это как соревнования в басеене. 8 дорожек все плывут паролельно кто-то впереди кто-то позади но все плывут паролельно. Первый проплыл. Второй человек из команды прыгает в бассен. На свою дорожку. Да он в переди. Но на паралельной дорожке тоже приплыл и у него из команды тоже второй номер прыгает в бассеен. Только в бассене 8 дорожек. А у нас 4 ALU 5 труб/каналов(англ pipes).
Вуааа!!! ПРОСВЕТЛЕНИЕ!!! Теперь, другая догадка. А на [censored - подставлять слово по мере извращенности воображения] тогда сейчас ввели идею с двух (трех, четырех... - подставлять слово по мере извращенности воображения) яДР(Ё)(Я)НЫХ процессоров. Не легче ли бы просто налепить на бедный кусочек кремния побольше АЛУ? Я как просветлился - дело в сложности охлаждения. Но ведь вся эта белиберда (в случае с многоЯДР(Ё)(Я)НЫМИ моделями) и так находится на ОДНОМ КУСОЧКЕ КРЕМНИЯ. Начерта было разделять на "группы алу"? Они ведь, вроде, пользуются единым кешем и т.д. Или там на каждое ядро по своему комплекту FPU и проч. радостей? P.S. Наумничал, однако...
В видео карте так и сделали 128 и более(Правда там считается что ALU работает с одним числом типа float или double). Там кэш только общий. Бывает разделенный. Все остальное раздельно. И у каждого ядра свой комплект ALU и прочей радости. Есть эксперементы про задействования ALU других ядер. ALU можно нолипить, но он бы простаивал по большему времени, пока процессор не дойдет до исполнения следующего трудоемкого блока. А многоядерность позволяет сократить такии промяжутки.
То есть исполняемый файл в многоядерном иполнении, мало того, что исполняется, но и преданализируется (если можно так сказать) процессором одновременно? То есть, поступил "заказ" на исполнение. Одно ядро надевает акваланг и погружается в трясину алгоритма, а остальные пробегают код (забугорными словами типа "парсинг" кидаться язык не поворачирается), анализируя, что в нем можно было бы еще бы такое попросчитывать, чтобы первый погрузившийся поменьше парился. Или теперь в сами исполняемые файлы должны содержать таблицы, указывающие "предпросчетные" участки кода. Или вообще многоядерность будет активируется лишь в том случае, когда программист выделит какое-либо определенное вычисление в нить (thread - прости меня,русский язык ) процесса.
Многоядерность позволяет выполнять потоки одновременно. В случае двух ядер это будут два одновременно выполняемых потока, в случае четырёх - четыре и т.д. Сам же поток не может размазаться между двумя ядрами.
PUSH потоками и процессами распоряжается операционная система, она выделяет промежутки времени для выполнения. необязательно один и тот же поток будет выполнятся на одном и том же ядре. вытесняющая многозадачность способна попортить преимущества аппаратного ускорения
Pavia Ну ты и загнул. Это что ж за суперпроцессоры такие "современные" ? В атлонах и Core 2 только по 3 порта запуска SSE и к тому же не универсальных, в частности умножитель у всех только один, поэтому mulps могут распараллелиться со сложениями\мувами и т.п., а цепочка одних mulps - никак
Мда... Я понял ошибку всей моей жизни - надо было не PUSH-ем называться, а БЛОНДИНК-ой... ... А еще программистом себя считал. Основательно покурив это небольшое обсуждение я все-таки просветлился насчет многоЯДРЕННОСТИ. (А то, бедняга, ночами не спал - мучался вопросом "НАХРЕНА?"). Получается, что просто "порции" виртуальных страниц разжевываются несколькими ядрами сразу. Остался только один, наверное такой же глупый, как и предыдущие вопрос - надо ли уяснять как-нибудь в программе (новые оппкоды или специальные указания транслятору), что мы пишем с возможностью многоядерности, или даже написанные для одноядерных машин программы, многодерные пережуют, ускорив их выполнение распараллеливанием (а может все-таки не блондинка? ). И насчет SSE... Получается ускорение мы получаем только засчет вычисления пакетных (байтов, слов - все что душе угодно, в пределах 128 разрядов) за "один проход"? По сути дела для ЭТОГО я и начал череду этих глупых вопросов... P.S. Благодарю за "информацию к просветлению"... Она очень хорошо курится
leo Мне казалось что там 4. А оказалось 3. Тогда я спокоен в следующем поколении сделают. А то я уж было решил что интел все свои козыри раскрыла. PUSH Программы написанные для одноядерников и многоядерников можно запускать на обоих машинах. Другое дело что программа рассчитанная на одноядерник используют один поток то прироста не будет. Если они рассчитаны на несколько потоков, то может оказать в результате ошибке на одноядерной машине они будут работать корректно, а на многоядерной нет. Если ошибок нет то будет прирост производительности. Отдельно, нужно сказать, что программа на писанная под однопроцессорник и один поток должна работать корректно на многоядернике. Но все-таки может работать не корректно. Во время переключения поток может быть запущен на другом ядре. Казалось бы, ничего страшного, но счетчик татов (RDSTC) на разных ядрах разный и в результате расхождения получалось отрицательное время!!! Вроде как ИТ индустрия справилась с этим. Если ты хочешь использовать многоядерность, то для этого есть много путей. К примеру в VS можно указать ключь OpenMP, он подключает соответствующие библиотеки/модули производит настройку и ты сможешь пользоваться OpenMP. Это библиотека макросов, которая позволяет управлять параллельностью. То, есть как управлять. Ты настраиваешь синхронизацию. Можешь указать какой участок нужно распараллелить какой должен обрабатывать первый. Где второй процесс должно подождать первый. Вроде где-то слышал есть очень умные компиляторы, которые сами могут распараллелить код. Это можно сделать и без OpenMP в виндоусе для этих целей есть набор средств/функций. Что касается низкоуровневых механизмов. То для синхронизации есть префикс LOCK и им обязательно надо пользоваться, и пользоваться аккуратно.