Текущее время: Вс 28 апр 2024 14:30

Часовой пояс: UTC + 4 часа




Начать новую тему Ответить на тему  [ Сообщений: 212 ]  На страницу Пред.  1 ... 4, 5, 6, 7, 8  След.
Автор Сообщение
 Сообщение Чт 23 июн 2011 14:19
Профиль  
Разработчик идей
Аватара пользователя
Сообщения: 4577
Откуда: Минск, Беларусь
Зарегистрирован: Ср 14 ноя 2007 19:00
SHW писал(а):
Но если не складировать ящики друг на друга, то первый подход тоже сойдёт.
Это из-за отсутствия обработки таких мультиточечных контактов объекты в некоторых играх дрыгаются, лёжа друг на друге и "плывут" по поверхностям? Не, кривая физика не годится. ИМХО, если требуются сложные расчёты для реалистичного обсчёта, то ассемблер в помощь. Набор команд SSE для работы с векторами ввели ещё во втором пентиуме - использование их скомпенсирует сложность расчётов. Щас изучаю. Прыкольно - даже четыре квадратных корня из float'ов одной командой можно вычислить! :teeth:

_________________
Сообщество креативных механоидов:
aim-fans.ru


 Сообщение Пт 24 июн 2011 10:41
Профиль  
Бывший разработчик
Сообщения: 66
Откуда: Самара
Зарегистрирован: Чт 5 окт 2006 9:45
Шаман писал(а):
Не, кривая физика не годится.

Ну "прямая" физика не всегда возможна в реальном времени, так что приходится балансировать. Ассемблер тут тоже может не справиться, по-этому и используют GPU. Удачи.

_________________
Your mind is software. Program it.
Your body is a shell. Change it.
Death is a disease. Cure it.


 Сообщение Пт 24 июн 2011 15:24
Профиль  
#105d99
Аватара пользователя
Сообщения: 15233
Откуда: Москва, сектор бетонных домов
Зарегистрирован: Пн 20 фев 2006 3:56
Ассемблер - не панацея. Нужны эффективные алгоритмы.


 Сообщение Сб 25 июн 2011 0:22
Профиль  
Разработчик идей
Аватара пользователя
Сообщения: 4577
Откуда: Минск, Беларусь
Зарегистрирован: Ср 14 ноя 2007 19:00
То, что они существуют, доказывает Half-Life 2, физика в которой меня более чем устраивает. Пример кривой физики - GTA 3.

З.Ы.: ну и отстой же эти Intel'овские MMX и SSE... Интересно, AMD такой же бессмысленный набор команд в своём 3D Now! имеет?..

_________________
Сообщество креативных механоидов:
aim-fans.ru


 Сообщение Сб 25 июн 2011 0:25
Профиль  
#105d99
Аватара пользователя
Сообщения: 15233
Откуда: Москва, сектор бетонных домов
Зарегистрирован: Пн 20 фев 2006 3:56
Шаман писал(а):
ну и отстой же эти Intel'овские MMX и SSE...

Че с ними не так? Кстати, они не хило так устарели...


 Сообщение Сб 25 июн 2011 1:14
Профиль  
Разработчик идей
Аватара пользователя
Сообщения: 4577
Откуда: Минск, Беларусь
Зарегистрирован: Ср 14 ноя 2007 19:00
Дикий тупизм, что нельзя просто одной командой замоздить xmm-регистр четырьмя одинаковыми float'ами (см. shufps - единственный наиболее близкий способ, но без пол-литра не разберёшься). Другой дикий тупизм - нельзя умножить xmm-регистр на один из float'ов другого регистра - другой регистр нужно сперва замостить одинаковыми float'ами. :god: Результат - умножение вектора на скаляр средствами SSE требует столько же команд, сколько почленное умножение на fpu. :god:
Уж боюсь подумать о смысле скалярного умножения векторов на SSE... И вот задача трансформации вершины: фактически, новая х-координата - это скалярное умножение вектора на первую строку матрицы, y - на вторую, z - на третью... Нет, ничего сложного, если не учитывать того, что заполнение координатами вектора 3-х регистров стоит 6-и команд:
movaps xmm4,v; //Грузим из памяти x,y,z,? в xmm4
movaps xmm5,xmm4; //Копируем в xmm5
movaps xmm6,xmm4; //Копируем в xmm6
shufps xmm4,xmm4,0x00; //x,y,z,? -> x,x,x,x
shufps xmm5,xmm5,0x55; //x,y,z,? -> y,y,y,y
shufps xmm6,xmm6,0xAA; //x,y,z,? -> z,z,z,z
...
Razum писал(а):
Кстати, они не хило так устарели...
А какие альтернативы для CPU? :hm: SSE2,SSE3,SSSE3,SSE4 - всё отстой из одной бочки. Просто добавка более тормозных ещё более многотактовых крокодилов.

Добавлено спустя 8 минут 46 секунд:

Вообще не понятно, для каких задач, по мнению Intel'а, в представленном виде предназанчались команды типа shufps, unpckhps, unpcklps?

_________________
Сообщество креативных механоидов:
aim-fans.ru


 Сообщение Сб 25 июн 2011 2:14
Профиль  
#105d99
Аватара пользователя
Сообщения: 15233
Откуда: Москва, сектор бетонных домов
Зарегистрирован: Пн 20 фев 2006 3:56
Шаман писал(а):
что нельзя просто одной командой замоздить xmm-регистр четырьмя одинаковыми float'ами

Как это нельзя? Можно, насколько я помню.
Шаман писал(а):
Другой дикий тупизм - нельзя умножить xmm-регистр на один из float'ов другого регистра

Ну, это нормально, зачем тебе векторные регистры, если ты один флоат умножать собрался. Кроме того, это сделано не просто так, а так быстрее. Еще не забывай, что память для того, чтобы операции быстро работали, должна быть выровнена по 64 битам.
Шаман писал(а):
Нет, ничего сложного, если не учитывать того, что заполнение координатами вектора 3-х регистров стоит 6-и команд:

Это шляпа. Регистр заполняется одной командой, иначе смысла во всем этом ноль. Память для заполнения, конечно, придется подготавливать традиционными способами, но создатели этих инструкций подразумевали, что вектор будет значительно чаще использоваться как целое, а не по координатам.
Шаман писал(а):
Просто добавка более тормозных ещё более многотактовых крокодилов.

С чего это ты взял, что они многотактовые и тормозные?


 Сообщение Сб 25 июн 2011 3:04
Профиль  
Разработчик идей
Аватара пользователя
Сообщения: 4577
Откуда: Минск, Беларусь
Зарегистрирован: Ср 14 ноя 2007 19:00
Razum писал(а):
Можно, насколько я помню.
Не с SSE или SSE2. Те, сисейки, что позже появились (уже на корах) - позволяют, но я считаю, что щас всё ещё нужно равняться на 4-й пень. Вообще не представляю, что за ограничения перемещения данных такие запутанные в этом Интеле? Просто в ступор эта каракатица иногда вводит...
Организация регистров в стак в мат-спороцессоре - это ж вообще додуматься до такого!.. :shock:
Razum писал(а):
память ... должна быть выровнена по 64 битам
Не, по 128. :tongue:

_________________
Сообщество креативных механоидов:
aim-fans.ru


 Сообщение Сб 25 июн 2011 3:15
Профиль  
#105d99
Аватара пользователя
Сообщения: 15233
Откуда: Москва, сектор бетонных домов
Зарегистрирован: Пн 20 фев 2006 3:56
Да ты упал штоле? Там не четыре регистра, там один 128 битный! Они ДОЛЖЕН грузиться одной командой! Как ЕАХ, например.

Добавлено спустя 37 секунд:

Цитата:
Не, по 128.

Да. Ошибся мальца...


 Сообщение Сб 25 июн 2011 3:20
Профиль  
Разработчик идей
Аватара пользователя
Сообщения: 4577
Откуда: Минск, Беларусь
Зарегистрирован: Ср 14 ноя 2007 19:00
Обана! Проглядел PSHUFD из SSE2! Не, этот форум определённо обладает магическими свойствами - расскажу о проблеме - и она вскоре решается! :teeth:
Ну и маразм... Две команды: shufps (SSE) и pshufd (SSE2) делают одно и то же - перекомбинируют местоположения 4-х байтных кусков в 16-байтных регистрах, но одна считает эти куски float'ами, а другая - int'ами, и в результате вторая избавлена от франкенштейнового маразма первой (когда два нижних 32б куска обязательно берутся из того же регистра, а два верхних - могут из другого). :shock: Ну где здесь логика?! Какая разница, чем заполнены регистры для функции перемещения данных?! Зачем десяток одинаковых функций с разными названиями, каждая из которых иногда отличается своим маразмом? :god: Пойду чайку выпью - так не бесился даже при умножении векторов на fpu... :blabla:

Добавлено спустя 3 минуты 20 секунд:

Razum писал(а):
Да ты упал штоле? Там не четыре регистра, там один 128 битный!
Да ты упал штоле?! Там 8 16-байтных регистров! :blabla: :lol: Плюс ещё 8 в 64-битном режиме у 64битников. :angel:

_________________
Сообщество креативных механоидов:
aim-fans.ru


 Сообщение Сб 25 июн 2011 3:23
Профиль  
#105d99
Аватара пользователя
Сообщения: 15233
Откуда: Москва, сектор бетонных домов
Зарегистрирован: Пн 20 фев 2006 3:56
Поюзай их побольше, я когда чего-то на СИ с ними делал, тоже думал, что бред полный, а потом выяснилось, что некоторые вещи делаются парой команд вместо целой куче именно благодаря маразматическому подходу к созданию.
Вот, например, самый простой пример - почему есть инструкция, вычисляющая число, обратное квадратному корю из аргумента, а не просто квадратному корню? А оказалось, что в большинстве бытовых ситуаций нам нужно делить на корень, а тут сразу раз - и готовое значение, на которое можно умножить, что куда быстре, чем деление, пашет.


 Сообщение Сб 25 июн 2011 3:30
Профиль  
Разработчик идей
Аватара пользователя
Сообщения: 4577
Откуда: Минск, Беларусь
Зарегистрирован: Ср 14 ноя 2007 19:00
З.Ы.: вот оригинальные мануалы на Интеле:
http://www.intel.com/products/processor ... /index.htm
Конкретно мануал по системам команд:
http://www.intel.com/Assets/PDF/manual/253665.pdf
И вот алфавитный справочник по каждой из команд (все системы команд в кучу):
http://www.intel.com/Assets/PDF/manual/325383.pdf
(Гы, 13 метров, 1600+ страниц - а-ля Интел) :smile:

_________________
Сообщество креативных механоидов:
aim-fans.ru


 Сообщение Сб 25 июн 2011 3:31
Профиль  
#105d99
Аватара пользователя
Сообщения: 15233
Откуда: Москва, сектор бетонных домов
Зарегистрирован: Пн 20 фев 2006 3:56
И да, кстати, забей на АСМу, фигач на С лучше. Все равно можно директивой ассемблерные куски вставлять.


 Сообщение Сб 25 июн 2011 3:47
Профиль  
Разработчик идей
Аватара пользователя
Сообщения: 4577
Откуда: Минск, Беларусь
Зарегистрирован: Ср 14 ноя 2007 19:00
Первый тест функции трансформации массива вершин, написанной на ассемблере: 10 млн вершин за 300 мс на моём Intel Core i5 & 3.2 GHz. Надеюсь, это быстрее, чем если просто на С?.. Вот сия первая коряга, если интересно:
Код:
//Transform each vector (float[3]) of source array and write result into
//destination array; transformation is done by multiplying 4x4 matrix (it is
//assumed to be stored column-by-column) onto each of the source vectors with
//resulting vector stored in the corresponding position in the destination array
//It is assumed that w component of all vectors are 1
//The formula is the following:
//v2[0]=m[0]*v1[0]+m[4]*v1[1]+m[8]*v1[2]+m[12];
//v2[1]=m[1]*v1[0]+m[5]*v1[1]+m[9]*v1[2]+m[13]; 
//v2[2]=m[2]*v1[0]+m[6]*v1[1]+m[10]*v1[2]+m[14];
void __declspec(naked) __stdcall
TransformVertices3f(const void *Matrix4x4f_ColumnMajor,
                    const void *ArrayOfVec3f_Source,
                          void *ArrayOfVec3f_Destination,
                    unsigned int VertexCount){
 _asm{
  //Load pointers and counter
  mov eax,[esp+4]; //Move pointer to the matrix into accumulator
  mov esi,[esp+8]; //Move pointer to the source array into esi
  mov edi,[esp+12];//Move pointer to the destination array into edi
  mov ecx,[esp+16];//Move quantity of vectors into counter register
  //Load matrix
  movups xmm0,[eax   ]; //Load first column of matrix into xmm0
  movups xmm1,[eax+16]; //Load second column of matrix into xmm1
  movups xmm2,[eax+32]; //Load third column of matrix into xmm2
  movups xmm3,[eax+48]; //Load forth column of matrix into xmm3
  //Prepare writing mask
  mov eax,0x80808080;   //Set the high bits of 4 bytes of eax to 1's
  movd xmm7,eax;        //Move this mask into xmm7 low dword
  shufps xmm7,xmm7,0xC0;//Distribute mask pattern across first 3 dwords of xmm7
  //Vertex transformation' cycle
  LABEL_CYCLE_START:
   movups xmm4,[esi]; //Load vector {x,y,z,?} into xmm4
   pshufd xmm5,xmm4,0x00;//Load {x,x,x,x} into xmm5
   mulps xmm5,xmm0;     //Multiply first column of matrix by x
   pshufd xmm6,xmm4,0x55;//Load {y,y,y,y} into xmm6
   mulps xmm6,xmm1;     //Multiply second column of matrix by y
   addps xmm5,xmm6;    //Summ intermidiate results
   pshufd xmm6,xmm4,0xAA;//Load {z,z,z,z} into xmm6
   mulps xmm6,xmm2;     //Multiply third column of matrix by z
   addps xmm5,xmm6;    //Summ intermidiate results
   addps xmm5,xmm3;      //Add forth column of matrix to the result
   maskmovdqu xmm5,xmm7; //Write 3 dwords into destination buffer
   add esi,12; //Advance pointer to the source array by 12 bytes
   add edi,12; //Advance pointer to the destination array by 12 bytes
   loop LABEL_CYCLE_START;
  //Exit
  ret 16; //Return from function cleaning up the stack
 }
}
//______________________________________________________________________________

_________________
Сообщество креативных механоидов:
aim-fans.ru


Последний раз редактировалось Shaman Вс 26 июн 2011 2:47, всего редактировалось 1 раз.

 Сообщение Вс 26 июн 2011 3:10
Профиль  
Разработчик идей
Аватара пользователя
Сообщения: 4577
Откуда: Минск, Беларусь
Зарегистрирован: Ср 14 ноя 2007 19:00
Гы! Если заменить
maskmovdqu xmm5,xmm7;
на
movlpd [edi],xmm5;
movhlps xmm5,xmm5;
movss [edi+8],xmm5;
то общее быстродействие увеличивается в 3 раза. Нифигасе, насколько команда записи выборочных байтов (maskmovdqu) тормозная... :shock: Вообще, я читкал, что она хавает порядка 50 процессорных тактов. К слову, команды сложения хмм-регистров - до 2-х, умножения - до 3-х, деления - в районе 17, вычисления корней - около 60, а 1/корень - до 3-х ( :wink: ) мувы - до 6, pshufd - 4...
Да, насколько, оказывается, наивно полагать, что чем меньше команд юзнуто, тем быстрее код будет летать! :no:

_________________
Сообщество креативных механоидов:
aim-fans.ru


