如果执行频率更高,相同的代码需要更长的时间

Same code takes longer if executed more often?

本文关键字:代码 时间 频率 执行 如果      更新时间:2023-09-26

我在一个网页上的<script>标签中有以下代码,上面没有其他内容。恐怕我目前还没有在线代码。正如你所看到的,它以两种不同的方式将200万以下的所有素数相加,并计算出平均耗时。变量howOften用于多次执行此操作,因此您可以对其进行平均。让我困惑的是,对于howOften == 1,方法2更快,但对于howOften == 10,方法1更快。这种差异是显著的,即使你打了几次F5也会保持不变。

我的问题很简单:怎么会这样?

(这篇文章经过了编辑,采纳了alf的建议。但这并没有什么区别!我现在非常困惑。)

(再次编辑:howOften达到或超过1000,时间似乎稳定。Alf的答案似乎是正确的。)

function methodOne(maxN) {
    var sum, primes_inv, i, j;
    sum = 0;
    primes_inv = [];
    for ( var i = 2; i < maxN; ++i ) {
        if ( primes_inv[i] == undefined ) {
            sum += i;
            for ( var j = i; j < maxN; j += i ) {
                primes_inv[j] = true;
            }
        }
    }
    return sum;
}
function methodTwo(maxN) {
    var i, j, p, sum, ps, n;
    n = ((maxN - 2) / 2);
    sum = n * (n + 2);
    ps = [];
    for(i = 1; i <= n; i++) {
        for(j = i; j <= n; j++) {
            p =  i + j + 2 * i * j;
            if(p <= n) {
                if(ps[p] == undefined) {
                    sum -= p * 2 + 1;
                    ps[p] = true;
                }
            }
            else {
                break;
            }
        }
    }
    return sum + 2;
}

// ---------- parameters
var howOften = 10;
var maxN = 10000;
console.log('iterations: ', howOften);
console.log('maxN: ', maxN);

// ---------- dry runs for warm-up
for( i = 0; i < 1000; i++ ) {
    sum = methodOne(maxN);
    sum = methodTwo(maxN);
}
// ---------- method one
var start = (new Date).getTime();
for( i = 0; i < howOften; i++ )
    sum = methodOne(maxN);
var stop = (new Date).getTime();
console.log('methodOne: ', (stop - start) / howOften);
// ---------- method two
for( i = 0; i < howOften; i++ )
    sum = methodTwo(maxN);
var stop2 = (new Date).getTime();
console.log('methodTwo: ', (stop2 - stop) / howOften);

JS运行时是一个优化的JIT编译器。这意味着,在一段时间内,您的代码会被解释(tint),之后,它会被编译(tjitun)。

现在,您计算的很可能是(tint+tjitunun,不幸的是,这种比较没有多大意义。

所以答案是,我不知道。为了得到正确的答案,

  1. 将要评测的代码提取到函数中
  2. 对这些功能运行预热循环,不要使用预热循环中的计时
  3. 无论是热身还是测量,都要跑1….10次以上
  4. 尝试将测量时间的顺序替换为算法
  5. 如果可以的话,进入JS解释器的内部,并确保你了解发生了什么:你真的衡量你认为你做了什么吗?JIT是否在预热周期中运行,而不是在测量时运行?等等

更新:还要注意,在1个周期内,您得到的运行时间小于系统计时器的分辨率,这意味着错误可能大于您比较的实际值。

methodTwo只需要处理器执行更少的计算。在methodOne中,您的初始for循环最多执行N次。在methodTwo中,您的初始for循环执行(maxN-2)/2次。因此,在第二种方法中,处理器所做的计算数量不到第一种方法的一半。每个方法都包含一个嵌套的for循环,这一事实加剧了这种情况。所以方法一的big-O是maxN^2。而方法二的big-O是((maxN-2)/2)^2。