У меня есть некоторые функции, предназначенные для обработки 1-256 байт, работающие на встроенной платформе C, где передача байта намного быстрее и компактнее, чем передача int (одна инструкция против трех), что является предпочтительным способом кодирую это:
- Примите int, ранний выход, если ноль, и в противном случае скопируйте младший бит значения счетчика в беззнаковый символ и используйте его в do {} while (- count); цикл (значение параметра 256 будет преобразовано в 0, но будет выполняться 256 раз)
- Примите символ без знака, досрочно завершите работу, если ноль, и имейте специальную версию функции для 256 байтов (эти случаи будут известны заранее).
- Принять неподписанный символ и запустить 256 раз, если он равен нулю.
- Имейте функцию, подобную вышеупомянутой, но вызывайте ее через функции-обертки, которые ведут себя как (0-255) и (только 256).
- Имейте функцию, подобную вышеупомянутой, но вызывайте ее через макросы-обертки, которые ведут себя как (0-255) и (только 256).
Ожидается, что внутренний цикл функции, вероятно, будет представлять 15-30% времени выполнения процессора, когда система занята; иногда он будет использоваться для небольшого количества байтов, а иногда для больших. Микросхема памяти, используемая функцией, имеет накладные расходы на транзакцию, и я предпочитаю, чтобы моя функция доступа к памяти выполняла последовательность start-транзакция / do-вещи / конец транзакции внутри.
Наиболее эффективный код будет состоять в том, чтобы просто принять неподписанный символ и рассматривать значение параметра 0 как запрос на выполнение 256 байтов, полагаясь на вызывающего, чтобы избежать любых случайных попыток прочитать 0 байтов. Это кажется немного опасным. Были ли другие проблемы с такими проблемами во встроенных системах? Как они были обработаны?
EDIT
Платформа представляет собой PIC18Fxx (кодовое пространство 128 КБ; ОЗУ 3.5 КБ), подключенная к флэш-чипу SPI; чтение 256 байтов, когда ожидается меньшее количество, может привести к переполнению буферов чтения в PIC. Запись 256 байтов вместо 0 повредит данные во флеш-чипе. Порт SPI PIC ограничен одним байтом каждые 12 раз, если не проверять состояние занятости; это будет медленнее, если вы это сделаете. Типичная транзакция записи требует отправки 4 байтов в дополнение к полученным данным; Для чтения требуется дополнительный байт для «Оборота SPI» (самый быстрый способ получить доступ к порту SPI - это прочитать последний байт непосредственно перед отправкой следующего).
Компилятор HiTech PICC-18std.
Мне обычно нравятся компиляторы HiTech PICC-16; HiTech, по-видимому, отводит свою энергию от продукта PICC-18std к своей линии PICC-18pro, которая имеет даже более медленное время компиляции, кажется, требует использования 3-байтовых «константных» указателей, а не двухбайтовых указателей, и имеет свои собственные представления о распределении памяти. Возможно, мне стоит больше взглянуть на PICC-18pro, но когда я попытался скомпилировать свой проект на eval-версии PICC-18pro, это не сработало, и я не понял точно, почему - возможно, что-то с изменяемой компоновкой не соответствует мои процедуры asm - я просто продолжал использовать PICC-18std.
Кстати, я только что обнаружил, что PICC-18 особенно любит do {} while (- bytevar); и особенно не любит do {} while (- intvar); Интересно, что происходит с «разумом» компилятора, когда он генерирует последний?
do
{
local_test++;
--lpw;
} while(lpw);
2533 ;newflashpic.c: 792: do
2534 ;newflashpic.c: 793: {
2535 0144A8 2AD9 incf fsr2l,f,c
2536 ;newflashpic.c: 795: } while(--lpw);
2537 0144AA 0E00 movlw low ?_var_test
2538 0144AC 6EE9 movwf fsr0l,c
2539 0144AE 0E01 movlw high ?_var_test
2540 0144B0 6EEA movwf fsr0h,c
2541 0144B2 06EE decf postinc0,f,c
2542 0144B4 0E00 movlw 0
2543 0144B6 5AED subwfb postdec0,f,c
2544 0144B8 50EE movf postinc0,w,c
2545 0144BA 10ED iorwf postdec0,w,c
2546 0144BC E1F5 bnz l242
Компилятор загружает указатель на переменную, даже не используя инструкцию LFSR (которая заняла бы два слова), а комбинацию MOVLW / MOVWF (занимающую четыре). Затем он использует этот указатель для уменьшения и сравнения. Пока я признаю, что do {} while (- wordvar); не может выдать такой хороший код, как do {} while (wordvar--); код лучше, чем тот, который на самом деле генерирует последний формат. Выполнение отдельного декремента и while-теста (например, while (--lpw, lpw)) дает разумный код, но это выглядит немного уродливо. Оператор после декремента может дать лучший код для цикла обратного отсчета:
decf _lpw
btfss _STATUS,0 ; Skip next inst if carry (i.e. wasn't zero)
decf _lpw+1
bc loop ; Carry will be clear only if lpw was zero
но вместо этого он генерирует худший код, чем --lpw. Лучший код был бы для повышающего цикла:
infsnz _lpw
incfsz _lpw+1
bra loop
но компилятор этого не генерирует.
РЕДАКТИРОВАТЬ 2
Другой подход, который я мог бы использовать: выделить глобальную 16-битную переменную для количества байтов и записать функции так, чтобы счетчик всегда обнулялся до выхода. Тогда, если требуется только 8-битное значение, необходимо будет загрузить только 8 бит. Я бы использовал макросы для вещей, чтобы они могли быть настроены для лучшей эффективности. На PIC использование | = для переменной, которая, как известно, равно нулю, никогда не медленнее, чем использование =, а иногда и быстрее. Например, intvar | = 15 или intvar | = 0x300 будут двумя инструкциями (каждый случай должен беспокоить только один байт результата и может игнорировать другой); intvar | = 4 (или любая степень 2) - это одна инструкция. Очевидно, что на некоторых других процессорах intvar = 0x300 будет быстрее, чем intvar | = 0x300; если я использую макрос, его можно настроить соответствующим образом.