Последний раз редактировалось Shaman Вс 26 июн 2011 21:21, всего редактировалось 1 раз.

 Сообщение Вс 26 июн 2011 16:54
Профиль  
Бывший разработчик
Сообщения: 66
Откуда: Самара
Зарегистрирован: Чт 5 окт 2006 9:45
В низкоуровневой оптимизации вообще много "неожиданностей".
Мы раньше боролись за каждый такт, меряя время выполнения участка кода с помощью RDTSC. В некоторых случаях даже простая перестановка команд позволяла выиграть время.

_________________
Your mind is software. Program it.
Your body is a shell. Change it.
Death is a disease. Cure it.


 Сообщение Вс 26 июн 2011 20:53
Профиль  
Разработчик идей
Аватара пользователя
Сообщения: 4577
Откуда: Минск, Беларусь
Зарегистрирован: Ср 14 ноя 2007 19:00
SHW писал(а):
Мы раньше боролись за каждый такт
Когда первых Мехов клепали? А что теперь - при нынешних гигагерцах можно уже не лезть в ассемблерные дебри? Да, ассемблерные оптимизации стОят дикого убийства времени - не выгодно коммерчески, когда сроки выпуска продухта определены. Зато в качестве хобби - довольно занятная головоломка. :smile:

_________________
Сообщество креативных механоидов:
aim-fans.ru


 Сообщение Вс 26 июн 2011 20:58
Профиль  
#105d99
Аватара пользователя
Сообщения: 15233
Откуда: Москва, сектор бетонных домов
Зарегистрирован: Пн 20 фев 2006 3:56
SHW писал(а):
Мы раньше боролись за каждый такт, меряя время выполнения участка кода с помощью RDTSC.

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


 Сообщение Вс 26 июн 2011 21:18
Профиль  
Разработчик идей
Аватара пользователя
Сообщения: 4577
Откуда: Минск, Беларусь
Зарегистрирован: Ср 14 ноя 2007 19:00
SHW писал(а):
перестановка команд позволяла выиграть время
Чтоб компенсировать многотактовость простейших операций, Интел усложнил своих каракатиц (начиная с 4-го пня) возможностью [почти]параллельного выполнения нескольких команд, если результат предыдущей операции не требуется следующей, и при этом команды выполняются разными АЛУ на чипе. Разобраться в этом могут только сами больные на голову Интеловцы, а нормальным людям остаётся лишь переставлять команды местами и методом тыка выбирать лучший вариант, выигрывая пару процентов быстродействия в неделю. Но всё это не имеет смысла, ибо любое обращение к памяти всё равно равносильно нескольким десяткам операций над данными в регистрах - настолько в среднем частота FSB ниже частоты проца. Так что основной упор в оптимизации - уменьшить и рационализировать число обращений к памяти. Когда пишешь на ассемблере, есть возможность загрузить данные в регистры, произвести все операции над ними, а затем слить в память конечный результат. Но если доверится компиллятору С, то скорее всего, данные и промежуточные результаты будут тягаться туда-обратно при каждой операции, в результате чего производительность на 90% будет определяться не частотой проца, а частотой FSB, которая лишь недавно перевалила за 1 ГГц. Хотя и это тоже липа - полный цикл "обращение к памяти" -> "начало получения данных" с незапямятных времён до сих пор занимает (1/33MHz) секунд, но к счастью, чаще всего, всё необходимое обычно валяется в кэши...

_________________
Сообщество креативных механоидов:
aim-fans.ru


 Сообщение Вс 26 июн 2011 21:24
Профиль  
#105d99
Аватара пользователя
Сообщения: 15233
Откуда: Москва, сектор бетонных домов
Зарегистрирован: Пн 20 фев 2006 3:56
Шаман писал(а):
Но если доверится компиллятору С, то скорее всего, данные и промежуточные результаты будут тягаться туда-обратно при каждой операции,

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


 Сообщение Вс 26 июн 2011 21:39
Профиль  
Разработчик идей
Аватара пользователя
Сообщения: 4577
Откуда: Минск, Беларусь
Зарегистрирован: Ср 14 ноя 2007 19:00
Ну я ж на старом Билдере фигачу... :teeth: Но в любом случае, никакой робот не сможет написать код лучше Хомо Сапы. Другое дело, что ему можно доверить перетасовку асм-команд, чтобы параллельное исполнение осуществить.

_________________
Сообщество креативных механоидов:
aim-fans.ru


 Сообщение Вс 26 июн 2011 23:53
Профиль  
#105d99
Аватара пользователя
Сообщения: 15233
Откуда: Москва, сектор бетонных домов
Зарегистрирован: Пн 20 фев 2006 3:56
Цитата:
Но в любом случае, никакой робот не сможет написать код лучше Хомо Сапы

Ну, это в случае очень высокой квалификации чулавека. А если он не особо сечет, то компилер получше справится.


 Сообщение Пн 27 июн 2011 0:03
Профиль  
Переменчивый
Аватара пользователя
Сообщения: 13659
Откуда: Королёв, сборочные цеха
Зарегистрирован: Сб 15 сен 2007 21:53
Кстати, Шаман, а ты сдюжишь впилить свои наработки в готовый движок? Просто идея складывается...

_________________
-=S.A.L.K.E.R.=-
Совпадение? Не думаю.


 Сообщение Пн 27 июн 2011 1:42
