Jump to content

    

C6x GCC - что скажете? Лучше или хуже чем TI CC 8.3.5 ?

Всем здравствуйте!

 

Жажда получить более оптимальный код по быстродействию без вылизывания на Асме, побудил меня поискать альтернативный компилятор 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.

 

 

 

Share this post


Link to post
Share on other sites

Насколько я помню, GCC для C6x не умеет software pipeline. Для сигнальников программная конвейеризация - это основное.

Share this post


Link to post
Share on other sites
30 minutes ago, Grizzly said:

Насколько я помню, GCC для C6x не умеет software pipeline. Для сигнальников программная конвейеризация - это основное.

Огромное спасибо за информацию!

Да, без пайплайнинга пасти там нечего. Фтпоку GCC, продолжаю работать на TI CC

Share this post


Link to post
Share on other sites
1 час назад, __inline__ сказал:

Огромное спасибо за информацию!

К сожалению, не могу никак найти ссылку. Какая-то PDF была. Нашли?

Share this post


Link to post
Share on other sites
1 час назад, __inline__ сказал:

Да, без пайплайнинга пасти там нечего.

А разве по листингу этого не видно?

Share this post


Link to post
Share on other sites
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;
 }
}

 

 

Edited by __inline__

Share this post


Link to post
Share on other sites

Листинг всей функции:     test.asm

 

Как можно ускорить если возможно?

 

Опции компилятора:

1.thumb.jpg.ecb98e7562a5446f96134073215eb963.jpg

2.thumb.jpg.19a6f69c9cb8aae69e15bb5e5b180afe.jpg

3.thumb.jpg.4a7531d24367e09cf2d9a9209801dcd3.jpg

4.thumb.jpg.0849c43acf5bc1ce7763c35694ca6c23.jpg

5.thumb.jpg.4375de3b273b727b06821688acf778d5.jpg

 

 

 

Share this post


Link to post
Share on other sites
4 часа назад, __inline__ сказал:

Это плохо или хорошо? На что надо обращать внимание чтобы код работал очень быстро?

Я уже много лет не работал с этим ядром. Мало что помню. Спросили бы вы меня это лет 8 назад - я бы подробно разъяснил. Где Вы были 8 лет назад?  :wink:

Можете попробовать поизменять код и посмотреть как меняется листинг.

Вот тут мне кажется важная строчка:

4 часа назад, __inline__ сказал:

ii = 4 Schedule found with 3 iterations in parallel

Вижу что у Вас "Register Usage Table" мало регистров используется. Обычно в сложных циклах у меня бывало гораздо больше регистров задействовано. Чем лучше напишешь - тем больше регистров используется в "Register Usage Table".

Share this post


Link to post
Share on other sites

Однако в текущих версиях GCC sploop в каком-то виде присутвует: https://github.com/gcc-mirror/gcc/blob/master/gcc/config/c6x/c6x.c

Есть комментарии в коде, читайте их. Подозреваю, что в 4.8.2 что-то и было, только не всегда оптимизировало.

Впрочем, как посоветовал @jcxz, смотрите листинги.

Share this post


Link to post
Share on other sites
6 hours ago, jcxz said:

Я уже много лет не работал с этим ядром. Мало что помню. Спросили бы вы меня это лет 8 назад - я бы подробно разъяснил. Где Вы были 8 лет назад?  :wink:

8 лет назад я только заканчивал осваивать ARM9, BlackFin и Cyclone-2 FPGA :biggrin:

 

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. Попробую поковырять.

Edited by __inline__

Share this post


Link to post
Share on other sites
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()-запись).

Возможно, что многое из этого оптимизатор уже сделал и не принесёт эффекта.

 

А вообще - нужно конечно ядро изучать, его особенности. Чтобы не вслепую оптимизировать.....

Share this post


Link to post
Share on other sites
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, как повлиять на генерацию кода компилятором?

Edited by __inline__

Share this post


Link to post
Share on other sites

Внял рекомендациям jcxz (за что ему громное спасибо! :sun_bespectacled:).  Удалось увеличить производительность на 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% больше, чем в последних вариантах. На практике прирост скорости подтвердился также.:wink:

 

Особо радует вот это (несколько инструкций впараллель):

 

$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] 

 

Edited by __inline__

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now