Альтернативы отсутствующим инструкциям SSAT и USAT в Arm64? - PullRequest
0 голосов
/ 13 ноября 2018

Мы портируем основное приложение с Arm32 на Arm64.Наши алгоритмы часто используют инструкции SSAT и USAT.Они чрезвычайно полезны, выполняя сдвиг влево или вправо любого размера, а затем насыщение со знаком или без знака до произвольного числа битов.Это чрезвычайно полезно для алгоритмов обработки изображений, потому что мы можем выполнить некоторую математику, которая генерирует 32-разрядный целочисленный результат, а затем извлечь из этого все нужные нам биты (насыщенные до максимальной / минимальной глубины в битах выходного изображения) с помощьюодна инструкция.

Эти инструкции необъяснимо исчезли в Arm64, и мы нашли ближайшую альтернативу SQSHRN / UQSHRN / SQSHLN / UQSHLN, которая выполняет сдвиг и насыщение, но далекоболее ограниченный в насыщенности, которую они выполняют (USAT может насыщать до любой ширины, даже до 7 битов; новые инструкции могут насыщать только до половины ширины ввода, например, 16 битов в случае 32-битного ввода, что потребует дополнительныхобработка для достижения необходимого результата).

Может кто-нибудь объяснить, почему эти инструкции были отброшены, и каков наилучший способ эффективно портировать существующий код, который их использует?

1 Ответ

0 голосов
/ 14 ноября 2018

- ОБНОВЛЕНИЕ - время корректного тестирования было значительно медленнее при использовании не ассемблерного кода, я буду искать другой метод

Я сравнил этот код сборки:

#define __arm_ssat(src, bits)   asm("ssat %[srcr], %[satv], %[srcr]"    :[srcr]"+r"(src):[satv]"I"(bits));

с этим:

#define MAX_SIGNED_NUM(bits) ((1 << (bits -1)) -1)
#define __arm_ssat(src, bits)   {src = ((src > MAX_SIGNED_NUM(bits)) ? MAX_SIGNED_NUM(bits) : src);}

при выполнении этого - ОБНОВЛЕННОГО ТЕСТА - на 32-битном устройстве:

volatile  void assert_ssat_asm(int* buf, size_t loops){
    int64_t num = buf[0];
    int64_t num_a = buf[1];
    int64_t num_b = buf[2];
    int sum = 0;
    struct timeval tmv1; gettimeofday(&tmv1,NULL);
    for (int i = 0; i < loops; ++i){
        __arm_ssat(num, 8);
        sum+=num;
        assert( 127 == num);
        num = buf[0];

        __arm_ssat(num, 16);
        sum+=num;
        assert(32767 == num);

        __arm_ssat(num_a, 8);
        sum+=num;
        assert( 127 == num_a);
        num_a = buf[1];

        __arm_ssat(num_a, 16);
        sum+=num;
        assert( 690 == num_a);

        __arm_ssat(num_b, 8);
        sum+=num;
        assert( 127 == num_b);
        num_b = buf[2];

        __arm_ssat(num_b, 16);
        sum+=num;
        assert( 32767 == num_b);
    }
    struct timeval tmv2; gettimeofday(&tmv2,NULL);
    int tdiff_usec = (tmv2.tv_sec*1000000 + tmv2.tv_usec) - (tmv1.tv_sec*1000000 + tmv1.tv_usec);

    printf("%d\n", sum);
    printf("ran %d times, total time: %d,  average time asm: %.7f\n", loops, tdiff_usec, (double)tdiff_usec/loops);
}
int main ()
{
    int buf[] = { 69000, 690, 64000 };
    test_ssat(buf, 1000000);
}

Я получил такие результаты:

Выполнить 1000000 циклов, среднее время рег: 0,0210270

Выполнить 1000000 циклов, среднее время сборки: 0,0057960

...