repstosw 18 22 декабря, 2019 Опубликовано 22 декабря, 2019 · Жалоба Добрый день. Использую C674x в связке с внешней SDRAM: организация памяти 32M x 16 (64 МБ), 4 банка памяти, одна страница памяти 1024 байт (10 бит). Кеш L1D, L1P и L2 включены, регионы кеширования MAR для адресов 0xC0000000 ... 0xC3FFFFFF (все 64 МБ) включены в регистрах. Программа выполняется из этой SDRAM, много данных (на несколько МБ), кода - несколько сотен килобайт (от 500 кБ). Активно используется выделения памяти из кучи (через memalign(128,x) ) - указатели выровнены на длину строки кеша L2 (максимальная длина) - 128 байт. Вопрос собственно вот в чём: есть ли смысл привязываться к банкам SDRAM для ускорения доступа к данным? Например - стек - в одном банке, куча - в другом, Zero-Init, UnInit - в третьем, код (.text) - в четвёртом? Или это ничего не даст? Прилагаю файл для линковщика - с расписанными секциями и регионами памяти, который сейчас используется. Программа стартует с самого начала SDRAM 0xC0000000: #define SDRAM_BASE 0xC0000000 #define SDRAM_SIZE 0x04000000 // 64 MB #define RO_SIZE 0x00098000 // <512 kB -c -stack 0x01700000 -heap 0x02800000 MEMORY { RO o = SDRAM_BASE l = RO_SIZE RW o = SDRAM_BASE+RO_SIZE l = SDRAM_SIZE-RO_SIZE } SECTIONS { .text:_c_int00* > SDRAM_BASE .text > RO .const > RO .switch > RO .cinit > RO .rodata > RO .init_array > RO //C++ .sysmem > RW .far > RW .stack > RW .bss > RW .neardata > RW .fardata > RW } Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
jcxz 234 22 декабря, 2019 Опубликовано 22 декабря, 2019 · Жалоба 3 часа назад, __inline__ сказал: Вопрос собственно вот в чём: есть ли смысл привязываться к банкам SDRAM для ускорения доступа к данным? Например - стек - в одном банке, куча - в другом, Zero-Init, UnInit - в третьем, код (.text) - в четвёртом? Имхо: не стек и пр. надо отвязывать, а входные данные и выходные. Т.е. - у вас в цикле обработки данных идёт чтение из одной области и запись в другую. Они должны идти как можно параллельнее. Вот их и нужно развязать. А стек тут не при чём. Он ведь в этих SPLOOP не используется? И зачем тут динамическое выделение памяти вообще? Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
repstosw 18 22 декабря, 2019 Опубликовано 22 декабря, 2019 (изменено) · Жалоба 1 hour ago, jcxz said: Имхо: не стек и пр. надо отвязывать, а входные данные и выходные. Т.е. - у вас в цикле обработки данных идёт чтение из одной области и запись в другую. Входные данные - SDRAM. Выходные - L2 и дисплей. SDRAM - на EMIFB, LCD на EMIFA. Вроде всё отвязано.. Конвеер видео-системы ниже на рисунке. С SDRAM видео-данные записываются в L2 - не весь буфер, а только выборочные элементы - спрайты с SDRAM. До 5 слоёв может быть. Quote И зачем тут динамическое выделение памяти вообще? Загрузка с файлов музыки, звуков, картинок, спрайтов.... Размер неизвестен. С каждым новым уровнем - берутся новые данные: старые данные более неактуальны, идёт освобождение HEAP, занятие новыми объектами. Динамический менеджмент памяти. Однако же, помогло распределение памяти в скрипте ниже: модель памяти удалось сделать far_agregates, вместо far. Near не канает - код сильно большой. Выровнял указатели на 1024 байт (размер страницы SDRAM). Все объекты кроме кучи - толкнул с первую банку SDRAM. Итого: было 93 FPS, стало 110 FPS. Имеется ввиду не чистый FPS видео (он равен 171), а грязный - 1 фрейм + весь остальной процессинг. Нужно 60 FPS с VSYNC (что и обеспечивается), но запас не трёт ..... #define SDRAM_BASE 0xC0000000 #define SDRAM_SIZE 0x02000000 // 32 MB #define RO_SIZE 0x00098000 //< 640 kB -c -stack 0x00100000 -heap 0x00FFFF00 MEMORY { BANK0_RO o = 0xC0000000 l = RO_SIZE BANK0_RW o = 0xC0000000+RO_SIZE l = 0x00800000-RO_SIZE // BANK0_RO+BANK0_RW = 8MB BANK12 o = 0xC0800000 l = 0x01000000 // 16MB // BANK3 o = 0xC1800000 l = 0x00800000 // 8MB not used yet... } SECTIONS { .text:_c_int00* > SDRAM_BASE .text > BANK0_RO .const > BANK0_RO .switch > BANK0_RO .cinit > BANK0_RO .rodata > BANK0_RO .init_array > BANK0_RO //C++ .far > BANK0_RW .bss > BANK0_RW .neardata > BANK0_RW .fardata > BANK0_RW .stack > BANK0_RW //Stack .sysmem > BANK12 //Heap } Изменено 22 декабря, 2019 пользователем repstosw Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
repstosw 18 22 декабря, 2019 Опубликовано 22 декабря, 2019 (изменено) · Жалоба Есть ли другие способы копирования из одной памяти в другую с помощью DSP (именно DSP, не DMA), кроме как _mem8 и _amem8 ? У меня отрисовка тайла 8x8 занимает 34 цикла: void PPU16_Test(u16 SX,u32 SY,s16 DX,s16 DY) { u64 * __restrict SROMOffset=(u64*)&SROMBuffer[((SY*SROM_WIDTH8)+SX)<<1]; u64 * __restrict VRAMOffset=(u64*)&VRAMBuffer[((DY*VRAM_WIDTH )+DX)<<1]; _nassert((int)SROMOffset%16==0); _nassert((int)VRAMOffset%2==0); _mem8(&VRAMOffset[((VRAM_WIDTH>>2)*0)+0])=_amem8(&SROMOffset[((SROM_WIDTH8>>2)*0)+0]); _mem8(&VRAMOffset[((VRAM_WIDTH>>2)*1)+0])=_amem8(&SROMOffset[((SROM_WIDTH8>>2)*1)+0]); _mem8(&VRAMOffset[((VRAM_WIDTH>>2)*2)+0])=_amem8(&SROMOffset[((SROM_WIDTH8>>2)*2)+0]); _mem8(&VRAMOffset[((VRAM_WIDTH>>2)*3)+0])=_amem8(&SROMOffset[((SROM_WIDTH8>>2)*3)+0]); _mem8(&VRAMOffset[((VRAM_WIDTH>>2)*4)+0])=_amem8(&SROMOffset[((SROM_WIDTH8>>2)*4)+0]); _mem8(&VRAMOffset[((VRAM_WIDTH>>2)*5)+0])=_amem8(&SROMOffset[((SROM_WIDTH8>>2)*5)+0]); _mem8(&VRAMOffset[((VRAM_WIDTH>>2)*6)+0])=_amem8(&SROMOffset[((SROM_WIDTH8>>2)*6)+0]); _mem8(&VRAMOffset[((VRAM_WIDTH>>2)*7)+0])=_amem8(&SROMOffset[((SROM_WIDTH8>>2)*7)+0]); _mem8(&VRAMOffset[((VRAM_WIDTH>>2)*0)+1])=_amem8(&SROMOffset[((SROM_WIDTH8>>2)*0)+1]); _mem8(&VRAMOffset[((VRAM_WIDTH>>2)*1)+1])=_amem8(&SROMOffset[((SROM_WIDTH8>>2)*1)+1]); _mem8(&VRAMOffset[((VRAM_WIDTH>>2)*2)+1])=_amem8(&SROMOffset[((SROM_WIDTH8>>2)*2)+1]); _mem8(&VRAMOffset[((VRAM_WIDTH>>2)*3)+1])=_amem8(&SROMOffset[((SROM_WIDTH8>>2)*3)+1]); _mem8(&VRAMOffset[((VRAM_WIDTH>>2)*4)+1])=_amem8(&SROMOffset[((SROM_WIDTH8>>2)*4)+1]); _mem8(&VRAMOffset[((VRAM_WIDTH>>2)*5)+1])=_amem8(&SROMOffset[((SROM_WIDTH8>>2)*5)+1]); _mem8(&VRAMOffset[((VRAM_WIDTH>>2)*6)+1])=_amem8(&SROMOffset[((SROM_WIDTH8>>2)*6)+1]); _mem8(&VRAMOffset[((VRAM_WIDTH>>2)*7)+1])=_amem8(&SROMOffset[((SROM_WIDTH8>>2)*7)+1]); } ASM: ;****************************************************************************** ;* FUNCTION NAME: PPU16_Test * ;* * ;* Regs Modified : A3,A4,A5,A6,A7,A8,A9,A16,A17,A18,B4,B5,B6,B7,B8,B9, * ;* B16,B17,B18,B19,B20,B21,B22,B23,B24,B25 * ;* Regs Used : A3,A4,A5,A6,A7,A8,A9,A16,A17,A18,B3,B4,B5,B6,B7,B8, * ;* B9,DP,B16,B17,B18,B19,B20,B21,B22,B23,B24,B25 * ;* Local Frame Size : 0 Args + 0 Auto + 0 Save = 0 byte * ;****************************************************************************** PPU16_Test: ;** --------------------------------------------------------------------------* ; EXCLUSIVE CPU CYCLES: 34 LDW .D2T1 *+DP(SROMBuffer),A5 ; [B_D64P] |8| SHL .S2 B4,8,B4 ; [B_Sb674] |8| MVK .S2 400,B5 ; [B_Sb674] |9| ADD .L1X A4,B4,A3 ; [A_L674] |8| LDW .D2T2 *+DP(VRAMBuffer),B4 ; [B_D64P] |9| ADDAH .D1 A5,A3,A9 ; [A_D64P] |8| LDDW .D1T1 *A9(0),A5:A4 ; [A_D64P] |17| || MPYSU .M2 B6,B5,B5 ; [B_M674] |9| MVK .S1 512,A3 ; [A_S674] |18| ADD .L1 A3,A9,A8 ; [A_L674] |18| || MVK .S1 1024,A3 ; [A_S674] |19| || ADD .L2X A6,B5,B5 ; [B_L674] |9| || MVK .S2 1536,B6 ; [B_Sb674] |20| ADD .L1 A3,A9,A18 ; [A_L674] |19| || LDDW .D1T1 *A8(0),A7:A6 ; [A_D64P] |18| || ADDAH .D2 B4,B5,B22 ; [B_D64P] |9| || ADD .L2X B6,A9,B7 ; [B_L674] |20| LDDW .D1T1 *A18(0),A17:A16 ; [A_D64P] |19| || MVK .S2 2560,B4 ; [B_Sb674] |22| || LDDW .D2T2 *B7(0),B9:B8 ; [B_D64P] |20| MVK .S1 2048,A3 ; [A_S674] |21| || ADD .L2X B4,A9,B6 ; [B_L674] |22| || MVK .S2 3072,B4 ; [B_Sb674] |23| || STNDW .D2T1 A5:A4,*B22(0) ; [B_D64P] |17| ADD .L1 A3,A9,A3 ; [A_L674] |21| || ADD .L2X B4,A9,B5 ; [B_L674] |23| || LDDW .D2T2 *B6(0),B21:B20 ; [B_D64P] |22| || MVK .S2 100,B16 ; [B_Sb674] |18| LDDW .D1T1 *A3(0),A5:A4 ; [A_D64P] |21| || MVK .S2 3584,B4 ; [B_Sb674] |24| || LDDW .D2T2 *B5(0),B25:B24 ; [B_D64P] |23| ADD .L2X B4,A9,B4 ; [B_L674] |24| || STNDW .D2T1 A7:A6,*+B22[B16] ; [B_D64P] |18| MVK .S2 200,B16 ; [B_Sb674] |19| || LDDW .D2T2 *B4(0),B19:B18 ; [B_D64P] |24| || LDDW .D1T1 *A18(8),A7:A6 ; [A_D64P] |28| STNDW .D2T1 A17:A16,*+B22[B16] ; [B_D64P] |19| || MVK .S2 300,B16 ; [B_Sb674] |20| STNDW .D2T2 B9:B8,*+B22[B16] ; [B_D64P] |20| LDDW .D1T1 *A9(8),A17:A16 ; [A_D64P] |26| || LDDW .D2T2 *B7(8),B17:B16 ; [B_D64P] |29| MVK .S2 400,B7 ; [B_Sb674] |21| || LDDW .D1T1 *A8(8),A9:A8 ; [A_D64P] |27| || LDDW .D2T2 *B6(8),B9:B8 ; [B_D64P] |31| STNDW .D2T1 A5:A4,*+B22[B7] ; [B_D64P] |21| MVK .S2 500,B23 ; [B_Sb674] |22| || LDDW .D1T1 *A3(8),A5:A4 ; [A_D64P] |30| || LDDW .D2T2 *B5(8),B7:B6 ; [B_D64P] |32| STNDW .D2T2 B21:B20,*+B22[B23] ; [B_D64P] |22| || MVK .S2 600,B20 ; [B_Sb674] |23| STNDW .D2T2 B25:B24,*+B22[B20] ; [B_D64P] |23| || MVK .S2 700,B20 ; [B_Sb674] |24| STNDW .D2T2 B19:B18,*+B22[B20] ; [B_D64P] |24| STNDW .D2T1 A17:A16,*B22(8) ; [B_D64P] |26| || MVK .S2 101,B18 ; [B_Sb674] |27| STNDW .D2T1 A9:A8,*+B22[B18] ; [B_D64P] |27| || MVK .S2 201,B18 ; [B_Sb674] |28| STNDW .D2T1 A7:A6,*+B22[B18] ; [B_D64P] |28| || MVK .S2 301,B18 ; [B_Sb674] |29| LDDW .D2T2 *B4(8),B5:B4 ; [B_D64P] |33| || RET .S2 B3 ; [B_Sb674] |34| STNDW .D2T2 B17:B16,*+B22[B18] ; [B_D64P] |29| || MVK .S2 401,B16 ; [B_Sb674] |30| STNDW .D2T1 A5:A4,*+B22[B16] ; [B_D64P] |30| || ADD .L2 1,B23,B16 ; [B_L674] STNDW .D2T2 B9:B8,*+B22[B16] ; [B_D64P] |31| || MVK .S2 601,B8 ; [B_Sb674] |32| STNDW .D2T2 B7:B6,*+B22[B8] ; [B_D64P] |32| || ADD .L2 1,B20,B6 ; [B_L674] STNDW .D2T2 B5:B4,*+B22[B6] ; [B_D64P] |33| ; BRANCH OCCURS {B3} ; [] |34| Не всегда удаётся загрузить все 8 юнитов, как бы не старался (особенно простаивают L и M, активно используются S и D). В доке на ядро написано, что юниты не все операции могут, особенно M - это мультипликатор. В ARM'ах помню, можно было мульти-пересылку сделать наподобие: LDMCopy PUSH {r4-r10} LDMloop LDMIA r1!, {r3 - r10} STMIA r0!, {r3 - r10} SUBS r2, r2, #32 BGE LDMloop POP {r4-r10} или через регистровый файл VFP. Изменено 22 декабря, 2019 пользователем repstosw Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
jcxz 234 22 декабря, 2019 Опубликовано 22 декабря, 2019 · Жалоба 42 минуты назад, __inline__ сказал: Не всегда удаётся загрузить все 8 юнитов, как бы не старался (особенно простаивают L и M, активно используются S и D). В доке на ядро написано, что юниты не все операции могут, особенно M - это мультипликатор. Так это быстрей всего из-за ограничений шин, а не ядра. Ядро C55xx помню допускало максимум до 2-х 16-битных обращений к одному банку ОЗУ за такт ядра (там шина к памяти была 16-битная; ОЗУ разбито на 8 банков). Но всего ядро имело 5 шт. 16-битных шин. Так что в сумме ядро позволяло за такт до 5-ти 16-битных доступов за такт, если исполнительные адреса этих доступов раскидать по банкам ОЗУ не более чем по 2 доступа к банку. Если в какой-то команде осуществлялся 32-битный доступ, то он учитывался как два 16-битных. Из этих 5 шин к ОЗУ две использовались только для чтения команд в буфер предвыборки, так что для данных оставалось 3 шины. Правда ещё надо было учитывать доступы от DMA-контроллера и если с каким-то банком ОЗУ работал и DMA тоже, то количество непрерывно занятых ядром шин к этому банку ОЗУ приходилось ограничивать одной 16-битной шиной. Иначе DMA захлёбывался данными и выставлял ошибку. Если же в каком такте DSP пытался осуществить более 3-х доступов к данным за такт (такого можно достичь только на спаренных командах), то на такую команду налагался штраф (penalty) на 1 такт. Я видел в Ваших листингах тоже какие-то упоминания про штрафы, и подумал, что возможно их природа здесь аналогична. Я сам настолько глубоко в механизмы ядра C674x не погружался (мне с избытком хватило быстродействия после оптимизации си-циклов типа): Спойлер #pragma CODE_SECTION(".textL2") void CopyToXVectFFT(float const * restrict src0, float * restrict dst0, float const * restrict win, float addv, int n) { _nassert((int)dst0 % 8 == 0); _nassert((int)src0 % 8 == 0); _nassert((int)win % 8 == 0); _nassert(n > 0 && n % 4 == 0); long long k, x0, x1; float const * restrict src1 = src0 + n - 2; float * restrict dst1 = dst0 + n * 2 - 4; do { k = _amem8((void *)win); x0 = _amem8((void *)src0); x1 = _amem8((void *)src1); _amem8((void *)dst0) = _itoll(_ftoi(0.0), _ftoi((_itof(_loll(x0)) + addv) * _itof(_loll(k)))); _amem8((void *)(dst0 + 2)) = _itoll(_ftoi(0.0), _ftoi((_itof(_hill(x0)) + addv) * _itof(_hill(k)))); _amem8((void *)dst1) = _itoll(_ftoi(0.0), _ftoi((_itof(_loll(x1)) + addv) * _itof(_hill(k)))); _amem8((void *)(dst1 + 2)) = _itoll(_ftoi(0.0), _ftoi((_itof(_hill(x1)) + addv) * _itof(_loll(k)))); win += 2; src0 += 2; dst0 += 4; src1 -= 2; dst1 -= 4; } while (n -= 4); } так как в моей задаче была работа с realtime-потоком, а не как у Вас - "чем быстрее тем лучше", то я, добившись того что "всё успевает" и + 5-кратного запаса по скорости (загрузка DSP-ядра <=20%) - на этом успокоился и дальше не копал. Поэтому думаю: если хотите дальнейшего ускорения, то нужно погружаться в механизмы работы конвеера, организации шин и т.п. Например (также в C55xx) был 5-ступенчатый (или даже 6-ти?) конвеер, в котором вычисление адреса доступа к памяти осуществлялось на 2-й ступени (и это делал только A-unit), а все логические-/арифметические-/сдвиги и прочие операции выполнялись на последнем или предпоследнем шаге (на 5-м шаге) D-unit-ом. A-unit - имел упрощённые возможности, он мог выполнять только сложения/вычитания и может ещё сдвиг на 1 влево (не помню точно), а также умел операцию кольцевой адресации (аппаратную закрутку адреса по границам кольца). Всё остальное - только на D-unit-е. Поэтому, если скажем в коде встречалось вычисление адреса с использованием не только операций сложения/вычитания, то такие операции выполнялись на D-unit-e на 5-й ступени конвеера. И, естественно, если сразу после такого вычисления стояла команда, использующая полученный адрес для доступа к памяти (адрес ей нужен на 2 ступени), то такая команда штрафовалась на время между 5-ступенью предыдущей команды и 2-ступенью её самой (т.е. - на 3 такта). Время этого штрафа можно было уменьшать, помещая между этими командами другие команды, никак не связанные по операндам и использованным регистрам с этой цепочкой вычислений. Уменьшение происходило на время выполнения этих команд. Именно поэтому я Вам и дал совет относительно индексной адресации. Для C55xx я писал на ассемблере и все эти времена, ступени конвеера (какая команда на какой ступни что делает), количество используемых шин в команде и какие шины конкретно используется - всё это я вручную учитывал так, чтобы построить код по возможности без штрафов или с минимальными штрафами. Штрафы там были ещё по множеству других причин, уже всё и не вспомню за давностью лет. В C674x внутренняя структура мне кажется должна быть во многом похожей на младшего собрата C55xx и оптимизатор си-кода за Вас должен её учитывать. На C55xx си-оптимизатор работал из рук вон плохо (бывало код получался даже в 10 раз более медленным чем написанный вручную на асме), так что там без асма было никуда. А в C674x мне кажется си-оптимизатор работает более-менее. Но как я уже сказал - глубоко это ядро я не копал. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
repstosw 18 23 декабря, 2019 Опубликовано 23 декабря, 2019 · Жалоба Достиг 140 FPS (отрисовка + процессинг). Сделал буфер поворота на весь кадр (ранее было на 4 строки) и в L2. А видеобуфер расположил в SDRAM (номер банки SDRAM для буфера совпадает с номером банки где лежат исходные данные для отрисовки). DSP делает поворот на 90 градусов - оперирует блоками 4x4 пикселя сразу: void Video_Draw(void) { for(int y=0;y<(SCREEN_HEIGHT<<1);y+=8) //копирование видеобуфера с SDRAM с поворотом на 90 градусов в буфер L2 { u64 * __restrict vc=(u64*)(VIDEO_CACHE+y); u64 * __restrict vb0=(u64*)&VRAMBuffer[((SCREEN_WIDTH-4)<<1)+(SCREEN_WIDTH*y)]; u64 * __restrict vb1=vb0+(SCREEN_WIDTH>>2); u64 * __restrict vb2=vb1+(SCREEN_WIDTH>>2); u64 * __restrict vb3=vb2+(SCREEN_WIDTH>>2); for(int x=0;x<(SCREEN_WIDTH>>2);x++) { _amem8(&vc[(3*(SCREEN_HEIGHT>>2))+(SCREEN_HEIGHT*x)])=((u64)((u16) _amem8(&vb0[-x]) ))|((u64)((u16) _amem8(&vb1[-x]) )<<16)|((u64)((u16) _amem8(&vb2[-x]) )<<32)|((u64)((u16) _amem8(&vb3[-x]) )<<48); _amem8(&vc[(2*(SCREEN_HEIGHT>>2))+(SCREEN_HEIGHT*x)])=((u64)((u16)(_amem8(&vb0[-x])>>16)))|((u64)((u16)(_amem8(&vb1[-x])>>16))<<16)|((u64)((u16)(_amem8(&vb2[-x])>>16))<<32)|((u64)((u16)(_amem8(&vb3[-x])>>16))<<48); _amem8(&vc[(1*(SCREEN_HEIGHT>>2))+(SCREEN_HEIGHT*x)])=((u64)((u16)(_amem8(&vb0[-x])>>32)))|((u64)((u16)(_amem8(&vb1[-x])>>32))<<16)|((u64)((u16)(_amem8(&vb2[-x])>>32))<<32)|((u64)((u16)(_amem8(&vb3[-x])>>32))<<48); _amem8(&vc[(0*(SCREEN_HEIGHT>>2))+(SCREEN_HEIGHT*x)])=((u64)((u16)(_amem8(&vb0[-x])>>48)))|((u64)((u16)(_amem8(&vb1[-x])>>48))<<16)|((u64)((u16)(_amem8(&vb2[-x])>>48))<<32)|((u64)((u16)(_amem8(&vb3[-x])>>48))<<48); } } while(PRUSS_VideoBuffer); //PRU закончил рисовать предыдущий кадр? PRUSS_VideoBuffer=VIDEO_CACHE; //даём команду PRU отрисовывать новый кадр из L2 в LCD } В итоге выходит такая же производительность, что и без поворота! И это радует. Если же поворот переложить на PRU или дать ему буфер не в L2, то в SDRAM, то производительность падает в 3 раза по понятным причинам (PRU не имеет кеша данных и доступ к SDRAM идёт медленно. Также у PRU тактовая частота вдвое ниже частоты ядра DSP). А вот рутинную пересылку PRU очень даже отлично переваривает - можно бурстами делать (до 44 байт за 1 раз). Драйвер PRU для отрисовки всего LCD: #define IO volatile #define u32 unsigned int #define LCD 0x60004000 #define Pixel16 memcpy((void*)LCD,(const void*)VideoBuffer,32);VideoBuffer+=32; #define REP150(x) {x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x} #pragma LOCATION(VideoBuffer,0x0000) IO u32 VideoBuffer; void main(void) { VideoBuffer=0; Loop: if(!VideoBuffer)goto Loop; for(register int i=0;i<40;i++)REP150(Pixel16) VideoBuffer=0; goto Loop; } Ассемблер красиво это преобразует в бурсты по 32 байта: ;* --------------------------------------------------------------------------* ;* BEGIN LOOP ||$C$L2|| ;* ;* Loop source line : 22 ;* Loop closing brace source line : 22 ;* Known Minimum Trip Count : 40 ;* Known Maximum Trip Count : 40 ;* Known Max Trip Count Factor : 40 ;* --------------------------------------------------------------------------* ||$C$L2||: LDI32 r23, 0x60004000 ; [] |22| LBBO &r14, r0, 0, 4 ; [] |22| $O$K2 LBBO &r14.b0, r14, 0, 32 ; [] |22| SBBO &r14.b0, r23, 0, 32 ; [] |22| LBBO &r14, r0, 0, 4 ; [] |22| $O$K2 ADD r14, r14, 0x20 ; [] |22| SBBO &r14, r0, 0, 4 ; [] |22| $O$K2 LBBO &r14, r0, 0, 4 ; [] |22| $O$K2 LBBO &r14.b0, r14, 0, 32 ; [] |22| SBBO &r14.b0, r23, 0, 32 ; [] |22| LBBO &r14, r0, 0, 4 ; [] |22| $O$K2 ADD r14, r14, 0x20 ; [] |22| SBBO &r14, r0, 0, 4 ; [] |22| $O$K2 LBBO &r14, r0, 0, 4 ; [] |22| $O$K2 LBBO &r14.b0, r14, 0, 32 ; [] |22| SBBO &r14.b0, r23, 0, 32 ; [] |22| LBBO &r14, r0, 0, 4 ; [] |22| $O$K2 ADD r14, r14, 0x20 ; [] |22| SBBO &r14, r0, 0, 4 ; [] |22| $O$K2 LBBO &r14, r0, 0, 4 ; [] |22| $O$K2 LBBO &r14.b0, r14, 0, 32 ; [] |22| SBBO &r14.b0, r23, 0, 32 ; [] |22| LBBO &r14, r0, 0, 4 ; [] |22| $O$K2 ADD r14, r14, 0x20 ; [] |22| ... SBBO &r14, r0, 0, 4 ; [] |22| $O$K2 LBBO &r14, r0, 0, 4 ; [] |22| $O$K2 LBBO &r14.b0, r14, 0, 32 ; [] |22| SBBO &r14.b0, r23, 0, 32 ; [] |22| LBBO &r14, r0, 0, 4 ; [] |22| $O$K2 ADD r14, r14, 0x20 ; [] |22| SBBO &r14, r0, 0, 4 ; [] |22| $O$K2 LBBO &r14, r0, 0, 4 ; [] |22| $O$K2 LBBO &r14.b0, r14, 0, 32 ; [] |22| SBBO &r14.b0, r23, 0, 32 ; [] |22| LBBO &r14, r0, 0, 4 ; [] |22| $O$K2 ADD r14, r14, 0x20 ; [] |22| SBBO &r14, r0, 0, 4 ; [] |22| $O$K2 SUB r22.b0, r22.b0, 0x01 ; [] |22| $O$L1,$O$L1 JMPNE ||$C$L2||, r22.b0, 0x00 ; [] |22| ... Думаю, на этих результатах можно остановиться: достигнут чистый FPS видеосистемы: 172 FPS, с процессингом : 140 FPS. А нужно было не меньше 60 FPS Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться