Тесты ARM Cortex A8: может ли кто-нибудь помочь мне разобраться в этих цифрах?

Я работаю над написанием нескольких DSP-алгоритмов реального времени на Android, поэтому решил запрограммировать ARM прямо на ассемблере, чтобы максимально все оптимизировать и сделать математику максимально облегченной. Сначала я получал тесты скорости, которые не имели особого смысла, поэтому я начал читать об опасностях конвейера, возможностях двойной проблемы и так далее. Меня все еще озадачивают некоторые цифры, которые я получаю, поэтому я размещаю их здесь в надежде, что кто-нибудь сможет пролить свет на то, почему я получаю то, что получаю. В частности, меня интересует, почему NEON занимает разное количество времени для выполнения вычислений с разными типами данных, хотя он утверждает, что выполняет каждую операцию ровно за один цикл. Мои выводы таковы.

Я использую очень простой цикл для бенчмаркинга и запускаю его на 2 000 000 итераций. Вот моя функция:

hzrd_test:

    @use received argument an number of iterations in a loop
    mov r3 , r0

    @come up with some simple values
    mov r0, #1
    mov r1, #2

    @Initialize some NEON registers (Q0-Q11)
    vmov.32 d0, r0, r1
    vmov.32 d1, r0, r1
    vmov.32 d2, r0, r1

    ...

    vmov.32 d21, r0, r1
    vmov.32 d22, r0, r1
    vmov.32 d23, r0, r1

hzrd_loop:

    @do some math
    vadd.s32 q0, q0, q1
    vadd.s32 q1, q0, q1
    vadd.s32 q2, q0, q1
    vadd.s32 q3, q0, q1
    vadd.s32 q4, q0, q1
    vadd.s32 q5, q0, q1
    vadd.s32 q6, q0, q1
    vadd.s32 q7, q0, q1
    vadd.s32 q8, q0, q1
    vadd.s32 q9, q0,s q1
    vadd.s32 q10, q0, q1
    vadd.s32 q11, q0, q1

    @decrement loop counter, branch to loop again or return
    subs r3, r3, #1
    bne hzrd_loop

    @return
    mov r0, r3
    mov pc, lr

Обратите внимание на операцию вычисления и тип данных, указанные как векторное сложение (vadd) и 32-битное целое число со знаком (s32). Эта операция завершается в течение определенного времени (см. таблицу результатов ниже). Согласно документу ARM Cortex-A8 и последующим страницам, почти все элементарная арифметическая операция в NEON должна выполняться за один цикл, но вот что я получаю:

vmul.f32 ~62ms
vmul.u32 ~125ms
vmul.s32 ~125ms

vadd.f32 ~63ms
vadd.u32 ~29ms
vadd.s32 ~30ms

Я делаю их, просто заменяя операции и типы данных всего в приведенном выше цикле. Есть ли причина, по которой vadd.u32 в два раза быстрее, чем vadd.f32, а vmul.f32 в два раза быстрее, чем vmul.u32?

Ваше здоровье! знак равно


person Phonon    schedule 08.11.2011    source источник


Ответы (2)


Вау, ваши результаты ОЧЕНЬ точны:

  • 32-битное целое число Q умножение стоит 4 цикла, а число с плавающей запятой - 2.
  • 32-битное целое число Q add стоит 1 цикл, а float — 2.

Хороший эксперимент.

Возможно, вы уже знаете, но будьте осторожны при написании кода для NEON:

  • не обращайтесь к памяти с помощью ARM, пока NEON выполняет тяжелую работу
  • не смешивайте инструкции VFP с инструкциями NEON. (кроме общих)
  • не обращаться к регистрам S.
  • не переходить с регистров NEON на ARM

Все вышеперечисленное вызовет ОГРОМНУЮ икоту.

Удачи!

PS: вместо этого я бы предпочел оптимизировать для A9 (немного другое время цикла), поскольку почти все новые устройства поставляются с A9. А временная диаграмма A9 от ARM намного читабельнее. :-)

person Jake 'Alquimista' LEE    schedule 08.11.2011
comment
Что ж, судя по тому, как вы считаете циклы, мои данные определенно имеют смысл, но как вы их считаете? Очевидно, я неправильно читаю документацию, или мы читаем разные документы. - person Phonon; 09.11.2011
comment
На самом деле мы читаем одно и то же. Вам не нужно считать. Это просто на графике в разделе «Циклы». ARM пытается показать нам слишком много и только сбивает с толку. Посмотрите на VMUL(integer, normal). Справа от Qd, Qm, Qn вы видите от 1 до 4. Это означает, что требуется 4 цикла, и справа от них вы можете видеть, что происходит на ЭТОМ этапе цикла выполнения. (на каком этапе конвейера ожидаются операнды src и на каком этапе помещаются операнды dst) - person Jake 'Alquimista' LEE; 09.11.2011
comment
Я бы сказал так: 4, Qd(5,3), Qn(4,2), Qm(3,1). Но это АРМ. - person Jake 'Alquimista' LEE; 09.11.2011
comment
Спасибо! =) Меня действительно смутил документ. - person Phonon; 09.11.2011

Я собираюсь предположить (поскольку у меня нет ссылок на мои документы), что вы сталкиваетесь с проблемами конвейера. Я знаю, что FPU - ошибка, теперь называемая VFPU, имеет другую длину конвейера, чем ЦП, для выполнения целочисленной математической части вашего цикла. Я вижу, что вторая арифметическая операция зависит от первой, которая остановит любой конвейер и, возможно, выявит различия, которые вы видите.

Кроме того, я считаю, что умножение - это не 1-тактная инструкция для целых чисел, а 2-5 циклов в зависимости от старшего разряда 2-го значения - 2 цикла здесь из-за небольшого размера числа, что объясняет эту разницу. Чтобы убедиться в этом, начните с большего числа умножения и посмотрите, замедляется ли оно при большем размере.

Я бы также проверил, что весь ваш код умещается на 1 странице кеша, чтобы исключить и эту возможность.

Я также хотел бы проверить раздел о двойном выполнении чуть выше, так как есть всевозможные остановки конвейера, которые происходят там, когда все зависит друг от друга.

person Michael Dorgan    schedule 08.11.2011
comment
Я не уверен на 100%, что читаю раздел 16.6 Cortex-A8 TRM (PDF) правильно, но я думаю, что вы правы в том, что некоторые инструкции занимают более 1 цикла. - person user786653; 08.11.2011
comment
Михаил, насколько велика одна страница кэша инструкций? Я никогда не обращал на это внимания до сих пор. Было бы хорошо знать. - person Jake 'Alquimista' LEE; 09.11.2011
comment
Честно говоря, я не уверен. Это зависит от реализации - что-то, что вам нужно искать для вашего конкретного устройства. Хороший ответ, кстати. Ваш ответ объясняет большие различия, а мой может объяснить меньшие различия. - person Michael Dorgan; 09.11.2011