Профиль  
Разработчик идей
Аватара пользователя
Сообщения: 4577
Откуда: Минск, Беларусь
Зарегистрирован: Ср 14 ноя 2007 19:00
Шаман не сдюжит только в чужом творчестве разобраться. А своё предоставить - от чистого сердца, да со всеми разъяснениями! :smile:
Вот, к примеру, функциёвина для трансформа вершин (код самой функции будет приведён ниже и будет обновляться по мере оптимизирования):

void TransformVertices3f(
const void *Matrix4x4f_ColumnMajor,
const void *ArrayOfVec3f_Source,
void *ArrayOfVec3f_Destination,
unsigned int VertexCount
);

Берёт четыре аргумента:

первый (Matrix4x4f_ColumnMajor) - указатель на матрицу из 16-ти float'ов (4 на 4), разложенную в строчку по столбцам (первые 4 float'а - элементы первого столбца, следующие 4 - второй столбец и т.д.) - в таком виде, кстати, матрицы любит OpenGL, которую люблю я;

второй аргумент (ArrayOfVec3f_Source) - указатель на исходный массив вершин, каждая из которых состоит из 3-х float'ов (x,y,z);

третий аргумент (ArrayOfVec3f_Destination) - указатель на буффер, куда будут записываться трансформированные вершины; можно дать указатель на исходный массив - тогда вершины после трансформации запишутся туда же;

последний аргумент (VertexCount) - количество вершин в исходном массиве (смотрите только, чтоб в буффере было достаточно места!) - сколько заказано, столько и будет трансформировано и записано в буффер.

Пример использования:

struct vec3{ float x; float y; float z; };
vec3 ArraySrc[10];
vec3 ArrayDst[10];
float Matrix[16]={1,0,0,0, 0,1,0,0, 0,0,1,0, 3,5,7,1};
TransformVertices3f(Matrix, ArraySrc, ArrayDst, 10);

В качестве примера была дана матрица:
1 0 0 3
0 1 0 5
0 0 1 7
0 0 0 1
Трансформирование вершин с её помощью эквивалентно перемещению на вектор {3,5,7}

Вот код самой функции:

Код:
//Transform each vector (float[3]) of source array and write result into
//destination array; transformation is done by multiplying 4x4 matrix (it is
//assumed to be stored column-by-column) onto each of the source vectors with
//resulting vector stored in the corresponding position in the destination array
//It is assumed that w component of all vectors are 1
//The formula is the following:
//Vdst[0]=m[0]*Vsrc[0]+m[4]*Vsrc[1]+m[8]*Vsrc[2]+m[12];
//Vdst[1]=m[1]*Vsrc[0]+m[5]*Vsrc[1]+m[9]*Vsrc[2]+m[13];
//Vdst[2]=m[2]*Vsrc[0]+m[6]*Vsrc[1]+m[10]*Vsrc[2]+m[14];
void __declspec(naked) __stdcall
TransformVertices3f(const void *Matrix4x4f_ColumnMajor,
                    const void *ArrayOfVec3f_Source,
                          void *ArrayOfVec3f_Destination,
                    unsigned int VertexCount){
 _asm{
  //Load pointers and counter
  mov eax,[esp+4]; //Move pointer to the matrix into accumulator
  mov esi,[esp+8]; //Move pointer to the source array into esi
  mov edi,[esp+12];//Move pointer to the destination array into edi
  mov ecx,[esp+16];//Move quantity of vectors into counter register
  //Load matrix
  movups xmm0,[eax   ]; //Load {m[0],m[1],m[2],m[3]} into xmm0
  movups xmm1,[eax+16]; //Load {m[4],m[5],m[6],m[7]} into xmm1
  movups xmm2,[eax+32]; //Load {m[8],m[9],m[10],m[11]} into xmm2
  movups xmm3,[eax+48]; //Load {m[12],m[13],m[14],m[15]} into xmm3
  //Reorder matrix elements (it will help to cheat 1 op per cycle)
  shufps xmm0,xmm0,0x9C; //xmm0: {m[0],m[3],m[1],m[2]}
  shufps xmm1,xmm1,0x9C; //xmm1: {m[4],m[7],m[5],m[6]}
  shufps xmm2,xmm2,0x9C; //xmm2: {m[8],m[11],m[9],m[10]}
  shufps xmm3,xmm3,0x9C; //xmm3: {m[12],m[15],m[13],m[14]}
  //Start of vertex transformation' cycle
  LABEL_CYCLE_START:
   //Load vector {x,y,z,?} into xmm4
   movups xmm4,[esi];
   //Perform transformation
   pshufd xmm5,xmm4,0x55;//Load {y,y,y,y} into xmm5
   mulps xmm5,xmm1;      //Mult {m[4],m[7],m[5],m[6]} and y
   pshufd xmm6,xmm4,0xAA;//Load {z,z,z,z} into xmm6
   addps xmm5,xmm3;      //Add {m12,m15,m13,m14} to y*{m4,m7,m5,m6}
   mulps xmm6,xmm2;      //Mult {m[8],m[11],m[9],m[10]} and z
   shufps xmm4,xmm4,0x00;//Load {x,x,x,x} into xmm4
   addps xmm5,xmm6; //xmm5 = y*{m4,m7,m5,m6}+z*{m8,m11,m9,m10}+{m12,m15,m13,m14}
   mulps xmm4,xmm0;      //Mult {m[0],m[3],m[1],m[2]} and x
   addps xmm4,xmm5;      //xmm4 = {Xt,Wt,Yt,Zt}
   //Write resulting vector: x and then y&z
   movss [edi],xmm4;    //Write x
   movhps [edi+4],xmm4; //Write y and z
   //Advance arrays' pointers to the next vector position
   add esi,12; //Advance pointer to the source array by 12 bytes
   add edi,12; //Advance pointer to the destination array by 12 bytes
   loop LABEL_CYCLE_START; //Jump to the beginning of the cycle
  //Exit
  ret 16; //Return from function cleaning up the stack
 }
}
//______________________________________________________________________________


Добавлено спустя 18 минут 15 секунд:

З.Ы.: судя по тесту, мой Intel Core i5 3.2 ГГц за секунду умудряется 35 раз обработать массив из 10 млн вершин. :twisted:

_________________
Сообщество креативных механоидов:
aim-fans.ru


 Сообщение Вс 3 июл 2011 20:48
Профиль  
Бывший разработчик
Сообщения: 66
Откуда: Самара
Зарегистрирован: Чт 5 окт 2006 9:45
Razum писал(а):
SHW писал(а):
Мы раньше боролись за каждый такт, меряя время выполнения участка кода с помощью RDTSC.

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

Я говорил за себя. На ассемблере мы с другом писали на втором курсе, когда в СкайРивере я еще не работал.

А про обращение к памяти это - да. Самая тормозная операция. В книжках по компьютерной графике писали, что в старых играх типа Дума для оптимизации обращений к памяти и снижения кеш-промахов хранилось две версии текстуры с ориентацией по строкам и по столбцам. Это позволяло увеличить скорость в разы. Дело в том, что память в кеш грузится линейками по 2^n байт. По-этому, последовательное обращение к памяти быстрее полного рандома.

_________________
Your mind is software. Program it.
Your body is a shell. Change it.
Death is a disease. Cure it.


 Сообщение Вс 3 июл 2011 22:30
