repstosw 18 19 декабря, 2019 Опубликовано 19 декабря, 2019 · Жалоба Всем здравствуйте! Жажда получить более оптимальный код по быстродействию без вылизывания на Асме, побудил меня поискать альтернативный компилятор C/C++ для семейства C674x. И вот что удалось найти: https://sourcery.mentor.com/GNUToolchain/release1882 Скачал, поробовал компильнуть. Всё вроде отлично компилится, но есть пара печальных моментов: 1) Компилятор не знает таких функций как : disable_interrupts() и прочие. Хедеры есть? Или прийдётся лезть в мануал и самому на регистрах писать? 2) Не знает регистров: ICR, IER, ISTP и многих других. Опять самому писать глядя в мануал? Или хедеры где-то всё-же есть? Компилирую так: c6x-uclinux-g++ -march=c674x -O3 -fomit-frame-pointer -ffast-math -fno-rtti -fno-exceptions -Dc6745 -DNDEBUG -IFatFs -IAPI -c FatFs/diskio.c Ещё напрягает -eabi в названии тулчейна. Это только для uC-Linux, Bare Metal этим тулчейном не собрать? Какие есть альтернативы C/C++ от TI ? Интересует семейство DSP C67x, конкретнее C674x. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Grizzly 0 19 декабря, 2019 Опубликовано 19 декабря, 2019 · Жалоба Насколько я помню, GCC для C6x не умеет software pipeline. Для сигнальников программная конвейеризация - это основное. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
repstosw 18 19 декабря, 2019 Опубликовано 19 декабря, 2019 · Жалоба 30 minutes ago, Grizzly said: Насколько я помню, GCC для C6x не умеет software pipeline. Для сигнальников программная конвейеризация - это основное. Огромное спасибо за информацию! Да, без пайплайнинга пасти там нечего. Фтпоку GCC, продолжаю работать на TI CC Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Grizzly 0 19 декабря, 2019 Опубликовано 19 декабря, 2019 · Жалоба 1 час назад, __inline__ сказал: Огромное спасибо за информацию! К сожалению, не могу никак найти ссылку. Какая-то PDF была. Нашли? Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
jcxz 241 19 декабря, 2019 Опубликовано 19 декабря, 2019 · Жалоба 1 час назад, __inline__ сказал: Да, без пайплайнинга пасти там нечего. А разве по листингу этого не видно? Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Grizzly 0 19 декабря, 2019 Опубликовано 19 декабря, 2019 · Жалоба Тут пишут, что таки есть: https://linux.die.net/man/1/c6x-linux-gnu-gcc Просто зрительно помню PDF с презентацией, где в недостатках про конвейер говорилось... Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
repstosw 18 19 декабря, 2019 Опубликовано 19 декабря, 2019 (изменено) · Жалоба 50 minutes ago, Grizzly said: К сожалению, не могу никак найти ссылку. Какая-то PDF была. Нашли? Я вам поверил на слово! :) 43 minutes ago, jcxz said: А разве по листингу этого не видно? Недавно стал разбираться с конвеерами. Мощная штука. А листинг я не додумался сгенерить, хотя бы по причине того, что в голову не пришло, что GCC не умеет конвееризировать код. :) Кстати, коль уж про конвееры речь пошла, какие опции полезны в настройках TI CC 8.3.5 ? У меня часто пишет в листинге такое: ;* Loop found in file : ..test.c ;* Loop source line : 10 ;* Loop opening brace source line : 11 ;* Loop closing brace source line : 15 ;* Known Minimum Trip Count : 8 ;* Known Maximum Trip Count : 8 ;* Known Max Trip Count Factor : 8 ;* Loop Carried Dependency Bound(^) : 3 ;* Unpartitioned Resource Bound : 2 ;* Partitioned Resource Bound(*) : 3 ;* Resource Partition: ;* A-side B-side ;* .L units 0 0 ;* .S units 1 2 ;* .D units 2 2 ;* .M units 0 0 ;* .X cross paths 0 0 ;* .T address paths 0 0 ;* Logical ops (.LS) 0 0 (.L or .S unit) ;* Addition ops (.LSD) 5 0 (.L or .S or .D unit) ;* Bound(.L .S .LS) 1 1 ;* Bound(.L .S .D .LS .LSD) 3* 2 ;* ;* Searching for software pipeline schedule at ... ;* ii = 3 Did not find schedule ;* ii = 4 Schedule found with 3 iterations in parallel ;* ;* Register Usage Table: ;* +-----------------------------------------------------------------+ ;* |AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA|BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB| ;* |00000000001111111111222222222233|00000000001111111111222222222233| ;* |01234567890123456789012345678901|01234567890123456789012345678901| ;* |--------------------------------+--------------------------------| ;* 0: | ***** | ** | ;* 1: | **** | ** | ;* 2: | ** * | **** | ;* 3: | ** * | ** | ;* +-----------------------------------------------------------------+ ;* ;* Done ;* ;* Loop will be splooped ;* Collapsed epilog stages : 0 ;* Collapsed prolog stages : 0 ;* Minimum required memory pad : 0 bytes ;* ;* Minimum safe trip count : 1 ;* Min. prof. trip count (est.) : 3 ;* ;* Mem bank conflicts/iter(est.) : { min 0.000, est 0.000, max 0.000 } ;* Mem bank perf. penalty (est.) : 0.0% ;* ;* ;* Total cycles (est.) : 8 + min_trip_cnt * 4 = 40 Это плохо или хорошо? На что надо обращать внимание чтобы код работал очень быстро? Пишу вывод графических примитивов размером 8x8 и 16x16 с учётом поворота (на кратные 90 градусов углы) и отзеркаливание по вертикали-горизонтали и оба. Всего 8 случаев вышло - вариант отражения + поворот Код отрисовки тайла 16x16 с поворотами и зеркалингами: Spoiler void PPU16_Tile16B(u16 SX,u32 SY,s16 DX,s16 DY,u8 M) { switch(M) { case 0: //NORMAL { u64 * __restrict SROMOffset=(u64*)&SROMBuffer[(SY*SPRITE_PITCH)+(SX<<1)]; u64 * __restrict VRAMOffset=(u64*)&VRAMBuffer[((DY*SCREEN_WIDTH)+DX)<<1]; for(u32 y=0;y<16;y++) { for(u32 x=0;x<4;x++)_mem8(VRAMOffset++)=_amem8(SROMOffset++); SROMOffset+=((SPRITE_PITCH>>1)-16)>>2; VRAMOffset+=(SCREEN_WIDTH-16)>>2; } } break; case 1: //MIRROR_HORIZONTAL { u64 * __restrict SROMOffset=(u64*)&SROMBuffer[(SY*SPRITE_PITCH)+(SX<<1)]; u32 * __restrict VRAMOffset=(u32*)&VRAMBuffer[((DY*SCREEN_WIDTH)+(DX+(16-2)))<<1]; //decrease 1 half-word for(u32 y=0;y<16;y++) { for(u32 x=0;x<4;x++) { register u64 p=_amem8(SROMOffset++); _mem4(VRAMOffset--)=_rotl((u32)p,16); _mem4(VRAMOffset--)=_rotl((u32)(p>>32),16); } SROMOffset+=((SPRITE_PITCH>>1)-16)>>2; VRAMOffset+=(SCREEN_WIDTH+16)>>1; } } break; case 2: //MIRROR_VERTICAL { u64 * __restrict SROMOffset=(u64*)&SROMBuffer[(SY*SPRITE_PITCH)+(SX<<1)]; u64 * __restrict VRAMOffset=(u64*)&VRAMBuffer[(((DY+16-1)*SCREEN_WIDTH)+DX)<<1]; for(u32 y=0;y<16;y++) { for(u32 x=0;x<4;x++)_mem8(VRAMOffset++)=_amem8(SROMOffset++); SROMOffset+=((SPRITE_PITCH>>1)-16)>>2; VRAMOffset-=(SCREEN_WIDTH+16)>>2; } } break; case 3: //MIRROR_HORIZONTAL_VERTICAL = ROTATE_180 { u64 * __restrict SROMOffset=(u64*)&SROMBuffer[(SY*SPRITE_PITCH)+(SX<<1)]; u32 * __restrict VRAMOffset=(u32*)&VRAMBuffer[(((DY+(16-1))*SCREEN_WIDTH)+(DX+(16-2)))<<1]; //decrease 1 half-word for(u32 y=0;y<16;y++) { for(u32 x=0;x<4;x++) { register u64 p=_amem8(SROMOffset++); _mem4(VRAMOffset--)=_rotl((u32)p,16); _mem4(VRAMOffset--)=_rotl((u32)(p>>32),16); } SROMOffset+=((SPRITE_PITCH>>1)-16)>>2; VRAMOffset-=(SCREEN_WIDTH-16)>>1; } } break; case 4: //ROTATE_90 { u64 * __restrict SROMOffset=(u64*)&SROMBuffer[(SY*SPRITE_PITCH)+(SX<<1)]; u16 * __restrict VRAMOffset=(u16*)&VRAMBuffer[((DY*SCREEN_WIDTH)+(DX+16-1))<<1]; for(u32 x=0;x<16;x++) { for(u32 y=0;y<4;y++) { register u64 p=_amem8(SROMOffset++); *VRAMOffset=(u16)p; p>>=16; VRAMOffset+=SCREEN_WIDTH; *VRAMOffset=(u16)p; p>>=16; VRAMOffset+=SCREEN_WIDTH; *VRAMOffset=(u16)p; p>>=16; VRAMOffset+=SCREEN_WIDTH; *VRAMOffset=(u16)p; VRAMOffset+=SCREEN_WIDTH; } SROMOffset+=((SPRITE_PITCH>>1)-16)>>2; VRAMOffset-=(SCREEN_WIDTH*16)+1; } } break; { case 5: //MIRROR_HORIZONTAL + ROTATE_90 u64 * __restrict SROMOffset=(u64*)&SROMBuffer[(SY*SPRITE_PITCH)+(SX<<1)]; u16 * __restrict VRAMOffset=(u16*)&VRAMBuffer[(((DY+16-1)*SCREEN_WIDTH)+(DX+16-1))<<1]; for(u32 x=0;x<16;x++) { for(u32 y=0;y<4;y++) { register u64 p=_amem8(SROMOffset++); *VRAMOffset=(u16)p; p>>=16; VRAMOffset-=SCREEN_WIDTH; *VRAMOffset=(u16)p; p>>=16; VRAMOffset-=SCREEN_WIDTH; *VRAMOffset=(u16)p; p>>=16; VRAMOffset-=SCREEN_WIDTH; *VRAMOffset=(u16)p; VRAMOffset-=SCREEN_WIDTH; } SROMOffset+=((SPRITE_PITCH>>1)-16)>>2; VRAMOffset+=(SCREEN_WIDTH*16)-1; } } break; case 6: //MIRROR_VERTICAL + ROTATE_90 { u64 * __restrict SROMOffset=(u64*)&SROMBuffer[(SY*SPRITE_PITCH)+(SX<<1)]; u16 * __restrict VRAMOffset=(u16*)&VRAMBuffer[((DY*SCREEN_WIDTH)+DX)<<1]; for(u32 x=0;x<16;x++) { for(u32 y=0;y<4;y++) { register u64 p=_amem8(SROMOffset++); *VRAMOffset=(u16)p; p>>=16; VRAMOffset+=SCREEN_WIDTH; *VRAMOffset=(u16)p; p>>=16; VRAMOffset+=SCREEN_WIDTH; *VRAMOffset=(u16)p; p>>=16; VRAMOffset+=SCREEN_WIDTH; *VRAMOffset=(u16)p; VRAMOffset+=SCREEN_WIDTH; } SROMOffset+=((SPRITE_PITCH>>1)-16)>>2; VRAMOffset-=(SCREEN_WIDTH*16)-1; } } break; case 7: //ROTATE_270 { u64 * __restrict SROMOffset=(u64*)&SROMBuffer[(SY*SPRITE_PITCH)+(SX<<1)]; u16 * __restrict VRAMOffset=(u16*)&VRAMBuffer[(((DY+16-1)*SCREEN_WIDTH)+DX)<<1]; for(u32 x=0;x<16;x++) { for(u32 y=0;y<4;y++) { register u64 p=_amem8(SROMOffset++); *VRAMOffset=(u16)p; p>>=16; VRAMOffset-=SCREEN_WIDTH; *VRAMOffset=(u16)p; p>>=16; VRAMOffset-=SCREEN_WIDTH; *VRAMOffset=(u16)p; p>>=16; VRAMOffset-=SCREEN_WIDTH; *VRAMOffset=(u16)p; VRAMOffset-=SCREEN_WIDTH; } SROMOffset+=((SPRITE_PITCH>>1)-16)>>2; VRAMOffset+=(SCREEN_WIDTH*16)+1; } } break; } } Изменено 19 декабря, 2019 пользователем repstosw Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
repstosw 18 19 декабря, 2019 Опубликовано 19 декабря, 2019 · Жалоба Листинг всей функции: test.asm Как можно ускорить если возможно? Опции компилятора: Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
jcxz 241 19 декабря, 2019 Опубликовано 19 декабря, 2019 · Жалоба 4 часа назад, __inline__ сказал: Это плохо или хорошо? На что надо обращать внимание чтобы код работал очень быстро? Я уже много лет не работал с этим ядром. Мало что помню. Спросили бы вы меня это лет 8 назад - я бы подробно разъяснил. Где Вы были 8 лет назад? Можете попробовать поизменять код и посмотреть как меняется листинг. Вот тут мне кажется важная строчка: 4 часа назад, __inline__ сказал: ii = 4 Schedule found with 3 iterations in parallel Вижу что у Вас "Register Usage Table" мало регистров используется. Обычно в сложных циклах у меня бывало гораздо больше регистров задействовано. Чем лучше напишешь - тем больше регистров используется в "Register Usage Table". Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Grizzly 0 19 декабря, 2019 Опубликовано 19 декабря, 2019 · Жалоба @__inline__ нашел документ: https://elinux.org/images/4/49/Balister-elc2014.pdf gcc 4.8.2 c674x backend is generally good, but стр. 28-29. perfect, candidate for software pipeline, but gcc doesn’t support SPLOOP Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Grizzly 0 19 декабря, 2019 Опубликовано 19 декабря, 2019 · Жалоба Однако в текущих версиях GCC sploop в каком-то виде присутвует: https://github.com/gcc-mirror/gcc/blob/master/gcc/config/c6x/c6x.c Есть комментарии в коде, читайте их. Подозреваю, что в 4.8.2 что-то и было, только не всегда оптимизировало. Впрочем, как посоветовал @jcxz, смотрите листинги. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
repstosw 18 20 декабря, 2019 Опубликовано 20 декабря, 2019 (изменено) · Жалоба 6 hours ago, jcxz said: Я уже много лет не работал с этим ядром. Мало что помню. Спросили бы вы меня это лет 8 назад - я бы подробно разъяснил. Где Вы были 8 лет назад? 8 лет назад я только заканчивал осваивать ARM9, BlackFin и Cyclone-2 FPGA Quote Можете попробовать поизменять код и посмотреть как меняется листинг. Вот тут мне кажется важная строчка: Вижу что у Вас "Register Usage Table" мало регистров используется. Обычно в сложных циклах у меня бывало гораздо больше регистров задействовано. Чем лучше напишешь - тем больше регистров используется в "Register Usage Table". Знать бы ещё как этот код поизменять. Сделал всё возможное от меня зависящее: память в SROM - это SDRAM закешированная на 152 МГц, там исходные данные, с неё читаю сразу 8 байт(4 пикселя) в регистр. Эти данные выровнены на 64 байта (вместо malloc использую memalign для выделения памяти), координаты образца-источника кратны 8 или 16. Память VRAM - это задний видеобуфер. Это кусочек L2 на частоте ядра 456 МГц - туда начитываю в зависимости от случая с атрибутами(отражение-поворот) - иногда удается сразу 8 байт записать, а иногда по 2 байта только. Здесь конечный адрес не выровнен, так как координаты записи могут быть не кратны 8. L2 - отдана на 64 кБ кеш данных, остальные 192 кБ- VRAM и буфер поворота. Отрисовка дисплея сделана через PRU1, с двойной буферизацией - пока DSP готовит новые данные, PRU1 отрисовывает предыдущие, затем меняем местами буфера. Процессор делает ещё поворот на 90 градусов, так как дисплей ведёт развертку вдоль меньшей стороны, и чтобы избежать тиринга изображения и подёргивания, приходится синхронизироваться по VSYNC. И поэтому буфер приходится записывать в дисплей повёрнутым на 90 градусов. Отрисовка в дисплей 171 FPS (переброс с L2 в LCD) , что очень близко к теоретическому максимуму - 176 FPS. Так как разрешение 400*240 *2 байта, шина 8 бит. Запись одного байта в дисплей EMIFA_CLK/3 = 101.3/3 = 33. 76 МГц 3 hours ago, Grizzly said: Однако в текущих версиях GCC sploop в каком-то виде присутвует: https://github.com/gcc-mirror/gcc/blob/master/gcc/config/c6x/c6x.c Есть комментарии в коде, читайте их. Подозреваю, что в 4.8.2 что-то и было, только не всегда оптимизировало. Впрочем, как посоветовал @jcxz, смотрите листинги. Вот этих файлов как раз и не хватало для GCC. Попробую поковырять. Изменено 20 декабря, 2019 пользователем repstosw Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
jcxz 241 20 декабря, 2019 Опубликовано 20 декабря, 2019 · Жалоба 56 минут назад, __inline__ сказал: Знать бы ещё как этот код поизменять. Сделал всё возможное от меня зависящее: память в SROM - это SDRAM закешированная на 152 МГц, там исходные данные, с неё читаю сразу 8 байт(4 пикселя) в регистр. Эти данные выровнены на 64 байта (вместо malloc использую memalign для выделения памяти), координаты образца-источника кратны 8 или 16. Память VRAM - это задний видеобуфер. Это кусочек L2 на частоте ядра 456 МГц - туда начитываю в зависимости от случая с атрибутами(отражение-поворот) - иногда удается сразу 8 байт записать, а иногда по 2 байта только. Здесь конечный адрес не выровнен, так как координаты записи могут быть не кратны 8. L2 - отдана на 64 кБ кеш данных, остальные 192 кБ- VRAM и буфер поворота. Можно посмотреть например на: ;* Mem bank conflicts/iter(est.) : { min 0.000, est 1.375, max 7.000 } ;* Mem bank perf. penalty (est.) : 9.6% ;* ;* Effective ii : { min 13.00, est 14.38, max 20.00 } из вашего test.asm. Имхо это самое penalty и conflicts если их уменьшить или вообще устранить, то думаю скорость увеличится. Оно должно зависеть от взаиморасположения соседних операций доступа к памяти. "Поизменять" - например читать сразу не по 8 байт, а два раза по 8 байт (две register u64 p=_amem8(SROMOffset++) в две переменных), а потом в теле цикла одновременно писать из них в память. У Вас везде инкрементирование указателей, можно попробовать индексный доступ, а инкрементирование - в конце итерации сразу. Или например вместо одного указателя VRAMOffset вести пару смещённых друг относительно друга на SCREEN_WIDTH и писать поочередно - через 1-й затем 2-й. Здесь например: register u64 p=_amem8(SROMOffset++); _mem4(VRAMOffset--)=_rotl((u32)p,16); _mem4(VRAMOffset--)=_rotl((u32)(p>>32),16); попробовать сделать сначала 2 _rotl() в две переменные, а затем - две записи в память (или объединить в одну _mem8()-запись). Возможно, что многое из этого оптимизатор уже сделал и не принесёт эффекта. А вообще - нужно конечно ядро изучать, его особенности. Чтобы не вслепую оптимизировать..... Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
repstosw 18 20 декабря, 2019 Опубликовано 20 декабря, 2019 (изменено) · Жалоба 3 hours ago, jcxz said: Вас везде инкрементирование указателей, можно попробовать индексный доступ, а инкрементирование - в конце итерации сразу. Или например вместо одного указателя VRAMOffset вести пару смещённых друг относительно друга на SCREEN_WIDTH и писать поочередно - через 1-й затем 2-й. Да, скачал DSPlib, там везде индексный доступ типа такого: float DSPF_sp_dotprod(const float * x, const float * y, const int nx) { int i; float sum = 0; _nassert(nx > 0); _nassert(nx % 8 == 0); _nassert((int)x % 8 == 0); _nassert((int)y % 8 == 0); for(i = 0; i < nx; i++) sum += x[i]*y[i]; return (sum); } Немного рвёт шаблон, так как я предполагал, что индексная адресация более тормозная, чем работа через указатели. Вот наподобие сделал: void PPU16_Tile16B(u16 SX,u32 SY,s16 DX,s16 DY,u8 M) { u64 * __restrict SROMOffset=(u64*)&SROMBuffer[(SY*SPRITE_PITCH)+(SX<<1)]; u64 * __restrict VRAMOffset=(u64*)&VRAMBuffer[((DY*SCREEN_WIDTH)+DX)<<1]; _nassert((int)SROMOffset%32==0); //адрес откуда забираются данные всегда кратен 32 байтам (шаг 16 пикселей) _nassert((int)VRAMOffset%2==0); //адрес куда писать в видеобуфер всегда кратен 2 - точность 1 пиксел for(u32 y=0;y<16;y++) { _mem8(&VRAMOffset[4*y ])=_amem8(&SROMOffset[4*y ]); _mem8(&VRAMOffset[4*y+1])=_amem8(&SROMOffset[4*y+1]); _mem8(&VRAMOffset[4*y+2])=_amem8(&SROMOffset[4*y+2]); _mem8(&VRAMOffset[4*y+3])=_amem8(&SROMOffset[4*y+3]); } } Оно уже 102 цикла делается, вместо 168 что были. Тут не угадаешь, только пробовать и смотреть листинг. На счёт знаний ядра DSP, как повлиять на генерацию кода компилятором? Изменено 20 декабря, 2019 пользователем repstosw Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
repstosw 18 21 декабря, 2019 Опубликовано 21 декабря, 2019 (изменено) · Жалоба Внял рекомендациям jcxz (за что ему громное спасибо! ). Удалось увеличить производительность на 30...50% (в зависимости от случая). Оказывается, индексная адресация лучше ложится на конвеер, чем инкременты с указателями. И однотипные операции лучше дублировать 2 , 4 или 8 раз. Перестановка действий также может влиять. И ещё можно не присваивать переменным промежуточный результат - компилятор это видит и оптимизирует сам. Инкременторы и питчи задефайнены в константы: Quote #define SROM_WIDTH 320 /* тайлы 16x16 спрайтовая таблица шириной 320 пикселов */ #define VRAM_WIDTH 400 /* ширина видеобуфера в пикселах */ SROMBuffer - это закешированная SDRAM на частоте CPU_CLK/3 VRAMBuffer - это L2 на частоте CPU_CLK Вот пример простого отображения: //102 цикла: void PPU16_Tile16B_0(u16 SX,u32 SY,s16 DX,s16 DY,u8 M) { u64 * __restrict SROMOffset=(u64*)&SROMBuffer[((SY*SROM_WIDTH)+SX)<<1]; u64 * __restrict VRAMOffset=(u64*)&VRAMBuffer[((DY*VRAM_WIDTH)+DX)<<1]; _nassert((int)SROMOffset%32==0); _nassert((int)VRAMOffset%2==0); for(int n=0;n<16;n++) { _mem8(&VRAMOffset[0])=_amem8(&SROMOffset[0]); _mem8(&VRAMOffset[2])=_amem8(&SROMOffset[2]); _mem8(&VRAMOffset[1])=_amem8(&SROMOffset[1]); _mem8(&VRAMOffset[3])=_amem8(&SROMOffset[3]); SROMOffset+=SROM_WIDTH>>2; VRAMOffset+=VRAM_WIDTH>>2; } } //96 циклов: ATT void PPU16_Tile16B_0(u16 SX,u32 SY,s16 DX,s16 DY,u8 M) { u64 * __restrict SROMOffset=(u64*)&SROMBuffer[((SY*SROM_WIDTH)+SX)<<1]; u64 * __restrict VRAMOffset=(u64*)&VRAMBuffer[((DY*VRAM_WIDTH)+DX)<<1]; _nassert((int)SROMOffset%32==0); _nassert((int)VRAMOffset%2==0); for(int n=0;n<4;n++) { _mem8(&VRAMOffset[(0*(VRAM_WIDTH>>2))+0])=_amem8(&SROMOffset[(0*(SROM_WIDTH>>2))+0]); _mem8(&VRAMOffset[(0*(VRAM_WIDTH>>2))+1])=_amem8(&SROMOffset[(0*(SROM_WIDTH>>2))+1]); _mem8(&VRAMOffset[(0*(VRAM_WIDTH>>2))+2])=_amem8(&SROMOffset[(0*(SROM_WIDTH>>2))+2]); _mem8(&VRAMOffset[(0*(VRAM_WIDTH>>2))+3])=_amem8(&SROMOffset[(0*(SROM_WIDTH>>2))+3]); _mem8(&VRAMOffset[(1*(VRAM_WIDTH>>2))+0])=_amem8(&SROMOffset[(1*(SROM_WIDTH>>2))+0]); _mem8(&VRAMOffset[(1*(VRAM_WIDTH>>2))+1])=_amem8(&SROMOffset[(1*(SROM_WIDTH>>2))+1]); _mem8(&VRAMOffset[(1*(VRAM_WIDTH>>2))+2])=_amem8(&SROMOffset[(1*(SROM_WIDTH>>2))+2]); _mem8(&VRAMOffset[(1*(VRAM_WIDTH>>2))+3])=_amem8(&SROMOffset[(1*(SROM_WIDTH>>2))+3]); _mem8(&VRAMOffset[(2*(VRAM_WIDTH>>2))+0])=_amem8(&SROMOffset[(2*(SROM_WIDTH>>2))+0]); _mem8(&VRAMOffset[(2*(VRAM_WIDTH>>2))+1])=_amem8(&SROMOffset[(2*(SROM_WIDTH>>2))+1]); _mem8(&VRAMOffset[(2*(VRAM_WIDTH>>2))+2])=_amem8(&SROMOffset[(2*(SROM_WIDTH>>2))+2]); _mem8(&VRAMOffset[(2*(VRAM_WIDTH>>2))+3])=_amem8(&SROMOffset[(2*(SROM_WIDTH>>2))+3]); _mem8(&VRAMOffset[(3*(VRAM_WIDTH>>2))+0])=_amem8(&SROMOffset[(3*(SROM_WIDTH>>2))+0]); _mem8(&VRAMOffset[(3*(VRAM_WIDTH>>2))+1])=_amem8(&SROMOffset[(3*(SROM_WIDTH>>2))+1]); _mem8(&VRAMOffset[(3*(VRAM_WIDTH>>2))+2])=_amem8(&SROMOffset[(3*(SROM_WIDTH>>2))+2]); _mem8(&VRAMOffset[(3*(VRAM_WIDTH>>2))+3])=_amem8(&SROMOffset[(3*(SROM_WIDTH>>2))+3]); SROMOffset+=SROM_WIDTH; VRAMOffset+=VRAM_WIDTH; } } Вариант с поворотом-отражением: //172 цикла //MIRROR_VERTICAL + ROTATE_90 ATT void PPU16_Tile16B_6(u16 SX,u32 SY,s16 DX,s16 DY,u8 M) { u64 * __restrict SROMOffset0=(u64*)&SROMBuffer[((SY*SROM_WIDTH)+SX)<<1]; u64 * __restrict SROMOffset1=SROMOffset0+(SROM_WIDTH>>2); u32 * __restrict VRAMOffset =(u32*)&VRAMBuffer[((DY*VRAM_WIDTH)+DX)<<1]; _nassert((int)SROMOffset0%32==0); _nassert((int)SROMOffset1%32==0); _nassert((int)VRAMOffset%2==0); for(int n=0;n<8;n++) { _mem4(&VRAMOffset[0x0*(VRAM_WIDTH>>1)])=(((u32)((u16) _amem8(&SROMOffset1[0]) ))<<16)|(u32)((u16) _amem8(&SROMOffset0[0]) ); _mem4(&VRAMOffset[0x2*(VRAM_WIDTH>>1)])=(((u32)((u16)(_amem8(&SROMOffset1[0])>>32)))<<16)|(u32)((u16)(_amem8(&SROMOffset0[0])>>32)); _mem4(&VRAMOffset[0x4*(VRAM_WIDTH>>1)])=(((u32)((u16) _amem8(&SROMOffset1[1]) ))<<16)|(u32)((u16) _amem8(&SROMOffset0[1]) ); _mem4(&VRAMOffset[0x6*(VRAM_WIDTH>>1)])=(((u32)((u16)(_amem8(&SROMOffset1[1])>>32)))<<16)|(u32)((u16)(_amem8(&SROMOffset0[1])>>32)); _mem4(&VRAMOffset[0x8*(VRAM_WIDTH>>1)])=(((u32)((u16) _amem8(&SROMOffset1[2]) ))<<16)|(u32)((u16) _amem8(&SROMOffset0[2]) ); _mem4(&VRAMOffset[0xA*(VRAM_WIDTH>>1)])=(((u32)((u16)(_amem8(&SROMOffset1[2])>>32)))<<16)|(u32)((u16)(_amem8(&SROMOffset0[2])>>32)); _mem4(&VRAMOffset[0xC*(VRAM_WIDTH>>1)])=(((u32)((u16) _amem8(&SROMOffset1[3]) ))<<16)|(u32)((u16) _amem8(&SROMOffset0[3]) ); _mem4(&VRAMOffset[0xE*(VRAM_WIDTH>>1)])=(((u32)((u16)(_amem8(&SROMOffset1[3])>>32)))<<16)|(u32)((u16)(_amem8(&SROMOffset0[3])>>32)); _mem4(&VRAMOffset[0x1*(VRAM_WIDTH>>1)])=(((u32)((u16)(_amem8(&SROMOffset1[0])>>16)))<<16)|(u32)((u16)(_amem8(&SROMOffset0[0])>>16)); _mem4(&VRAMOffset[0x3*(VRAM_WIDTH>>1)])=(((u32)((u16)(_amem8(&SROMOffset1[0])>>48)))<<16)|(u32)((u16)(_amem8(&SROMOffset0[0])>>48)); _mem4(&VRAMOffset[0x5*(VRAM_WIDTH>>1)])=(((u32)((u16)(_amem8(&SROMOffset1[1])>>16)))<<16)|(u32)((u16)(_amem8(&SROMOffset0[1])>>16)); _mem4(&VRAMOffset[0x7*(VRAM_WIDTH>>1)])=(((u32)((u16)(_amem8(&SROMOffset1[1])>>48)))<<16)|(u32)((u16)(_amem8(&SROMOffset0[1])>>48)); _mem4(&VRAMOffset[0x9*(VRAM_WIDTH>>1)])=(((u32)((u16)(_amem8(&SROMOffset1[2])>>16)))<<16)|(u32)((u16)(_amem8(&SROMOffset0[2])>>16)); _mem4(&VRAMOffset[0xB*(VRAM_WIDTH>>1)])=(((u32)((u16)(_amem8(&SROMOffset1[2])>>48)))<<16)|(u32)((u16)(_amem8(&SROMOffset0[2])>>48)); _mem4(&VRAMOffset[0xD*(VRAM_WIDTH>>1)])=(((u32)((u16)(_amem8(&SROMOffset1[3])>>16)))<<16)|(u32)((u16)(_amem8(&SROMOffset0[3])>>16)); _mem4(&VRAMOffset[0xF*(VRAM_WIDTH>>1)])=(((u32)((u16)(_amem8(&SROMOffset1[3])>>48)))<<16)|(u32)((u16)(_amem8(&SROMOffset0[3])>>48)); SROMOffset0+=SROM_WIDTH>>1; SROMOffset1+=SROM_WIDTH>>1; VRAMOffset++; } } Горизонтальное отражение: //MIRROR_HORIZONTAL ATT void PPU16_Tile16B_1(u16 SX,u32 SY,s16 DX,s16 DY,u8 M) { /* //102 cycle u64 * __restrict SROMOffset=(u64*)&SROMBuffer[((SY*SROM_WIDTH)+SX)<<1]; u64 * __restrict VRAMOffset=(u64*)&VRAMBuffer[((DY*VRAM_WIDTH)+DX)<<1]; _nassert((int)SROMOffset%32==0); _nassert((int)VRAMOffset%2==0); for(int n=0;n<16;n++) { _mem8(&VRAMOffset[0])=_itoll(_rotl(_loll(_amem8(&SROMOffset[3])),16),_rotl(_hill(_amem8(&SROMOffset[3])),16)); _mem8(&VRAMOffset[1])=_itoll(_rotl(_loll(_amem8(&SROMOffset[2])),16),_rotl(_hill(_amem8(&SROMOffset[2])),16)); _mem8(&VRAMOffset[2])=_itoll(_rotl(_loll(_amem8(&SROMOffset[1])),16),_rotl(_hill(_amem8(&SROMOffset[1])),16)); _mem8(&VRAMOffset[3])=_itoll(_rotl(_loll(_amem8(&SROMOffset[0])),16),_rotl(_hill(_amem8(&SROMOffset[0])),16)); SROMOffset+=SROM_WIDTH>>2; VRAMOffset+=VRAM_WIDTH>>2; } */ //96 cycle u64 * __restrict SROMOffset=(u64*)&SROMBuffer[((SY*SROM_WIDTH)+SX)<<1]; u64 * __restrict VRAMOffset=(u64*)&VRAMBuffer[((DY*VRAM_WIDTH)+DX)<<1]; _nassert((int)SROMOffset%32==0); _nassert((int)VRAMOffset%2==0); for(int n=0;n<4;n++) { _mem8(&VRAMOffset[((VRAM_WIDTH>>2)*0)+0])=_itoll(_rotl(_loll(_amem8(&SROMOffset[((SROM_WIDTH>>2)*0)+3])),16),_rotl(_hill(_amem8(&SROMOffset[((SROM_WIDTH>>2)*0)+3])),16)); _mem8(&VRAMOffset[((VRAM_WIDTH>>2)*0)+2])=_itoll(_rotl(_loll(_amem8(&SROMOffset[((SROM_WIDTH>>2)*0)+1])),16),_rotl(_hill(_amem8(&SROMOffset[((SROM_WIDTH>>2)*0)+1])),16)); _mem8(&VRAMOffset[((VRAM_WIDTH>>2)*1)+0])=_itoll(_rotl(_loll(_amem8(&SROMOffset[((SROM_WIDTH>>2)*1)+3])),16),_rotl(_hill(_amem8(&SROMOffset[((SROM_WIDTH>>2)*1)+3])),16)); _mem8(&VRAMOffset[((VRAM_WIDTH>>2)*1)+2])=_itoll(_rotl(_loll(_amem8(&SROMOffset[((SROM_WIDTH>>2)*1)+1])),16),_rotl(_hill(_amem8(&SROMOffset[((SROM_WIDTH>>2)*1)+1])),16)); _mem8(&VRAMOffset[((VRAM_WIDTH>>2)*2)+0])=_itoll(_rotl(_loll(_amem8(&SROMOffset[((SROM_WIDTH>>2)*2)+3])),16),_rotl(_hill(_amem8(&SROMOffset[((SROM_WIDTH>>2)*2)+3])),16)); _mem8(&VRAMOffset[((VRAM_WIDTH>>2)*2)+2])=_itoll(_rotl(_loll(_amem8(&SROMOffset[((SROM_WIDTH>>2)*2)+1])),16),_rotl(_hill(_amem8(&SROMOffset[((SROM_WIDTH>>2)*2)+1])),16)); _mem8(&VRAMOffset[((VRAM_WIDTH>>2)*3)+0])=_itoll(_rotl(_loll(_amem8(&SROMOffset[((SROM_WIDTH>>2)*3)+3])),16),_rotl(_hill(_amem8(&SROMOffset[((SROM_WIDTH>>2)*3)+3])),16)); _mem8(&VRAMOffset[((VRAM_WIDTH>>2)*3)+2])=_itoll(_rotl(_loll(_amem8(&SROMOffset[((SROM_WIDTH>>2)*3)+1])),16),_rotl(_hill(_amem8(&SROMOffset[((SROM_WIDTH>>2)*3)+1])),16)); _mem8(&VRAMOffset[((VRAM_WIDTH>>2)*0)+1])=_itoll(_rotl(_loll(_amem8(&SROMOffset[((SROM_WIDTH>>2)*0)+2])),16),_rotl(_hill(_amem8(&SROMOffset[((SROM_WIDTH>>2)*0)+2])),16)); _mem8(&VRAMOffset[((VRAM_WIDTH>>2)*0)+3])=_itoll(_rotl(_loll(_amem8(&SROMOffset[((SROM_WIDTH>>2)*0)+0])),16),_rotl(_hill(_amem8(&SROMOffset[((SROM_WIDTH>>2)*0)+0])),16)); _mem8(&VRAMOffset[((VRAM_WIDTH>>2)*1)+1])=_itoll(_rotl(_loll(_amem8(&SROMOffset[((SROM_WIDTH>>2)*1)+2])),16),_rotl(_hill(_amem8(&SROMOffset[((SROM_WIDTH>>2)*1)+2])),16)); _mem8(&VRAMOffset[((VRAM_WIDTH>>2)*1)+3])=_itoll(_rotl(_loll(_amem8(&SROMOffset[((SROM_WIDTH>>2)*1)+0])),16),_rotl(_hill(_amem8(&SROMOffset[((SROM_WIDTH>>2)*1)+0])),16)); _mem8(&VRAMOffset[((VRAM_WIDTH>>2)*2)+1])=_itoll(_rotl(_loll(_amem8(&SROMOffset[((SROM_WIDTH>>2)*2)+2])),16),_rotl(_hill(_amem8(&SROMOffset[((SROM_WIDTH>>2)*2)+2])),16)); _mem8(&VRAMOffset[((VRAM_WIDTH>>2)*2)+3])=_itoll(_rotl(_loll(_amem8(&SROMOffset[((SROM_WIDTH>>2)*2)+0])),16),_rotl(_hill(_amem8(&SROMOffset[((SROM_WIDTH>>2)*2)+0])),16)); _mem8(&VRAMOffset[((VRAM_WIDTH>>2)*3)+1])=_itoll(_rotl(_loll(_amem8(&SROMOffset[((SROM_WIDTH>>2)*3)+2])),16),_rotl(_hill(_amem8(&SROMOffset[((SROM_WIDTH>>2)*3)+2])),16)); _mem8(&VRAMOffset[((VRAM_WIDTH>>2)*3)+3])=_itoll(_rotl(_loll(_amem8(&SROMOffset[((SROM_WIDTH>>2)*3)+0])),16),_rotl(_hill(_amem8(&SROMOffset[((SROM_WIDTH>>2)*3)+0])),16)); SROMOffset+=SROM_WIDTH; VRAMOffset+=VRAM_WIDTH; } } Кто смотрели асм-листинг выше, те заметили, что число циклов было на 30 ... 50% больше, чем в последних вариантах. На практике прирост скорости подтвердился также. Особо радует вот это (несколько инструкций впараллель): $C$L164: ; PIPED LOOP PROLOG ; EXCLUSIVE CPU CYCLES: 4 MVK .S1 8800,A5 ; [A_S674] || MVK .S2 800,B8 ; [B_Sb674] || ADD .L2X A18,B18,B24 ; [B_L674] || ADD .L1X A29,B18,A25 ; [A_L674] || LDDW .D1T1 *A20(0),A19:A18 ; [A_D64P] |363| (P) <0,0> || LDDW .D2T2 *B19(0),B7:B6 ; [B_D64P] |363| (P) <0,0> MVK .S2 9600,B17 ; [B_Sb674] || MVK .S1 1600,A17 ; [A_S674] || ADD .L2 B8,B18,B23 ; [B_L674] || ADD .L1X A5,B18,A26 ; [A_L674] || LDDW .D1T2 *A20(16),B9:B8 ; [A_D64P] |367| (P) <0,1> || LDDW .D2T1 *B19(8),A5:A4 ; [B_D64P] |365| (P) <0,1> MVK .S1 10400,A31 ; [A_S674] || MVKH .S2 0xffff0000,B27 ; [B_Sb674] || ADD .L2 B17,B18,B22 ; [B_L674] || ADD .L1X A17,B18,A21 ; [A_L674] || LDDW .D1T1 *A20(8),A17:A16 ; [A_D64P] |365| (P) <0,2> ADDK .S1 -2,A0 ; [A_S674] || ADD .L2 B6,B18,B20 ; [B_L674] || ADD .S2 B16,B18,B21 ; [B_Sb674] || ADD .D2 B29,B18,B25 ; [B_D64P] || ADD .L1X A31,B18,A27 ; [A_L674] Изменено 21 декабря, 2019 пользователем repstosw Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться