Существует множество оптимизаций, которые вы можете выполнить, включая введение целевого кода.Однако я буду придерживаться в основном общих вещей.
Во-первых, если вы собираетесь зацикливаться с ограничениями индекса, то вам обычно следует пытаться зацикливаться вниз.*
до
for (i = iLen-1; i <= 0; i--) {
Это может использовать тот факт, что многие распространенные процессоры по существу выполняют сравнение с 0 для результатов любой математической операции, поэтому вам не нужно делать явное сравнение.
Это работает только в том случае, если обратный ход цикла имеет те же результаты и если индекс подписан (хотя вы можете обойти это).
В качестве альтернативы вы можете попробовать ограничить с помощьюуказатель математика.Это может устранить необходимость в явной переменной индекса (счетчика), которая может ускорить процесс, особенно если регистров не хватает.
for (p = rgiFilter; p <= rgiFilter+8; ) {
iPred += (I32) (*p) + *rgiPreval++;
*p++ += *rgiUpdate++;
....
}
Это также избавляет от нечетного обновления в конце вашеговнутренний циклОбновление в конце цикла может запутать компилятор и заставить его создавать худший код.Вы также можете обнаружить, что развертывание цикла, которое вы делали, может привести к худшим или в равной степени таким же хорошим результатам, как если бы у вас было только два оператора в теле внутреннего цикла.Компилятор, вероятно, может принимать правильные решения о том, как этот цикл должен быть развернут / развернут.Или вы можете просто захотеть убедиться, что цикл развернут дважды, поскольку rgiFilter - это массив 16-битных значений, и посмотреть, может ли компилятор получить доступ к нему только дважды, чтобы выполнить два чтения и две записи - выполнение одной 32-битной загрузки.и одно 32-битное хранилище.
for (p = rgiFilter; p <= rgiFilter+8; ) {
I16 x = *p;
I16 y = *(p+1); // Hope that the compiler can combine these loads
iPred += (I32) x + *rgiPreval++;
iPred += (I32) y + *rgiPreval++;
*p++ += *rgiUpdate++;
*p++ += *rgiUpdate++; // Hope that the complier can combine these stores
....
}
Если ваш компилятор и / или целевой процессор поддерживает его, вы также можете попробовать выполнить инструкции предварительной выборки.Например, gcc имеет:
__builtin_prefetch (const void * addr)
__builtin_prefetch (const void * addr, int rw)
__builtin_prefetch (const void * addr, int rw, int locality)
. Они могут быть использованы, чтобы сообщить компилятору, что если у цели есть инструкции предварительной выборки, она должна использовать их, чтобы попытаться продолжить и получить addr
в кэш.Оптимально это должно быть выдано один раз на шаг строки кэша для массива, с которым вы работаете.Аргумент rw
указывает компилятору, хотите ли вы читать или писать по адресу.Локальность связана с тем, должны ли данные оставаться в кэше после того, как вы к ним получите доступ.Компилятор просто пытается сделать все возможное, чтобы выяснить, как сгенерировать правильные инструкции для этого, но если он не может сделать то, что вы просите, для определенной цели, он просто ничего не делает и ничего не вредит.
Кроме того, поскольку функции __builtin_ являются специальными, обычные правила, касающиеся переменного числа аргументов, на самом деле не применяются - это подсказка компилятору, а не вызов функции.
ВыТакже следует изучить любые векторные операции, которые поддерживает ваша цель, а также любые общие, специфические для платформы функции, встроенные функции или прагмы, которые ваш компилятор поддерживает для выполнения векторных операций.