Math.sin().cos()的成本vs whacky自己的实现

时间:2016-06-13 10:25:35

标签: javascript performance game-engine trigonometry

所以大约一个小时前,当很长一段时间后发生咔嗒声时,我遇到了这个美好而又令人尴尬的时刻。推30,我终于得到了鼻窦和cosinus。在一集之后,这只能被描述为基本上重新实现了轮子。

为了让WASD控件在两个轴上移动,考虑到玩家看起来的方向,我决定采用一个简单的开关:

function setModulation(rotation) {
    var rotationTemp = 0;
    var modulation = {};

    rotationTemp = rotation;
    if (rotationTemp > 180) {
        rotationTemp = 360 - rotationTemp;
    }
    rotationTemp /= 180;

    modulation.z = 1 - rotationTemp * 2;

    rotationTemp = rotation + 90;
    if (rotationTemp > 180) {
        rotationTemp = 360 - rotationTemp;
    }
    rotationTemp /= 180;

    modulation.x = 1 - rotationTemp * 2;
    modulation.x *= -1;

    return modulation;
};

...其中modulation.z和.x将基于玩家面向的方向。调制值需要在-1到+1之间。 (这应该是我的第一个线索,是的!)所以我帮助自己得到了一些可靠的笔和纸,并且对我9年级的教育一无所知,我想出了:

function setModulationMath(rotation) {
    var modulation = {};

    modulation.z = Math.cos(rotation * Math.PI / 180);
    modulation.x = Math.sin(rotation * Math.PI / 180);

    return modulation;
};

在此期间学习和/或记住了一点,在重新审视这段代码时,我突然意识到:这是一个cos和罪的工作。 Srsly。

function setModulationMath(rotation) {
    var modulation = {};

    modulation.z = Math.cos(rotation * Math.PI / 180);
    modulation.x = Math.sin(rotation * Math.PI / 180);

    return modulation;
};

function setModulation(rotation) {
    var rotationTemp = 0;
    var modulation = {};

    rotationTemp = rotation;
    if (rotationTemp > 180) {
        rotationTemp = 360 - rotationTemp;
    }
    rotationTemp /= 180;

    modulation.z = 1 - rotationTemp * 2;

    rotationTemp = rotation + 90;
    if (rotationTemp > 180) {
        rotationTemp = 360 - rotationTemp;
    }
    rotationTemp /= 180;

    modulation.x = 1 - rotationTemp * 2;
    modulation.x *= -1;

    return modulation;
};

var iterations = 1000000;
console.time('setModulation');
for(var i = 0; i < iterations; i++ ){
    setModulation(i, i / 2);
};
console.timeEnd('setModulation')

console.time('setModulationMath');
for(var i = 0; i < iterations; i++ ){
    setModulationMath(i, i / 2);
};
console.timeEnd('setModulationMath')

到目前为止很开心!使用数学的数学是什么!但后来我检查了表现。我自己的实施&#34;在FF,Chrome和&amp; IE浏览器。使用1.000.000次迭代,它在我的本地设置上增加了35%到45%之间。

任何人都有指示为什么?它只是Math.cos()/ .sin()吗?可衡量的增长方式超出人们预期,不是吗?有经验的人倾向于分享她的见解吗?

&#13;
&#13;
    private float readCpuUsage() {
    try {
        RandomAccessFile reader = new RandomAccessFile("/proc/stat", "r");
        String load = reader.readLine();

        String[] toks = load.split(" ");

        long idle1 = Long.parseLong(toks[5]);
        long cpu1 = Long.parseLong(toks[2]) + Long.parseLong(toks[3])
                + Long.parseLong(toks[4]) + Long.parseLong(toks[6])
                + Long.parseLong(toks[7]) + Long.parseLong(toks[8]);

        try {
            Thread.sleep(360);
        } catch (Exception e) {
        }

        reader.seek(0);
        load = reader.readLine();
        reader.close();

        toks = load.split(" ");

        long idle2 = Long.parseLong(toks[5]);
        long cpu2 = Long.parseLong(toks[2]) + Long.parseLong(toks[3])
                + Long.parseLong(toks[4]) + Long.parseLong(toks[6])
                + Long.parseLong(toks[7]) + Long.parseLong(toks[8]);

        return (float) (cpu2 - cpu1) / ((cpu2 + idle2) - (cpu1 + idle1));

    } catch (IOException ex) {
        ex.printStackTrace();
    }

    return 0;
}
&#13;
&#13;
&#13;

2 个答案:

答案 0 :(得分:4)

你的功能可能会更快。然而,这种比较没有任何意义,因为这些功能会产生完全不同的结果。我们只是对错误进行基准测试:

var err_z = 0;
var err_x = 0;
for (var i = 0; i < 360; ++i){
    var mm = setModulationMath(i);
    var m = setModulation(i);
    err_z += Math.abs(mm.z - m.z);
    err_x += Math.abs(mm.x - m.x);
}

而且......

err_z == 49.17730025861921
err_x == 113.58865012930961

那很糟糕。如果两者完全相似,那么你期望值接近0。

  

虽然我的第一个假设是我必须以某种方式伪装我的方式:它完全有效。两个函数都提供相同的调制值。这就是说:我很高兴被告知这是一个只能起作用的黑客,因为[x] ......:)

平均误差在z轴上为0.137,在x轴上为0.316。这对你来说可能已经足够了,但肯定存在价值差异较大的价值。你可能只是检查正交方向吗?在这种情况下,您需要的是switch,而不是三角函数。

编辑:哦,这是z部分的图表。波浪线是余弦,直线是你的近似值。

graphs

但我离题了。

(试图)推出你自己的trig函数,数学函数等实现并不一定是坏事。这实际上取决于应用程序的细节,有时你会发现你不需要这样做精度,或你的角度只会表示为整数度......在这些情况下,使用更简单的近似或查找表可能是有益的。

实际上,Javascript(以及许多其他标准库)中的正弦和余弦都经过了非常优化且非常精确。

答案 1 :(得分:1)

由于许多原因,数学运算成本更高,但主要是因为它们处理一般情况和边缘情况,而不像简单的乘法和加法运算。

对象查找和函数调用也非常昂贵,因此缓存PI始终是经验法则。我将setModulationMath操作更改为以下内容。我使用performance.now()而不是timeEnd,因为它更准确。在此处阅读更多内容:https://developer.mozilla.org/en-US/docs/Web/API/Performance/now

我的结果:

  • setModulation:50.264999999999986ms
  • setModulationMath:68.46499999999997ms

&#13;
&#13;
var PI180 = Math.PI/180;

function setModulationMath(rotation) {
  var rotationC = rotation * PI180;
  return {
    z: Math.cos(rotationC),
    x: Math.sin(rotationC)
  };
};


function setModulation(rotation) {
  var rotationTemp = 0;
  var modulation = {};
  rotationTemp = rotation;
  if (rotationTemp > 180) {
    rotationTemp = 360 - rotationTemp;
  }
  rotationTemp /= 180;
  modulation.z = 1 - rotationTemp * 2;
  rotationTemp = rotation + 90;
  if (rotationTemp > 180) {
    rotationTemp = 360 - rotationTemp; 
  }
  rotationTemp /= 180;
  modulation.x = 1 - rotationTemp * 2;
  modulation.x *= -1;
  return modulation;
};


var iterations = 1000000;
var t0 = performance.now();
for (var i = 0; i < iterations; i++){
  setModulation(i);
}
var t1=performance.now();
console.log("setModulation: "+(t1-t0)+"ms");


var t2 = performance.now();
for (var l = 0; l < iterations; l++){
  setModulationMath(l);
}
var t3=performance.now();
console.log("setModulationMath: "+(t3-t2)+"ms");
&#13;
&#13;
&#13;