jcxz 242 11 июля, 2012 Опубликовано 11 июля, 2012 · Жалоба Имеем CC3.3 + C6000 Code Generation Tools v6.1.21. Компилим с таргет = -mv6740. Функция: void DSPF_fltoq31(const float* restrict x, int * restrict y, int n) { int i0, i1, fadcr_store; float t00, t01, t10, t11; _nassert(n > 0); _nassert(n % 2 == 0); fadcr_store = FADCR; //preserve the FADCR value FADCR = _set(FADCR, 9, 10); //set FADCR bits to switch to ROUND TOWARD NEGATIVE INF FADCR = _set(FADCR, 25, 26); //set mode for both L units #pragma MUST_ITERATE(1, , ) for (; (n -= 2) >= 0; ) { t00 = x[n]; t01 = x[n + 1]; t10 = t00 * 0x800000; //multiply by 2^23 for converting to Q23 format (this brings the number in the range -0x800000 to 0x800000) t11 = t01 * 0x800000; //multiply by 2^23 for converting to Q23 format (this brings the number in the range -0x800000 to 0x800000) i0 = _spint(t10); //use intrinsic to convert the number to integer i1 = _spint(t11); //use intrinsic to convert the number to integer *y++ = _sshl(i1, 8); //saturate the number if required *y++ = _sshl(i0, 8); //saturate the number if required } FADCR = fadcr_store; //restore the FADCR value } Как видно - результат выполнения пишется в 'y' с инкрементированием. Но после компиляции смотрим в листинг и видим, что компилятор построил цикл с декрементированием 'y' !!! :crying: (обратите внимание на строки: 263, 268, 269, 277, 278) ... 241 00000090 0ce6 SPLOOP 2 ;16 ; (P) 242 00000092 9de1 || SUB .L2X A3,4,B6 243 00000094 018f0058 || SUB .L1 A3,8,A3 244 245 ;** --------------------------------------------------------------------------* 246 00000098 $C$L4: ; PIPED LOOP KERNEL 247 00000098 $C$DW$L$_DSPF_fltoq31__FPCfPii$8$B: 248 249 00000098 030c5465 LDW .D1T1 *A3--(8),A6 ; |116| (P) <0,0> 250 000000a0 041854e6 || LDW .D2T2 *B6--(8),B8 ; |118| (P) <0,0> 251 252 000000a4 00008000 NOP 5 253 254 000000a8 02990e01 MPYSP .M1 A8,A6,A5 ; |116| (P) <0,6> 255 000000ac 03a11e02 || MPYSP .M2X A8,B8,B7 ; |118| (P) <0,6> 256 257 000000b0 00004000 NOP 3 258 259 000000b4 03940159 SPINT .L1 A5,A7 ; |116| (P) <0,10> 260 000000b8 029c015a || SPINT .L2 B7,B5 ; |118| (P) <0,10> 261 262 000000bc 000b0001 SPMASK L2 263 000000c0 052b005a || SUB .L2 B10,8,B10 264 265 000000c4 0c6e NOP 1 266 267 000000c6 2f66 SPMASK S1,S2 268 000000c8 022901a3 || ADD .S2 8,B10,B4 269 000000cc 022991a0 || ADD .S1X 12,B10,A4 270 271 000000d0 029d08a1 SSHL .S1 A7,8,A5 ; |119| <0,14> 272 000000d4 0ec3 || SSHL .S2 B5,8,B5 ; |118| <0,14> 273 274 .dwpsn file "DSPF_fltoq31.c",line 120,column 0,is_stmt 275 276 000000d6 dc67 SPKERNEL 7,0 277 000000d8 2c54 || STW .D1T1 A5,*A4++(8) ; |119| <0,15> 278 000000da 3c55 || STW .D2T2 B5,*B4++(8) ; |118| <0,15> ... Естественно, что функция не работает - затирает чужую память. Если убрать модификатор 'restrict' для 'x' и 'y' всё начинает работать, но скорость совсем не та - не строится аппаратного цикла SPLOOP насколько я понимаю. Если делать обработку за один проход по одному сэмплу (одна операция *y++) - тоже всё нормально компилится и 'y' в листинге инкрементируется. Кто-нить сталкивался? Или я чего-то недогоняю? Или в CodeTools v6.1.21 'restrict' кривой и, чтобы писать быструю обработку, придётся изучать асм C674x??? Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
andrewn 0 12 июля, 2012 Опубликовано 12 июля, 2012 · Жалоба Но после компиляции смотрим в листинг и видим, что компилятор построил цикл с декрементированием 'y'С икрементированием. Вы точно хотите читать по убывающим адресам и писать по возрастающим? Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
jcxz 242 12 июля, 2012 Опубликовано 12 июля, 2012 · Жалоба С икрементированием. Вы точно хотите читать по убывающим адресам и писать по возрастающим? С декрементированием. Посмотрите внимательнее на указанные строки листинга. Точно хочу - в буфере входных данных данные расположены в обратном порядке, а функции DSPLIB (для которых готовит данные указанная функция) требуют прямого порядка. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Sauris 0 12 июля, 2012 Опубликовано 12 июля, 2012 (изменено) · Жалоба Хотите сказать, что STW .D1T1 A5,*A4++(8) это декремент указателя? Хе-хе... А команды, работающие с B10, находятся под SPMASK-ом, и исполняются один раз на первой итерации, они призваны проинициализировать A4 и B4 с разносом на одно слово. Так что ищите глюки не в компиляторе. а где нить в interrupt threshold например Изменено 12 июля, 2012 пользователем SAURIS GmbH Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
jcxz 242 12 июля, 2012 Опубликовано 12 июля, 2012 · Жалоба А команды, работающие с B10, находятся под SPMASK-ом, и исполняются один раз на первой итерации, они призваны проинициализировать A4 и B4 с разносом на одно слово. Вон оно что... Про SPMASK не читал... я собсно не знаю асма для этого ядра, так - навскидку смотрю листинг B) Здесь асм что-то сложный похоже... в C5502 был гораздо проще - там я всё целиком на нём писал ;) Надо прочитать про SPMASK. Так что ищите глюки не в компиляторе. а где нить в interrupt threshold например Обработчики прерывания здесь в этом ядре не сохраняют состояние аппаратных циклов и на время вызова функций DSPLIB надо запрещать прерывания? Интересно - а как тогда весь остальной код работает? Написал большой объём кода на это ядро (без использования DSPLIB и restrict) и всё прекрасно работало с множеством прерываний. С оптимизацией. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Sauris 0 12 июля, 2012 Опубликовано 12 июля, 2012 (изменено) · Жалоба Обработчики прерывания здесь в этом ядре не сохраняют состояние аппаратных циклов и на время вызова функций DSPLIB надо запрещать прерывания? В этом ядре не все возможно сохранить физически. Например, если стоит команда LDW в регистр A1, и за те 4 такта, что она выполняется, с этим A1 делается еще что-то, пока он еще не получил данное от LDW, то при возникновении прерывания невовремя регистр поймает значение от LDW, а не от того, что там делается конвейеризировано. например: LDW .D1T1 *A3, A1 || ADD .S1 A6,A1,A1 SUB .S1 A8,A1,A1 STW .D1T1 A1,*A4 NOP 2 ; и толкьо тут в A1 придет значение от LDW вот если тут между LDW || ADD и SUB, или между SUB и STW придет прерывание, то значение A1 будет испорчено результатом LDW в результате "нежданного" опустошения конвейера с приостановом исполнения Именно для этого в build options есть interrupt threshold, показывающий, на сколько времени можно при конвейеризации запрещать прерывания. Если он не указан вообще, то считается, что компилятор за это не отвечает, а сам программер следит. Хотя конкретно в том коде, что ты привел, я не вижу таких ситуаций. Он с точки зрения прерываемости вроде как корректен, во всех таких местах все NOP-ами забито.... Возможно, что после указания достаточного interrupt threshold его оптимизатор вообще до неузнаваемости извернет ЗЫ АСМ тут простой, у 5502 сложнее в разы. Однако конвейер у этого проца способен мозг вывихнуть и вынести, если писать конвейеризировно с максимальной параллельностью. Изменено 12 июля, 2012 пользователем SAURIS GmbH Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
vpd 0 12 июля, 2012 Опубликовано 12 июля, 2012 (изменено) · Жалоба Вообще то мне казалось, что у interrupt threshold немного другая логика. Процессор сам умеет неявно запрещать прерывания, если они могут порушить конвейерное исполнение команд. При этом в очень плотно оптимизированных циклах неявный запрет растягивается на выполнение всего цикла. И вот тогда ключик interrupt threshold помогает сказать компилятору, чтобы он не городил такой плотной оптимизации циклов и длинных промежутков с неявно запрещенными прерываниями длиннее N тактов. Справедливости ради стоит сказать, что при расчете этого времени считается, что все команды LDW и STW работают с L1. А для внешней памяти задержки могут быть в разы больше. ЗЫ: Да, и мне обычно хватало линейного ассемблера для выжимания того, чего я хочу. Во всяком случае фидбэк от него еще можно понять, а вот самому юниты и регистры там планировать - это конечно жесть. На практике как раз было проверено, что на C иногда не удается никакими ухищрениями добиться желаемой оптимизации, без особого напряга получаемой на линейном ассемблере. Изменено 12 июля, 2012 пользователем Hoodwin Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Sauris 0 12 июля, 2012 Опубликовано 12 июля, 2012 (изменено) · Жалоба Вообще то мне казалось, что у interrupt threshold немного другая логика. Процессор сам умеет неявно запрещать прерывания, если они могут порушить конвейерное исполнение команд. Ну лично я не имел дело с 64+, зато много писал под 67хх, в т.ч. на асме, в т.ч. с максимальной конвейеризацией и параллельностью исполнения. Базовая система команд у них совместима. Процессор там туп и глуп, ничего он не умеет неявно сам запрещать. Компилятор сам расставляет MVC в CSR согласно указанному ему interrupt threshold, и внутри этих мест, где запретил прерывания, конвейеризирует адски, а между запрещенных от прерывания кусков дает возможность прерваться. Справедливости ради стоит еще отметить, что в 6000-ной базе LDW и STW это команды только D-юнита, L-юнит не может исполнять их вообще и в принципе. А задержки в смысле памяти на длины делэй-слотов не влияют, они останавливают весь конвейер целиком. И если делэй у LDW 4 такта, то хоть на 100 тактов сталл из-за памяти, все равно дэлей 4 пакета инструкций, как ни крути. Мне линейного ассемблера не хватало, да и вообще он нафиг не нужен, если есть С, то же самое по большому счету - код такой же, проблемы те же, а возни с писанием кода больше. Я писал например оптимизированный кодер для аудио, там циклов почти не было, огромное кол-во линейного кода. Вручную разбил его на две половины, и A-стороной процессора обрабатывал первую половимну алгоритма для одного канала, а B-половиной - досчитывал параллеьно с этим вторую половину для предыдущего канала. Такое ни на С, ни на линейном асме, реализовать было не возможно вообще. А вручную расшедулив все операции - удалось где-то за неделю жесткого секса с элементами садо-мазо :) . Изменено 12 июля, 2012 пользователем SAURIS GmbH Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
vpd 0 12 июля, 2012 Опубликовано 12 июля, 2012 · Жалоба Ну, на самом деле неявный запрет прерываний есть. Цитата из SPRU187: 2.12 Interrupt Flexibility Options (--interrupt_threshold Option) On the C6000 architecture, interrupts cannot be taken in the delay slots of a branch. In some instances the compiler can generate code that cannot be interrupted for a potentially large number of cycles. For a given real-time system, there may be a hard limit on how long interrupts can be disabled. The --interrupt_threshold=n option specifies an interrupt threshold value n. The threshold value specifies the maximum number of cycles that the compiler can disable interrupts. If the n is omitted, the compiler assumes that the code is never interrupted. In Code Composer Studio, to specify that the code is never interrupted, select the Interrupt Threshold check box and leave the text box blank in the Build Options dialog box on the Compiler tab, Advanced category. If the --interrupt_threshold=n option is not specified, then interrupts are only explicitly disabled around software pipelined loops. When using the --interrupt_threshold=n option, the compiler analyzes the loop structure and loop counter to determine the maximum number of cycles it takes to execute a loop. If it can determine that the maximum number of cycles is less than the threshold value, the compiler generates the fastest/optimal version of the loop. If the loop is smaller than six cycles, interrupts are not able to occur because the loop is always executing inside the delay slots of a branch. Otherwise, the compiler generates a loop that can be interrupted (and still generate correct results—single assignment code), which in most cases degrades the performance of the loop. The --interrupt_threshold=n option does not comprehend the effects of the memory system. When determining the maximum number of execution cycles for a loop, the compiler does not compute the effects of using slow off-chip memory or memory bank conflicts. It is recommended that a conservative threshold value is used to adjust for the effects of the memory system. Так что насчет LDW STW я не прав, они не влияют на неявный запрет прерываний, только ветвления. А вот MVC у меня вылезали только, когда регистров для циклов не хватало и в ход шли указатели стека, и т.п. Вопрос к jcxz: а в чем проявляется конкретно беда? что за память он затирает? Пробовали ли вы писать более простой код типа: while( n>0 ) { t00 = x[n-1]; t01 = x[n-2]; n -= 2; y[0] = f(t00); y[1] = f(t01); y += 2; } Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Sauris 0 12 июля, 2012 Опубликовано 12 июля, 2012 (изменено) · Жалоба Если у вас оно не ставило MVC и не запрещало прерывания, это говорит о следующих вариантах: 1) Включено что-то, связанное с отладкой, отладочная инфа, или еще что-то, что не позволило компилятору совсем "скрутить цикл в бараний рог" с целью того, чтобы в отладчике можно было наблюдать значения переменных при пошаговом проходе тела цикла. 2) Компилятор не смог определить параметры цикла. То есть они либо не заданы явно, числами, либо не дана MUST_ITERATE, ну и компилятор не стал применять оптимизацию, требующую запрещения прерываний, так как не смог рассчитать, будет ли он в результате соответствовать порогу непрерываемости. 3) цикл построен так, что его вообще невозможно заоптимизировать по максимуму, и решение компилятора оказалось прерываемым само по себе. 4) Самый шоколадный - цикл, соптимизированный по максимуму, сам по себе оказался прерываемым. 5) Не менее шоколадный - оптимизатор уложил все так, что все команды и пролога, и ядра, и эпилога лежат в делэй слотах бранчей, и вся эта система в целом оказалась сама собой не прерываемая. Для примера приведу простое побайтное копирование памяти в цикле, т.е. memcpy без извратов. Оптимизированная версия представляет собой пролог из пяти LDW, ядро из LDW || STW и эпилог из пяти STW, при этом все LDW запараллелены с бранчами и декрементом счетчика цикла. Эта конструкция непрерываемая. Вот если включить отладку, или не задать MUST_ITERATE, что компилятор не сможет понять, что этот цикл в реальности не может быть длинным - то компилятор соберет его неэффективным, но прерываемым. Если же все сойдется в лушую сторону - то цикл будет сделан с LDW || STW в ядре цикла, что каждый такт делается одно LDW и одновременно одно STW. пример MVC - это кусок расчета alaw - тупо из рекомендации G. inline int search( int val, int *table, int size) { int i; for (i = 0; i < size; i++) { if (val <= *table++) return (i); } return (size); } unsigned char linear2alaw( int pcm_val) /* 2's complement (16-bit range) */ { int mask; int seg; unsigned char aval; if (pcm_val >= 0) { mask = 0xD5; /* sign (7th) bit = 1 */ } else { mask = 0x55; /* sign bit = 0 */ pcm_val = -pcm_val - 8; } /* Convert the scaled magnitude to segment number. */ seg = search(pcm_val, seg_end, 8); .................. .................. а вот и MVC в первых рядах ассемблерного кода (причем как-то не очень то и соптимизированного, я бы руками лучше расшедулил, ну или -mhXXX маловато задан был): _linear2alaw: ;** --------------------------------------------------------------------------* ;** 45 ----------------------- if ( (pcm_val = pcm_val) >= 0 ) goto g3; ;** 54 ----------------------- pcm_val = (-8)-pcm_val; ;** 53 ----------------------- mask = 85; ;** 54 ----------------------- goto g4; ;** -----------------------g3: ;** 51 ----------------------- mask = 213; ;** -----------------------g4: ;** 58 ----------------------- val = pcm_val; ;** 58 ----------------------- table = &seg_end[-1]; ;** 34 ----------------------- i = (-1); // [0] ;** 34 ----------------------- L$1 = 8; // [0] ;** ----------------------- #pragma MUST_ITERATE(1, 8, 1) ;** ----------------------- #pragma LOOP_FLAGS(4352u) ;** -----------------------g5: ;** 35 ----------------------- f$1 = (val <= *(++table))-1; // [0] ;** 34 ----------------------- ++i; // [0] ;** 34 ----------------------- if ( (--L$1)&f$1 ) goto g5; // [0] ;** ----------------------- f$1 ? (seg = 8) : (seg = i); $C$DW$8 .dwtag DW_TAG_formal_parameter, DW_AT_name("pcm_val") .dwattr $C$DW$8, DW_AT_TI_symbol_name("_pcm_val") .dwattr $C$DW$8, DW_AT_type(*$C$DW$T$10) .dwattr $C$DW$8, DW_AT_location[DW_OP_reg4] MVKL .S1 _seg_end-4,A8 MVKH .S1 _seg_end-4,A8 LDW .D1T1 *++A8,A5 ; |35| (P) <0,0> ^ MVK .S1 0x55,A0 ; |53| MVC .S2 CSR,B5 CMPLT .L1 A4,0,A1 ; |45| || MV .D1 A4,A9 ; |45| [ A1] SUB .L1 -8,A9,A9 ; |54| || AND .L2 -2,B5,B6 || MVK .S2 0xffffffff,B4 ; |34| || MVK .S1 0x8,A6 ; |34| CMPGT .L1 A9,A5,A4 ; |35| || MV .D1 A9,A7 ; |58| || MVC .S2 B6,CSR ; interrupts off || [!A1] MVK .S1 0xd5,A0 ; |51| ;*----------------------------------------------------------------------------* ;* SOFTWARE PIPELINE INFORMATION ;* ;* Loop source line : 34 ;* Loop opening brace source line : 34 ;* Loop closing brace source line : 37 ;* Known Minimum Trip Count : 1 ;* Known Maximum Trip Count : 8 ;* Known Max Trip Count Factor : 1 ;* Loop Carried Dependency Bound(^) : 1 ;* Unpartitioned Resource Bound : 2 ;* Partitioned Resource Bound(*) : 2 ;* Resource Partition: ;* A-side B-side ;* .L units 1 0 ;* .S units 0 1 ;* .D units 1 0 ;* .M units 0 0 ;* .X cross paths 0 0 ;* .T address paths 1 0 ;* Long read paths 0 0 ;* Long write paths 0 0 ;* Logical ops (.LS) 2 0 (.L or .S unit) ;* Addition ops (.LSD) 2 1 (.L or .S or .D unit) ;* Bound(.L .S .LS) 2* 1 ;* Bound(.L .S .D .LS .LSD) 2* 1 ;* ;* Searching for software pipeline schedule at ... ;* ii = 6 Unsafe schedule for irregular loop ;* ii = 6 Unsafe schedule for irregular loop ;* ii = 6 Unsafe schedule for irregular loop ;* ii = 6 Did not find schedule ;* ii = 7 Unsafe schedule for irregular loop ;* ii = 7 Unsafe schedule for irregular loop ;* ii = 7 Unsafe schedule for irregular loop ;* ii = 7 Did not find schedule ;* ii = 8 Unsafe schedule for irregular loop ;* ii = 8 Unsafe schedule for irregular loop ;* ii = 8 Unsafe schedule for irregular loop ;* ii = 8 Did not find schedule ;* ii = 9 Schedule found with 2 iterations in parallel ;* Done ;* ;* Collapsed epilog stages : 1 ;* Prolog not removed ;* Collapsed prolog stages : 0 ;* ;* Minimum required memory pad : 0 bytes ;* ;* For further improvement on this loop, try option -mh56 ;* ;* Minimum safe trip count : 1 ;*----------------------------------------------------------------------------* $C$L1: ; PIPED LOOP PROLOG ;** --------------------------------------------------------------------------* $C$L2: ; PIPED LOOP KERNEL $C$DW$L$_linear2alaw$3$B: .dwpsn "alaw.c",34,0 XOR .L1 1,A4,A3 ; |35| <0,6> ^ SUB .L1 A6,1,A6 ; |34| <0,7> ^ || SUB .S1 A3,1,A3 ; |35| <0,7> ^ AND .L1 A3,A6,A1 ; |34| <0,8> ^ [ A1] B .S2 $C$L2 ; |34| <0,9> || [ A1] LDW .D1T1 *++A8,A5 ; |35| <1,0> ^ NOP 4 .dwpsn "alaw.c",37,0 ADD .L2 1,B4,B4 ; |34| <0,14> || CMPGT .L1 A7,A5,A4 ; |35| <1,5> ^ $C$DW$L$_linear2alaw$3$E: ;** --------------------------------------------------------------------------* $C$L3: ; PIPED LOOP EPILOG ;** --------------------------------------------------------------------------* ;** 38 ----------------------- if ( seg < 8 ) goto g8; // [0] ;** 63 ----------------------- return (unsigned char)(mask^0x7f); MVC .S2 B5,CSR ; interrupts on || MV .L1 A3,A1 || MVK .S1 127,A3 ; |63| MVK .S2 0x8,B5 || XOR .L1 A3,A0,A3 ; |63| .................. .................. .................. Изменено 12 июля, 2012 пользователем SAURIS GmbH Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
jcxz 242 12 июля, 2012 Опубликовано 12 июля, 2012 · Жалоба Вопрос к jcxz: а в чем проявляется конкретно беда? что за память он затирает? Пробовали ли вы писать более простой код типа: Похоже дело было не в затирании памяти. Сейчас ещё проверил - все работает. Наверно висло не из-за этого, а когда я заглянул в листинг - мне показалось странным такое поведение указателя 'y'. Спасибо SAURIS GmbH разъяснил про SPMASK :rolleyes: Кстати эту функцию я получил из такой: void DSPF_fltoq31(const float* restrict x, int * restrict y, int n) { int fadcr_store; _nassert(n > 0); _nassert(n % 2 == 0); fadcr_store = FADCR; //preserve the FADCR value FADCR = _set(FADCR, 9, 10); //set FADCR bits to switch to ROUND TOWARD NEGATIVE INF FADCR = _set(FADCR, 25, 26); //set mode for both L units #pragma MUST_ITERATE(1, , ) for (x += n - 1; --n >= 0; ) *y++ = _sshl(_spint(*x-- * 0x800000), 8); FADCR = fadcr_store; //restore the FADCR value } Ожидал, что если сделать групповую обработку по 4 сэмпла, это должно ускорить. По листингу вижу, что вроде тактов стало меньше, но почему-то по измерениям загрузка процессора как была 8.8% так и осталась Кстати - попробовал interrupt threshold в опциях проекта (==3000) и вообще попробовал перед функцией поставить: #pragma FUNC_INTERRUPT_THRESHOLD(-1); запретив прерывания на время выполнения функции - на генерируемый компилятором листинг это не оказывает никакого влияния. Похоже всё-таки придётся изучать асм для этого процессора. Насколько я понимаю глядя на получающийся после оптимизатора код - он очень неэффективный, столько NOP-ов... LDW .D1T1 *A3, A1 || ADD .S1 A6,A1,A1 SUB .S1 A8,A1,A1 STW .D1T1 A1,*A4 NOP 2 ; и толкьо тут в A1 придет значение от LDW Вот это забавная штука... Да уж - в 5502 всё было по другому - наставил-бы stall-ов кроме того, что в первой команде был-бы конфликт ресурсов. Значит здесь неучёт времени выполнения (или фазы выполнения?) команд ведёт к неверной работе кода, в то время как в 5502 это вело-бы просто к лишним stall-ам. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
vpd 0 12 июля, 2012 Опубликовано 12 июля, 2012 · Жалоба Ну вот код конечно совсем не похож на то, что должно бы быть. ВОт у меня так выглядит: Исходник на линейном ассемблере: ;===================================================================== ; packing bytes into 16 levels (4 bits per swel) ;===================================================================== _pack16: .cproc dst, src, cnt .reg inp1, inp2 .reg cond11, cond12 .reg cond21, cond22 .reg cond3, phase .reg brightness, threashold, contrast, rescale .reg eff_brght1, above_thr1 .reg eff_brght2, above_thr2 .reg prescaled_x1, scaled_x1 .reg prescaled_x2, scaled_x2 .reg val1, result1, z1, packed1 .reg val2, result2, z2, packed2 .reg trimmed .reg high_nibble, low_nibble .reg mask, out_byte ; prepare working registers ldbu *+DP(_p16_p), threashold ldw *+DP(_p16_b), brightness ldhu *+DP(_p16_m), rescale ldhu *+DP(_p16_c), contrast mvk 0F0h, mask shl cnt, 2, cnt .no_mdep p16_loop: .trip 4, 256, 4 ; first byte to process ldbu *src++, inp1 || zero above_thr1 || zero eff_brght1 cmplt inp1, threashold, cond11 [!cond11] sub inp1, threashold, above_thr1 [!cond11] mv brightness, eff_brght1 mpyu above_thr1, rescale, prescaled_x1 shru prescaled_x1, 8, scaled_x1 mpyu scaled_x1, contrast, val1 add val1, eff_brght1, result1 zero z1 cmpgt result1, 0, cond12 [cond12] sshl result1, 11, z1 extu z1, 1, 28, low_nibble ; second byte to process ldbu *src++, inp2 || zero above_thr2 || zero eff_brght2 cmplt inp2, threashold, cond21 [!cond21] sub inp2, threashold, above_thr2 [!cond21] mv brightness, eff_brght2 mpyu above_thr2, rescale, prescaled_x2 shru prescaled_x2, 8, scaled_x2 mpyu scaled_x2, contrast, val2 add val2, eff_brght2, result2 zero z2 cmpgt result2, 0, cond22 [cond22] sshl result2, 11, z2 extu z2, 1, 28, trimmed shl trimmed, 4, high_nibble or high_nibble, low_nibble, out_byte stb out_byte, *dst++ [cnt] sub .l cnt, 2, cnt [cnt] b p16_loop .return .endproc А вот результат так выглядит (показан только LOOP KERNEL): ;** --------------------------------------------------------------------------* $C$L5: ; PIPED LOOP KERNEL [ cond12] SSHL .S2 result1,0xb,z1 ; |146| <0,17> || [ cond22] SSHL .S1 result2,0xb,z2 ; |161| <0,17> || MPYU .M1 scaled_x2,contrast,val2; |157| <1,12> ^ || [!cond11] SUB .L2X inp1,threashold,above_thr1; |138| <2,7> ^ || CMPLT .L1 inp2,threashold,cond21; |152| <2,7> ^ || LDBU .D1T1 *-src(1),inp2 ; |149| <3,2> EXTU .S2 z1,0x1,0x1c,low_nibble; |147| <0,18> || EXTU .S1 z2,0x1,0x1c,trimmed; |162| <0,18> || ADD .L2 val1,eff_brght1,result1; |143| <1,13> ^ || ZERO .L1 z2 ; |159| <1,13> || ZERO .D2 eff_brght1 ; |134| <2,8> ^ || MPYU .M2X above_thr1,rescale,prescaled_x1; |140| <2,8> ^ || [!cond21] SUB .D1 inp2,threashold,above_thr2; |153| <2,8> ^ SHL .S1 trimmed,0x4,high_nibble; |163| <0,19> || CMPLT .L2 0x0,result1,cond12; |145| <1,14> || ADD .D1 val2,eff_brght2,result2; |158| <1,14> ^ || [!cond11] MV .S2X brightness,eff_brght1; |139| <2,9> ^ || MPYU .M1 above_thr2,rescale,prescaled_x2; |155| <2,9> ^ || ZERO .L1 eff_brght2 ; |149| <2,9> ^ || ZERO .D2 above_thr1 ; |134| <3,4> OR .S1X high_nibble,low_nibble,out_byte; |164| <0,20> || ZERO .D2 z1 ; |144| <1,15> || CMPLT .L1 0x0,result2,cond22; |160| <1,15> || SHRU .S2 prescaled_x1,0x8,scaled_x1; |141| <2,10> ^ || CMPLT .L2X inp1,threashold,cond11; |137| <3,5> ^ || LDBU .D1T2 *src++(2),inp1 ; |134| <4,0> STB .D2T1 out_byte,*dst'++ ; |165| <0,21> || [ cnt'] B .S2 $C$L5 ; |168| <1,16> || [ cnt'] ADD .L2 0xfffffffe,cnt',cnt'; |167| <2,11> || MPYU .M2X scaled_x1,contrast,val1; |142| <2,11> ^ || [!cond21] MV .D1 brightness,eff_brght2; |154| <2,11> ^ || SHRU .S1 prescaled_x2,0x8,scaled_x2; |156| <2,11> ^ || ZERO .L1 above_thr2 ; |149| <3,6> ;** --------------------------------------------------------------------------* $C$L6: ; PIPED LOOP EPILOG ;** --------------------------------------------------------------------------* Но это я еще на C62 развлекался... Кстати, важно именно какой оно дает заголовок об оптимизации. В том цикле вот что было: ;*----------------------------------------------------------------------------* ;* SOFTWARE PIPELINE INFORMATION ;* ;* Loop source line : 134 ;* Loop closing brace source line : 168 ;* Known Minimum Trip Count : 4 ;* Known Maximum Trip Count : 256 ;* Known Max Trip Count Factor : 4 ;* Loop Carried Dependency Bound(^) : 4 ;* Unpartitioned Resource Bound : 5 ;* Partitioned Resource Bound(*) : 5 ;* Resource Partition: ;* A-side B-side ;* .L units 2 3 ;* .S units 4 4 ;* .D units 2 1 ;* .M units 2 2 ;* .X cross paths 1 5* ;* .T address paths 2 1 ;* Long read paths 1 0 ;* Long write paths 0 0 ;* Logical ops (.LS) 1 1 (.L or .S unit) ;* Addition ops (.LSD) 6 5 (.L or .S or .D unit) ;* Bound(.L .S .LS) 4 4 ;* Bound(.L .S .D .LS .LSD) 5* 5* ;* ;* Searching for software pipeline schedule at ... ;* ii = 5 Schedule found with 5 iterations in parallel ;* Done ;* ;* Epilog not entirely removed ;* Collapsed epilog stages : 2 ;* ;* Prolog not removed ;* Collapsed prolog stages : 0 ;* ;* Minimum required memory pad : 4 bytes ;* Minimum threshold value : -mh8 ;* ;* Minimum safe trip count : 3 ;*----------------------------------------------------------------------------* То есть видите, оно делало 5 итераций параллельно. А у Вас такого пока не видно... А ключик -mf включен? Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Sauris 0 12 июля, 2012 Опубликовано 12 июля, 2012 (изменено) · Жалоба попробуйте еще отключить всю отладочную информацию, это развяжет руки оптимизатору. Если она включена хоть в каком-то ее проявлении, то об нормальной оптимизации не может быть и речи. Еще есть полезный ключик "-mhXXX" (в среде - Speculative threshold, или похоже) - разрешение коду читать данные за пределами массива, но при этом ничего с ними не делать. Это тоже нередко дает выигрышь в скорости работы. а насчет сталлов - да, в этом процессоре такого понятия нету (в смысле защиты конвейера). Всем рулит программист. Хочешь тут сталл на 5 тактов - пиши NOP 5. Хочешь конфликт - будет конфликт - два юнита можно заставить записать в один регистр в одно время, и что из этого получится, никто не знает :), но что глюк, это явно. ---------------------------------------- вот Ваша ф-ция из первого поста (только стер работу с FADCR и собрал для C67+, а не 674х, ну и hint: не пытайтесь понять, почему это будет работать, без 0.5 литры, мозги можно повредить :) ) так что ищите что там в build options вашему оптимизатору мешает, конвейеризируется на ура, аж в каждом такте по одному LDW и одному STW ;****************************************************************************** ;* FUNCTION NAME: DSPF_fltoq31 * ;* * ;* Regs Modified : A1,A3,A4,A5,A6,A7,B0,B4,B5,B6 * ;* Regs Used : A1,A3,A4,A5,A6,A7,B0,B3,B4,B5,B6 * ;* Local Frame Size : 0 Args + 0 Auto + 0 Save = 0 byte * ;****************************************************************************** _DSPF_fltoq31: ;** --------------------------------------------------------------------------* ;*----------------------------------------------------------------------------* ;* SOFTWARE PIPELINE INFORMATION ;* ;* Loop source line : 13 ;* Loop opening brace source line : 13 ;* Loop closing brace source line : 22 ;* Known Minimum Trip Count : 1 ;* Known Max Trip Count Factor : 1 ;* Loop Carried Dependency Bound(^) : 0 ;* Unpartitioned Resource Bound : 2 ;* Partitioned Resource Bound(*) : 2 ;* Resource Partition: ;* A-side B-side ;* .L units 2* 0 ;* .S units 1 2* ;* .D units 2* 2* ;* .M units 2* 0 ;* .X cross paths 0 2* ;* .T address paths 2* 2* ;* Long read paths 0 2* ;* Long write paths 0 0 ;* Logical ops (.LS) 0 0 (.L or .S unit) ;* Addition ops (.LSD) 0 1 (.L or .S or .D unit) ;* Bound(.L .S .LS) 2* 1 ;* Bound(.L .S .D .LS .LSD) 2* 2* ;* ;* Searching for software pipeline schedule at ... ;* ii = 2 Schedule found with 9 iterations in parallel ;* Done ;* ;* Collapsed epilog stages : 8 ;* Prolog not entirely removed ;* Collapsed prolog stages : 6 ;* ;* Minimum required memory pad : 64 bytes ;* ;* Minimum safe trip count : 1 ;*----------------------------------------------------------------------------* $C$L1: ; PIPED LOOP PROLOG ZERO .L1 A3 || ADDAW .D1 A4,A6,A7 || B .S1 $C$L2 ; |13| (P) <0,11> MVKH .S1 0x4b000000,A3 || LDW .D1T1 *--A7(8),A4 ; |13| (P) <0,0> SHR .S2X A6,1,B5 ; |13| || LDW .D1T1 *+A7(4),A5 ; |20| (P) <0,1> || B .S1 $C$L2 ; |13| (P) <1,11> .dwpsn file "test.c",line 13,column 0,is_stmt SUB .L2 B4,8,B6 ; |5| || MVK .S1 0x6,A1 ; init prolog collapse predicate || ADD .S2 3,B5,B0 || MV .L1 A3,A6 || LDW .D1T1 *--A7(8),A4 ; |13| (P) <1,0> ;** --------------------------------------------------------------------------* $C$L2: ; PIPED LOOP KERNEL $C$DW$L$_DSPF_fltoq31$3$B: MV .L2 B5,B4 ; |21| <0,15> Split a long life || [!A1] STW .D2T2 B4,*++B6(8) ; |20| <0,15> || SSHL .S2X A5,8,B5 ; |21| <1,13> || [ B0] B .S1 $C$L2 ; |13| <2,11> || SPINT .L1 A3,A5 ; |13| <3,9> || MPYSP .M1 A6,A4,A3 ; |13| <5,5> || LDW .D1T1 *+A7(4),A5 ; |20| <7,1> .dwpsn file "test.c",line 22,column 0,is_stmt [ A1] SUB .S1 A1,1,A1 ; <0,16> || [!A1] STW .D2T2 B4,*+B6(4) ; |21| <0,16> || SSHL .S2X A3,8,B4 ; |20| <1,14> || [ B0] SUB .L2 B0,1,B0 ; |13| <3,10> || SPINT .L1 A4,A3 ; |20| <3,10> || MPYSP .M1 A6,A5,A4 ; |20| <5,6> || LDW .D1T1 *--A7(8),A4 ; |13| <8,0> $C$DW$L$_DSPF_fltoq31$3$E: ;** --------------------------------------------------------------------------* $C$L3: ; PIPED LOOP EPILOG ;** --------------------------------------------------------------------------* $C$DW$12 .dwtag DW_TAG_TI_branch .dwattr $C$DW$12, DW_AT_low_pc(0x00) .dwattr $C$DW$12, DW_AT_TI_return RET .S2 B3 ; |23| .dwpsn file "test.c",line 23,column 1,is_stmt NOP 5 ; BRANCH OCCURS {B3} ; |23| Изменено 12 июля, 2012 пользователем SAURIS GmbH Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
jcxz 242 12 июля, 2012 Опубликовано 12 июля, 2012 · Жалоба вот Ваша ф-ция из первого поста (только стер работу с FADCR и собрал для C67+, а не 674х, ну и hint: не пытайтесь понять, почему это будет работать, без 0.5 литры, Во! Скомпилил как у вас для C67+ и сразу получил 4 такта внутри kernel loop (у вас 2 такта как я понимаю) - очень похоже на Ваш результат. Вернул обратно на C674x - всё вернулось к старому моему результату - похоже не даёт оптимизить тип процессора :crying: Все отладки у меня выключены. Вот что получается: ;*----------------------------------------------------------------------------* ;* SOFTWARE PIPELINE INFORMATION ;* ;* Loop found in file : DSPF_fltoq31.c ;* Loop source line : 112 ;* Loop opening brace source line : 112 ;* Loop closing brace source line : 129 ;* Known Minimum Trip Count : 1 ;* Known Max Trip Count Factor : 1 ;* Loop Carried Dependency Bound(^) : 0 ;* Unpartitioned Resource Bound : 2 ;* Partitioned Resource Bound(*) : 4 ;* Resource Partition: ;* A-side B-side ;* .L units 2 2 ;* .S units 2 2 ;* .D units 2 2 ;* .M units 2 2 ;* .X cross paths 0 2 ;* .T address paths 4* 4* ;* Long read paths 0 0 ;* Long write paths 0 0 ;* Logical ops (.LS) 0 0 (.L or .S unit) ;* Addition ops (.LSD) 0 0 (.L or .S or .D unit) ;* Bound(.L .S .LS) 2 2 ;* Bound(.L .S .D .LS .LSD) 2 2 ;* ;* Searching for software pipeline schedule at ... ;* ii = 4 Schedule found with 5 iterations in parallel ;* Done ;* ;* Loop will be splooped ;* Collapsed epilog stages : 0 ;* Collapsed prolog stages : 0 ;* Minimum required memory pad : 0 bytes ;* ;* Minimum safe trip count : 1 ;*----------------------------------------------------------------------------* То есть видите, оно делало 5 итераций параллельно. А у Вас такого пока не видно... А ключик -mf включен? Ключик этот у меня включён и Partitioned Resource Bound почти ка у вас == 4, а толку нет :( Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Sauris 0 12 июля, 2012 Опубликовано 12 июля, 2012 (изменено) · Жалоба Тут железная фича задействована. Я не работал с 64+, поэтому сразу не знал. SPLOOP-нутый цикл лежит в памяти в развернутом виде, а конвейеризируется и спараллеивается аппаратно средствами этого буфера. Примеры в spru732 это хорошо показывают, Example 7-4, 7-5 например. То есть код при этом выглядит понятным :) и является прерываемым, но работает с той же скоростью у меня на компилере версии 7.3.2 удалось получить в этом цикле 8 iterations in parallel, то есть тоже 2 такта внутри ядра (c разрешенной спекуляцией на загрузках). короче это полезная фича плюснутых C64 Изменено 12 июля, 2012 пользователем SAURIS GmbH Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться