2013-08-30 5 views
3

Я хотел бы улучшить производительность моей Динамическая связанная библиотека (DLL).Предварительно вычислить значения cos() и sin() в таблицах

Для этого я хочу использовать таблицы поиска по соз() и грех() как я использую их много.

Как я хочу максимальной производительности, я хочу создать таблицу от от 0 до 2PI, которая содержит полученные вычисления cos и sin.

Для хорошего результата с точки зрения точности, я думаю, таблицы 1 мб для каждой функции - хорошая сделка между размером и точностью.

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

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

Но как это сделать в C++?

EDIT1: код от jons34yp очень хорош для создания векторных файлов.

Я сделал небольшой тест и обнаружил, что если вам нужна хорошая точность и хорошая скорость, вы можете сделать 250000 единиц векторного и линейного интерполята между ними, у вас будет максимальная ошибка 7.89E-11 (!), И это самый быстрый между всеми аппроксимаций я пытался (и это больше, чем 12x быстрее, чем грех() (13,296 х быстрее точно)

+0

http://stackoverflow.com/questions/4864866/cc-with-gcc-statically-add-resource-files-to-executable-library –

+0

Вы могли бы напишите вспомогательную программу/скрипт, который будет генерировать таблицу в синтаксисе исходного кода на C++. – Angew

+1

Профилированы ли вы и определили, что эти две функции действительно являются горячими точками в вашем коде? На современных процессорах они, как правило, довольно быстрые, и вы можете обнаружить, что решение на основе таблицы заканчивается медленнее и менее безопасно для кэша. –

ответ

3

простое решение написать отдельную программу, которая создает .cc файл с определением вашего вектора.

Например:

#include <iostream> 
#include <cmath> 

int main() 
{ 
    std::ofstream out("values.cc"); 

    out << "#include \"static_values.h\"\n"; 
    out << "#include <vector>\n"; 

    out << "std::vector<float> pi_values = {\n"; 
    out << std::precision(10); 

    // We only need to compute the range from 0 to PI/2, and use trigonometric 
    // transformations for values outside this range. 
    double range = 3.141529/2; 
    unsigned num_results = 250000; 

    for (unsigned i = 0; i < num_results; i++) { 
     double value = (range/num_results) * i; 
     double res = std::sin(value); 

     out << " " << res << ",\n"; 
    } 
    out << "};\n" 
    out.close(); 
} 

Обратите внимание, что это вряд ли улучшит производительность, так как таблица этого размера, вероятно, не поместится в вашем кэше L2. Это означает, что большой процент тригонометрических вычислений должен будет иметь доступ к ОЗУ; каждый такой доступ требует примерно нескольких сотен циклов ЦП.

Кстати, вы посмотрели примерные тригонометрические библиотеки SSE SIMD. Это похоже на хороший вариант использования.

+0

Это интересный вопрос: промаха в кеше стоит дорого, но более или менее дорого, чем вычисление 'sin' или' cos'? Эти функции также не дешевы. –

+0

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

+0

Библиотеки SSE SIMD, которые я использовал, вычисляют одно значение sin примерно в нескольких десятках циклов. Для снижения точности производительность даже может быть увеличена. –

0

Обычный ответ на этот вопрос заключается в том, чтобы написать небольшую программу , которая генерирует исходный файл C++ со значениями в таблице и скомпилирует ее в вашу DLL. Если вы думаете о таблицах с 128000 записей (128000 удваивается 1 МБ), однако вы можете столкнуться с некоторыми внутренними ограничениями в своем компиляторе. В этом случае вы можете записать значения файла в качестве дампа памяти и mmap при загрузке DLL. (Под окнами, я думаю, вы могли бы поместить этот второй файл во второй поток вашего DLL-файла, чтобы у вас не было для распространения второго файла.)

2

Вы можете использовать Предвычисление вместо того, чтобы хранить их уже предварительно вычислены в исполняемом файле:

double precomputed_sin[65536]; 

struct table_filler { 
    table_filler() { 
     for (int i=0; i<65536; i++) { 
      precomputed_sin[i] = sin(i*2*3.141592654/65536); 
     } 
    } 
} table_filler_instance; 

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

inline double tsin(int x) { return precomputed_sin[x & 65535]; } 
inline double tcos(int x) { return precomputed_sin[(x + 16384) & 65535]; } 
+0

65536 раз вычисление sin - это почти количество вычислений, которые я должен сделать для 1 изображения, поэтому это не вариант (btw я написал, что мне не нужны вычисляемые таблицы «на лету» – IonOne

+0

@IonOne: Я думаю, что вы допустили ошибку в своих измерениях. Предварительная вычисление синусоиды из 65536 элементов занимает намного меньше, чем загрузка и инициализация библиотеки DLL. Если вы не повторно инициализируете DLL для каждого изображения, почему вам нужно делать это вычисление каждого изображения?BTW-вычисление таблицы 65536 записей для синуса занимает около 0,3 мс на моем ПК (с использованием 4-симметрии): вы действительно можете загружать и инициализировать свою DLL более 3000 раз в секунду? – 6502

+0

ОК, может быть, вы правы, но в любом случае 65536 недостаточно для точности, которая мне нужна (возможно, с линейной интерполяцией ...) – IonOne