Профиль  
Разработчик идей
Аватара пользователя
Сообщения: 4577
Откуда: Минск, Беларусь
Зарегистрирован: Ср 14 ноя 2007 19:00
Читаю щас Интеловские мануалы. Выяснил, что обращение к памяти уже не такая тормозная операция - в Intel Core процах, что после четвёртого пня, если данные в каше, то их чтение занимает порядка десятка-двух тактов (а на Core i3,i5,i7 - три такта). Однако это реализовано в виде запроса - пока данные не требуются для операций над ними, последующие независимые от них команды могут исполняться. Интересно также, что запросов может быть съаккумулировано до 8 штук, отсылок на запись - до 4-х.
Касательно параллельного исполнения команд. Теоретический максимум - четыре микрокоманды за такт благодаря нескольким исполнительным АЛУ на чипе, и четырём декодерам (АЛУ, конвертирующим ассемблеровские инструкции в набор микрокоманд для конкретного ядра). Однако среднестатистическая инструкция обычно раскладывается в 2 микрокоманды. Плюс если результат операций одной команды требуется следующей, то параллельность исполнений блокируется. Плюс за такт может быть декодировано не более одной сложной инструкции (которая раскладывается на 2 и более микрокоманд) и три простых (которые переводятся в одну микрокоманду). Поскольку простых инструкций - единицы, то в результате быстродействие всей каракатицы всё так же может быть оценено лейбой с гигагерцами и количеством инструкций в коде. :tongue:
Но так или иначе, если верить моему последнему тесту функции трансформации, мне удалось 40 раз трансформировать 10 млн вершин за 1016 мс в сумме (среднестатистический результ), при этом за цикл обрабатывалось 4 вершины, и асм-инструкций в цикле - 52. Другими словами, эквивалентное быстродействие - 5.2 ГГц замест 3.2 по паспорту. Т.е. в среднем исполнялось по 2 инструкции за такт. Вот код (работает лишь для 4х количества вершин, т.е. функция не дописана):
Код:
void __declspec(naked) __stdcall
TransformVertices3fx4(const void *Matrix4x4f_ColumnMajor,
                      const void *ArrayOfVec3f_Source,
                            void *ArrayOfVec3f_Destination,
                      unsigned int VertexCount){
 _asm{
  //Load pointers and counter
  mov eax,[esp+4]; //Move pointer to the matrix into accumulator
  mov esi,[esp+8]; //Move pointer to the source array into esi
  mov edi,[esp+12];//Move pointer to the destination array into edi
  mov ecx,[esp+16];//Move quantity of vectors into counter register
  //Load matrix
  movups xmm0,[eax   ]; //Load {m[0],m[1],m[2],m[3]} into xmm0
  movups xmm1,[eax+16]; //Load {m[4],m[5],m[6],m[7]} into xmm1
  movups xmm2,[eax+32]; //Load {m[8],m[9],m[10],m[11]} into xmm2
  movups xmm3,[eax+48]; //Load {m[12],m[13],m[14],m[15]} into xmm3

  //Start of vertex transformation' cycle
  LABEL_CYCLE_4_START:
   movups xmm4,[esi];   //Load {x1,y1,z1,x2} into xmm4

     //xmm4:{x1,y1,z1,x2}; xmm5:{-,-,-,-}; xmm6:{-,-,-,-}; xmm7:{-,-,-,-}

   pshufd xmm6,xmm4,00000000b;//Load {x1,x1,x1,x1} into xmm6
    mulps xmm6,xmm0;         //Mult {m0...m3} and x1
     addps xmm6,xmm3;       //xmm6: {m0...m3}*x1 + {m12...m15}
   pshufd xmm7,xmm4,01010101b;//Load {y1,y1,y1,y1} into xmm7
    mulps xmm7,xmm1;         //Mult {m4...m7} and y1
     addps xmm6,xmm7;       //xmm6: {m0...m3}*x1 + {m4...m7}*y1 + {m12...m15}
   pshufd xmm7,xmm4,10101010b;//Load {z1,z1,z1,z1} into xmm7
    mulps xmm7,xmm2;         //Mult {m8...m11} and z1
     addps xmm6,xmm7;       //xmm6: {X1,Y1,Z1,W1}

   movups xmm5,[esi+16];//Load {y2,z2,x3,y3} into xmm5

     //xmm4:{-,-,-,x2}; xmm5:{y2,z2,x3,y3}; xmm6:{X1,Y1,Z1,-}; xmm7:{-,-,-,-}

   shufps xmm4,xmm4,11111111b;//Load {x2,x2,x2,x2} into xmm4
    mulps xmm4,xmm0;         //Mult {m0...m3} and x2
     addps xmm4,xmm3;       //xmm4: {m0...m3}*x2 + {m12...m15}
   pshufd xmm7,xmm5,00000000b;//Load {y2,y2,y2,y2} into xmm7
    mulps xmm7,xmm1;         //Mult {m4...m7} and y2
     addps xmm4,xmm7;       //xmm4: {m0...m3}*x2 + {m4...m7}*y2 + {m12...m15}
   pshufd xmm7,xmm5,01010101b;//Load {z2,z2,z2,z2} into xmm7
    mulps xmm7,xmm2;         //Mult {m8...m11} and z2
     addps xmm7,xmm4;       //xmm7: {X2,Y2,Z2,W2}

     //xmm4:{-,-,-,-}; xmm5:{-,-,x3,y3}; xmm6:{X1,Y1,Z1,-}; xmm7:{X2,Y2,Z2,-}

   movups xmm4,[esi+32];      //Load {z3,x4,y4,z4} into xmm4
   shufps xmm6,xmm6,00100100b;//Get {X1,Y1,Z1, X1} in xmm6
   movss xmm6,xmm7;           //Get {X2,Y1,Z1, X1} in xmm6
   shufps xmm6,xmm6,00100111b;//Get {X1,Y1,Z1, X2} in xmm6
   add esi,48; //Advance pointer to the source array by 48 bytes
   movups [edi],xmm6;         //Store {X1,Y1,Z1,X2} into destination buffer

     //xmm4:{z3,x4,y4,z4}; xmm5:{-,-,x3,y3}; xmm6:{-,-,-,-}; xmm7:{-,Y2,Z2,-}

   pshufd xmm6,xmm5,10101010b;//Load {x3,x3,x3,x3} into xmm6
    mulps xmm6,xmm0;         //Mult {m0...m3} and x3
     addps xmm6,xmm3;       //xmm6: {m0...m3}*x3 + {m12...m15}
   shufps xmm5,xmm5,11111111b;//Load {y3,y3,y3,y3} into xmm5
    mulps xmm5,xmm1;         //Mult {m4...m7} and y3
     addps xmm5,xmm6;       //xmm5: {m0...m3}*x3 + {m4...m7}*y3 + {m12...m15}
   pshufd xmm6,xmm4,00000000b;//Load {z3,z3,z3,z3} into xmm6
    mulps xmm6,xmm2;         //Mult {m8...m11} and z3
     addps xmm6,xmm5;       //Get {X3,Y3,Z3,W3} in xmm6

     //xmm4:{-,x4,y4,z4}; xmm5:{-,-,-,-}; xmm6:{X3,Y3,Z3,-}; xmm7:{-,Y2,Z2,-}

   shufps xmm7,xmm6,01001001b;//Get {Y2,Z2,X3,Y3} in xmm7
   movups [edi+16],xmm7;      //Store {Y2,Z2,X3,Y3} into destination buffer

     //xmm4:{-,x4,y4,z4}; xmm5:{-,-,-,-}; xmm6:{-,-,Z3,-}; xmm7:{-,-,-,-}

   pshufd xmm5,xmm4,01010101b;//Load {x4,x4,x4,x4} into xmm5
    mulps xmm5,xmm0;         //Mult {m0...m3} and x4
     addps xmm5,xmm3;       //xmm5: {m0...m3}*x4 + {m12...m15}
   pshufd xmm7,xmm4,10101010b;//Load {y4,y4,y4,y4} into xmm7
    mulps xmm7,xmm1;         //Mult {m4...m7} and y4
     addps xmm5,xmm7;       //xmm5: {m0...m3}*x4 + {m4...m7}*y4 + {m12...m15}
   shufps xmm4,xmm4,11111111b;//Load {z4,z4,z4,z4} into xmm4
    mulps xmm4,xmm2;         //Mult {m8...m11} and z4
     addps xmm5,xmm4;       //Get {X4,Y4,Z4,W4} in xmm5

     //xmm4:{-,-,-,-}; xmm5:{X4,Y4,Z4,-}; xmm6:{-,-,Z3,-}; xmm7:{-,-,-,-}

   movss xmm6,xmm5;           //Get {X4,-,Z3,-} in xmm6
   shufps xmm6,xmm5,10010010b;//Get {Z3,X4,Y4,Z4} in xmm6
   movups [edi+32],xmm6;      //Store {Z3,X4,Y4,Z4} into destination buffer
   add edi,48; //Advance pointer to the destination array by 48 bytes
   sub ecx,4;
   jnz LABEL_CYCLE_4_START; //Jump to the beginning of the cycle

  //Exit
  LABEL_EXIT:
  ret 16; //Return from function cleaning up the stack
 }
}
//______________________________________________________________________________

_________________
Сообщество креативных механоидов:
aim-fans.ru


 Сообщение Чт 7 июл 2011 14:36
Профиль  
Механоид 3 поколения
Аватара пользователя
Сообщения: 401
Откуда: С планеты-полигон4 дом-сборочный цех
Зарегистрирован: Вс 12 июн 2011 13:07
(сорри если было)Предлагаю сделать так, чтобы глайдеры могли летать(!без использования джамп-концентратора)За счет тяги движков. Вот это было-бы реалистично!

_________________
Расшифровка подписи Микросхемы:
тм'©


 Сообщение Чт 7 июл 2011 15:01
Профиль  
#105d99
Аватара пользователя
Сообщения: 15233
Откуда: Москва, сектор бетонных домов
Зарегистрирован: Пн 20 фев 2006 3:56
Ценное замечание!


 Сообщение Ср 13 июл 2011 4:42
Профиль  
Разработчик идей
Аватара пользователя
Сообщения: 4577
Откуда: Минск, Беларусь
Зарегистрирован: Ср 14 ноя 2007 19:00
Шаман писал(а):
Так что основной упор в оптимизации - уменьшить и рационализировать число обращений к памяти.
Дочитал интеловские мануалы по оптимизации, потестался с недельку, и пришёл к выводу, что фраза моя была слишком опрометчива.
Оказалось, что обращения к памяти нужно просто вставлять как можно раньше до того, как данные потребуются к вычислению. Т.е. интервал между инструкцией загрузки из памяти и первой инструкцией, требующей эти данные рекоммендуется быть порядка 10 команд. Разумеется, это имеет смысл только тогда, когда данные в кэши, что случается, когда подгрузки идут в порядке нарастания адреса и скачки адреса меньше ~64 байт. В Интеловских каракатицах два уровня кэши - L1 и L2. Обе имеют разные скорости (количество тактов, необходимое для получения данных из кэши) и разную вместимость (L1 - пара десятков килобайт, L2 - пара мегабайт). Вот циферки, по которым можно прикинуть, через сколько тактов после mov данные там реально появятся:
Pentium 4
L1: 12, L2: 20;
Core
L1: 3, L2: 15;
Core i7
L1: 4, L2: 10;
К примеру, если производится операция над массивами, как в случае трансформации массива вершин, то все данные тянутся последовательно, а значит, будут торчать в L1 кэши, и в случае Intel Core *** будут доступны за три такта после кушания команды mov* reg,mem.

Добавлено спустя 18 минут 27 секунд:

А, ещё кой-какая интересность касательно параллельного исполнения команд: в Intel Core три АЛУ, т.е. за такт может быть выполнено до трёх инструкций. Плюс в параллель ещё может идти загрузка и запись из/в память(и).
Насчёт перестановки команд местами: в отличие от Пней, Коры уже сами тусуют их - если данные для команды не готовы (предыдущая команда ещё не доковыряла результат), исполняется следующая команда, если это возможно и так далее. Так по 6 команд максимум, вроде, может быть распердячено. Звучит нереально, но судя по тестам перестановки команд местами и нулевому результату, это действительно так. :smile: Однако предварительная загрузка данных задолго до использования действительно играет большую роль - т.е. тусовать имеет смысл лишь мувы из памяти.
Загрузку очень желательно производить раньше записи (в случае, если порядок не важен), иначе может быть подвисон, если части записываемого и загружаемого кусков памяти перекрываются.
Вот ещё кой-какие циферки - сколько тактов какая команда занимает на современных Intel Core:
addps - 3 такта
shufps/pshufd - 1 такт
mulps - 4 такта
Операции над обычными регистрами (сложение, вычитание, логика) - все по 1 такту.

_________________
Сообщество креативных механоидов:
aim-fans.ru


 Сообщение Вт 9 авг 2011 9:23
Профиль  
Механоид 1 поколения
Аватара пользователя
Сообщения: 113
Откуда: Оренбург
Зарегистрирован: Вс 10 апр 2011 18:32
А есть ли отличия в тактах для процессоров amd?

_________________
Успехи науки подарят человеку иллюзорное бытие, яркостью и
неожиданностью своей значительно превышающие бытие реаль-
ное.
Стругацкие.


Последний раз редактировалось attashe Ср 10 авг 2011 8:46, всего редактировалось 1 раз.

Показать сообщения за:  Поле сортировки  
Начать новую тему Ответить на тему  [ Сообщений: 212 ]  На страницу Пред.  1 ... 4, 5, 6, 7, 8  След.

Часовой пояс: UTC + 4 часа


Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей и гости: 44


Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете добавлять вложения

Найти:
Перейти:  
Создано на основе phpBB® Forum Software © phpBB Group
Русская поддержка phpBB