Быстрый способ преобразования плавающего диапазона от -1 до 1 в короткий? - PullRequest
3 голосов
/ 12 августа 2010

Мне нужно многократно преобразовывать 1024+ последовательных 4-байтовых числа с плавающей запятой (диапазон от -1 до 1) в 2-байтовые шорты (диапазон от -32768 до 32767) и записывать на диск.

В настоящее время я делаю это с помощью цикла:

short v = 0;
for (unsigned int sample = 0; sample < length; sample++) 
{
    v = (short)(inbuffer[sample * 2] * 32767.0f);
    fwrite(&v, 2, 1, file);
}

И это работает, но вычисления и циклы с плавающей запятой стоят дорого.Есть ли способ, которым это может быть оптимизировано?

Ответы [ 4 ]

6 голосов
/ 12 августа 2010
short v = 0;
for (unsigned int sample = 0; sample < length; sample++) 
{
    v = (short)(inbuffer[sample * 2] * 32767.0f);
    // The problem is not here-------^^^^^^^^^^^
    fwrite(&v, 2, 1, file);        
    // it is here ^^^^^^^
}

Типичный Mac (тег target-c, или мы говорим здесь об iphone?) Может делать миллиардов умножений с плавающей точкой в ​​секунду. fwrite, однако, является библиотечным вызовом, который следует некоторым косвенным указаниям, чтобы записать свои данные в некоторый буфер и, возможно, очистить его. Лучше заполнить свой собственный буфер в пакете:

short v[SZ] = 0;
// make sure SZ is always > length, or allocate a working buffer on the heap.
for (unsigned int sample = 0; sample < length; sample++) 
{
    v[sample] = (short)(inbuffer[sample * 2] * 32767.0f);
}
fwrite(v,sizeof(v),1,file);
2 голосов
/ 12 августа 2010

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

short v = 0;
short outbuffer = // create outbuffer of required size
for (unsigned int sample = 0; sample < length; sample++) 
{
    outbuffer[sample] = (short)(inbuffer[sample * 2] * 32767.0f);
}

fwrite(outbuffer, 2, sizeof(outbuffer), file);
2 голосов
/ 12 августа 2010

Я бы подумал, что повторные звонки на fwrite будут дорогой частью. Как насчет:

short outbuffer[length]; // note: you'll have to malloc this if length isn't constant and you're not using a version of C that supports dynamic arrays.
for (unsigned int sample = 0; sample < length; sample++) 
{
    outbuffer[sample] = (short)(inbuffer[sample * 2] * 32767.0f);
}
fwrite(outbuffer, sizeof *outbuffer, length, file);
0 голосов
/ 12 августа 2010

Вы можете попробовать что-то вроде этого:

out[i] = table[((uint32_t *)in)[i]>>16];

где table - это справочная таблица, которая отображает старшие 16 битов плавающего элемента IEEE в требуемое значение int16_t Однако это потеряет некоторую точность. Вам необходимо сохранить и использовать 23 бита (1 знаковый бит, 8 экспоненциальных битов и 14 битов мантиссы) для полной точности, а это означает таблицу 16 МБ, которая нарушит когерентность кэша и, следовательно, производительность.

Вы уверены, что преобразования с плавающей запятой происходят медленно? Пока вы используете fwrite таким образом, вы тратите в 50-100 раз больше процессорного времени на fwrite, чем на арифметику с плавающей запятой. Если вы столкнулись с этой проблемой, а код все еще слишком медленный, вы можете использовать метод добавления магического смещения и считывания битов мантиссы для преобразования в int16_t вместо умножения на 32767.0. Это может быть или не быть быстрее.

...