Jump to content

    
__inline__

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

Recommended Posts

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

 

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

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

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

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

Share this post


Link to post
Share on other sites

Тут пишут, что таки есть: https://linux.die.net/man/1/c6x-linux-gnu-gcc

Просто зрительно помню PDF с презентацией, где в недостатках про конвейер говорилось...

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

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.