Производительность: с плавающей точкой на int и результат отсечения до диапазона - PullRequest
3 голосов
/ 27 мая 2010

Я делаю некоторую обработку звука с помощью float. Результат необходимо преобразовать обратно в образцы PCM, и я заметил, что приведение от float к int удивительно дорого. Что еще более расстраивает, что мне нужно обрезать результат до диапазона короткого (от -32768 до 32767). В то время как я обычно инстинктивно предполагаю, что это может быть обеспечено простым приведением значения типа float к short, в Java это терпит неудачу, поскольку на уровне байт-кода это приводит к F2I, за которым следует I2S. Так что вместо простого:

int sample = (short) flotVal;

Мне нужно было прибегнуть к этой ужасной последовательности:

int sample = (int) floatVal;
if (sample > 32767) {
    sample = 32767;
} else if (sample < -32768) {
    sample = -32768;
}

Есть ли более быстрый способ сделать это?

(около 6% общего времени выполнения, по-видимому, расходуется на приведение, в то время как на первый взгляд кажется, что 6% не так уж и много, и это поразительно, когда я считаю, что часть обработки включает в себя хороший кусок умножения матриц и IDCT )

  • РЕДАКТИРОВАТЬ Приведенный выше код приведения / отсечения находится (что неудивительно) в теле цикла, который считывает значения с плавающей точкой из float [] и помещает их в байт []. У меня есть набор тестов, который измеряет общее время выполнения в нескольких тестовых примерах (обработка около 200 МБ необработанных аудиоданных). 6% были получены из разницы времени выполнения, когда присвоение приведения "int sample = (int) floatVal" было заменено назначением индекса цикла для образца.

  • РЕДАКТИРОВАТЬ @leopoldkot: Мне известно об усечении в Java, как указано в исходном вопросе (F2I, последовательность байт-кода I2S). Я пробовал приводить только к коротким, потому что предполагал, что в Java есть байт-код F2S, чего, к сожалению, нет (изначально он был на фоне сборки 68K, где простой "fmove.w FP0, D0" сделал бы именно то, что я хотел) .

Ответы [ 5 ]

2 голосов
/ 28 мая 2010

Вы можете превратить два сравнения в одно для значений, которые находятся в диапазоне. Это может вдвое снизить стоимость. В настоящее время вы выполняете только одно сравнение, если значение слишком отрицательное. (что может быть не типичным случаем)

if (sample + 0x7fff8000 < 0x7fff0000)
    sample = sample < 0 ? -32768 : 32767;
1 голос
/ 27 мая 2010

Когда вы приводите int к short, вы никогда не получаете функцию отсечения, биты усекаются, а затем читаются как короткие. Например. (короткий) -40000 становится 25536, а не -32768, как вы ожидали.

Возможно, вам нужно отредактировать свой вопрос, я уверен, что вы это знаете, если разбирали байт-код. Также есть JIT-компилятор, который может оптимизировать этот код (потому что он часто вызывается) для зависимых от платформы инструкций.

Пожалуйста, преобразуйте этот ответ в комментарий.

0 голосов
/ 28 мая 2010

Это Python, но его легко конвертировать. Я не знаю, насколько дорогостоящими являются операции с плавающей запятой, но если вы можете хранить их в целочисленных регистрах, вы можете получить некоторое повышение; это предполагает, что вы можете интерпретировать биты IEEE754 как int. (Это то, что делает мой плохо названный float2hex.)

import struct

def float2hex(v):
    s = struct.pack('f', v)
    h = struct.unpack('I', s)[0]
    return h

def ToInt(f):
    h = float2hex(f)
    s = h >> 31
    exp = h >> 23 & 0xFF
    mantissa = h & 0x7FFFFF
    exp = exp - 126
    if exp >= 16:
        if s:
            v = -32768
        else:
            v = 32767
    elif exp < 0:
        v = 0
    else:
        v = mantissa | (1 << 23)
        exp -= 24
        if exp > 0:
            v = v << exp
        elif exp < 0:
            v = v >> -exp

        if s:
            v = -v

    print v

Это ветвление может убить вас, но, может быть, это все равно что-нибудь полезное? Это округляет до нуля.

0 голосов
/ 28 мая 2010

int sample = ((int) floatval) & 0xffff;

0 голосов
/ 27 мая 2010

преобразования с плавающей точкой в ​​int - это одна из самых медленных операций, которую вы можете выполнять на процессоре x86, поскольку она требует изменения режимов округления x87 (дважды), которые сериализуют и сбрасывают процессор. Вы можете получить значительное ускорение, если вы можете использовать инструкции SSE вместо инструкций x87, но я понятия не имею, есть ли способ сделать это в Java. Возможно, попробуйте использовать x86_64 JVM?

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...