【发布时间】:2021-07-09 16:25:08
【问题描述】:
我正在编写一个光线投射器,并试图通过为我最常用的三角函数(即sin、cos 和tan)制作查找表来加速它。这第一个 sn-p 是我的表查找代码。为了避免为每个表创建一个查找表,我只创建一个sin 表,并将cos(x) 定义为sin(half_pi - x) 和tan(x) 为sin(x) / cos(x)。
#include <math.h>
#include <time.h>
#include <stdio.h>
#include <stdlib.h>
const float two_pi = M_PI * 2, half_pi = M_PI / 2;
typedef struct {
int fn_type, num_vals;
double* vals, step;
} TrigTable;
static TrigTable sin_table;
TrigTable init_trig_table(const int fn_type, const int num_vals) {
double (*trig_fn) (double), period;
switch (fn_type) {
case 0: trig_fn = sin, period = two_pi; break;
case 1: trig_fn = cos, period = two_pi; break;
case 2: trig_fn = tan, period = M_PI; break;
}
TrigTable table = {fn_type, num_vals,
calloc(num_vals, sizeof(double)), period / num_vals};
for (double x = 0; x < period; x += table.step)
table.vals[(int) round(x / table.step)] = trig_fn(x);
return table;
}
double _lookup(const TrigTable table, const double x) {
return table.vals[(int) round(x / table.step)];
}
double lookup_sin(double x) {
const double orig_x = x;
if (x < 0) x = -x;
if (x > two_pi) x = fmod(x, two_pi);
const double result = _lookup(sin_table, x);
return orig_x < 0 ? -result : result;
}
double lookup_cos(double x) {
return lookup_sin(half_pi - x);
}
double lookup_tan(double x) {
return lookup_sin(x) / lookup_cos(x);
}
以下是我对代码进行基准测试的方法:当前时间的函数(以毫秒为单位)来自here。问题出现在这里:当我的lookup_sin 与math.h 的sin 计时时,我的变体需要大约三倍的时间:Table time vs default: 328 ms, 108 ms。
这是cos 的时间:
Table time vs default: 332 ms, 109 ms
这是tan 的时间安排:
Table time vs default: 715 ms, 153 ms
是什么让我的代码这么慢?我认为预先计算 sin 值会大大加速我的代码。也许是lookup_sin 函数中的fmod?请提供您拥有的任何见解。我正在使用 clang 进行编译,没有启用任何优化,因此不会删除对每个 trig 函数的调用(我忽略了返回值)。
const int64_t millis() {
struct timespec now;
timespec_get(&now, TIME_UTC);
return ((int64_t) now.tv_sec) * 1000 + ((int64_t) now.tv_nsec) / 1000000;
}
const int64_t benchmark(double (*trig_fn) (double)) {
const int64_t before = millis();
for (double i = 0; i < 10000; i += 0.001)
trig_fn(i);
return millis() - before;
}
int main() {
sin_table = init_trig_table(0, 15000);
const int64_t table_time = benchmark(lookup_sin), default_time = benchmark(sin);
printf("Table time vs default: %lld ms, %lld ms\n", table_time, default_time);
free(sin_table.vals);
}
【问题讨论】:
-
@CaspianAhlberg:寻找它每秒投射更多的光线,而不是占用少于 100% 的 cpu 时间。
-
查找包含两个除法,在
fmod和除以步长。除法速度慢是出了名的。它包含几个可能导致次优的测试和分支指令的测试。 (高性能代码通常会减少控制流分支以支持数据操作。)我看不到问题中所述的步长。如果表很大,那么它不会保留在缓存中,内存查找会很慢。 -
关于“我正在使用 clang 编译但未启用优化”:希望您的意思是编译时没有优化而不是查找例程。
-
@CaspianAhlberg:那么,系统
sin和cos比您的查找代码快的原因是因为我编写了它们。 -
@CaspianAhlberg 一般来说,如今在软件中使用查找表来实现数学函数很少是最佳选择。我自己在这方面的专业经验在很大程度上与本文中提出的观点相吻合:Marat Dukhan 和 Richard Vuduc,“基本函数的高通量计算方法”。 并行处理和应用数学,第 86-95 页。斯普林格,2014 年。
标签: c caching optimization trigonometry lookup-tables