最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - Why is Firefox 30 times slower than Chrome, when calculating Perlin noise? - Stack Overflow

programmeradmin4浏览0评论

I have written a map generator in javascript, using classical perlin noise scripts I have found in various places, to get the functionality I want. I have been working in chrome, and have not experienced any problems with the map. However, when I tested it in firefox, it was incredibly slow - almost hanging my system. It fared better in the nightly build, but still 30 times slower than Chrome.

You can find a test page of it here: /

Here is the html code:

<!DOCTYPE html>

<html>
<head>
<title>PerlinMapTest</title>
</head>
<body>

<canvas id="map" width="100" height="100" style="border: 1px solid red">My Canvas</canvas>

<script src="//code.jquery/jquery-2.0.0.min.js"></script>
<script>
$(document).ready(function(){
    //Log time in two ways
    var startTime = new Date().getTime();
    console.time("Map generated in: ");

    var canvas = $("#map")[0];
    var ctx = canvas.getContext("2d");
    var id = ctx.createImageData(canvas.width, canvas.height); 

    var noiseMap = new PerlinNoise(500);

    var startx = 0;
    var starty = 0;

    var value = 0;

    for(var i = startx; i < canvas.width; i++){
        for(var j = starty; j < canvas.height; j++){
            value = noiseMap.noise(i,j, 0, 42);
            value = linear(value,-1,1,0,255);
            setPixel(id, i, j, 0,0,0,value);
        }
    }

    ctx.putImageData(id,0,0);

    var endTime = new Date().getTime();
    console.timeEnd("Map generated in: ");
    alert("Map generated in: " + (endTime - startTime) + "milliseconds");

});

function setPixel(imageData, x, y, r, g, b, a) {
    index = (x + y * imageData.width) * 4;
    imageData.data[index+0] = r;
    imageData.data[index+1] = g;
    imageData.data[index+2] = b;
    imageData.data[index+3] = a;
}

//This is a port of Ken Perlin's "Improved Noise"
///

//Originally from .html
//but the site appears to be down, so here is a mirror of it

//Converted from php to javascript by Christian Moe
//Patched the errors with code from here: .html

var PerlinNoise = function(seed) {
this._default_size = 64;
this.seed = seed;       

//Initialize the permutation array.
this.p = new Array(512);

this.permutation = [ 151,160,137,91,90,15,
131,13,201,95,96,53,194,233,7,225,140,36,103,30,69,142,8,99,37,240,21,10,23,
190, 6,148,247,120,234,75,0,26,197,62,94,252,219,203,117,35,11,32,57,177,33,
88,237,149,56,87,174,20,125,136,171,168, 68,175,74,165,71,134,139,48,27,166,
77,146,158,231,83,111,229,122,60,211,133,230,220,105,92,41,55,46,245,40,244,
102,143,54, 65,25,63,161, 1,216,80,73,209,76,132,187,208, 89,18,169,200,196,
135,130,116,188,159,86,164,100,109,198,173,186, 3,64,52,217,226,250,124,123,
5,202,38,147,118,126,255,82,85,212,207,206,59,227,47,16,58,17,182,189,28,42,
223,183,170,213,119,248,152, 2,44,154,163, 70,221,153,101,155,167, 43,172,9,
129,22,39,253, 19,98,108,110,79,113,224,232,178,185, 112,104,218,246,97,228,
251,34,242,193,238,210,144,12,191,179,162,241, 81,51,145,235,249,14,239,107,
49,192,214, 31,181,199,106,157,184, 84,204,176,115,121,50,45,127, 4,150,254,
138,236,205,93,222,114,67,29,24,72,243,141,128,195,78,66,215,61,156,180
];

for (var i=0; i < 256 ; i++) {
this.p[256+i] = this.p[i] = this.permutation[i];
}
};

PerlinNoise.prototype.noise = function(x,y,z,size) {

if (size == undefined) 
{
size = this._default_size;
}

//Set the initial value and initial size
var value           = 0.0; 
var initialSize     = size;

//Add finer and finer hues of smoothed noise together
while(size >= 1) 
{
value += this.smoothNoise(x / size, y / size, z / size) * size;
size /= 2.0;
}

//Return the result over the initial size
return value / initialSize;

};

//This function determines what cube the point passed resides in
//and determines its value.
PerlinNoise.prototype.smoothNoise = function(x, y, z){
//Offset each coordinate by the seed value
x += this.seed; 
y += this.seed; 
z += this.seed;

var orig_x = x;
var orig_y = y;
var orig_z = z;

var X = Math.floor(x) & 255,                  // FIND UNIT CUBE THAT
    Y = Math.floor(y) & 255,                  // CONTAINS POINT.
    Z = Math.floor(z) & 255;

x -= Math.floor(x);                                // FIND RELATIVE X,Y,Z
y -= Math.floor(y);                                // OF POINT IN CUBE.
z -= Math.floor(z);

var    u = this.fade(x),                                // COMPUTE FADE CURVES
      v = this.fade(y),                                // FOR EACH OF X,Y,Z.
      w = this.fade(z);

var A = this.p[X  ]+Y, AA = this.p[A]+Z, AB = this.p[A+1]+Z,      // HASH COORDINATES OF
   B = this.p[X+1]+Y, BA = this.p[B]+Z, BB = this.p[B+1]+Z;      // THE 8 CUBE CORNERS,

return this.lerp(w, this.lerp(v, this.lerp(u, this.grad(this.p[AA  ], x  , y  , z   ),  // AND ADD
           this.grad(this.p[BA  ], x-1, y  , z   )), // BLENDED
           this.lerp(u, this.grad(this.p[AB  ], x  , y-1, z   ),  // RESULTS
                   this.grad(this.p[BB  ], x-1, y-1, z   ))),// FROM  8
                   this.lerp(v, this.lerp(u, this.grad(this.p[AA+1], x  , y  , z-1 ),  // CORNERS
                           this.grad(this.p[BA+1], x-1, y  , z-1 )), // OF CUBE
                           this.lerp(u, this.grad(this.p[AB+1], x  , y-1, z-1 ),
                                   this.grad(this.p[BB+1], x-1, y-1, z-1 ))));
};

PerlinNoise.prototype.fade = function(t) { 
return t * t * t * ( ( t * ( (t * 6) - 15) ) + 10);
};

PerlinNoise.prototype.lerp = function(t, a, b) { 
//Make a weighted interpolaton between points
return a + t * (b - a); 
};

PerlinNoise.prototype.grad = function(hash, x, y, z) {
h = hash & 15;                      // CONVERT LO 4 BITS OF HASH CODE
u = h<8 ? x : y;                 // INTO 12 GRADIENT DIRECTIONS.
v = h<4 ? y : (h==12||h==14 ? x : z);

return ((h&1) == 0 ? u : -u) + ((h&2) == 0 ? v : -v);
};

PerlinNoise.prototype.scale = function(n) { 
return (1 + n)/2; 
};

function linear(int, s1, s2, t1, t2)
{
t = [t1, t2];
s = [s1, s2];

rangeS = s1 - s2;
rangeT = t1 - t2;

if((s1 < s2 && t1 > t2) || (s1>s2 && t1<t2))
{
        interpolated = ((int - s1) / rangeS*rangeT) + t1;
}
else
{
        interpolated = ((int - s1) / rangeS)*rangeT + t1;
}

if(interpolated > Math.max.apply(Math, t))
{
        interpolated = Math.max.apply(Math, t);
}

if(interpolated < Math.min.apply(Math, t))
{
        interpolated = Math.min.apply(Math, t);
}

return interpolated;

}
</script>
</body>
</html>

I get 33 ms on Chrome, and 1051ms on Firefox 24 Nightly

The results are inconsistent though. Sometimes the Nightly results is as fast as chrome...

Do you know why there is so much variation in this particular instance? I don't know enough about the theory of perlin noise to try optimizing the code, so don't know what to do.

I have written a map generator in javascript, using classical perlin noise scripts I have found in various places, to get the functionality I want. I have been working in chrome, and have not experienced any problems with the map. However, when I tested it in firefox, it was incredibly slow - almost hanging my system. It fared better in the nightly build, but still 30 times slower than Chrome.

You can find a test page of it here: http://jsfiddle/7Gq3s/

Here is the html code:

<!DOCTYPE html>

<html>
<head>
<title>PerlinMapTest</title>
</head>
<body>

<canvas id="map" width="100" height="100" style="border: 1px solid red">My Canvas</canvas>

<script src="//code.jquery./jquery-2.0.0.min.js"></script>
<script>
$(document).ready(function(){
    //Log time in two ways
    var startTime = new Date().getTime();
    console.time("Map generated in: ");

    var canvas = $("#map")[0];
    var ctx = canvas.getContext("2d");
    var id = ctx.createImageData(canvas.width, canvas.height); 

    var noiseMap = new PerlinNoise(500);

    var startx = 0;
    var starty = 0;

    var value = 0;

    for(var i = startx; i < canvas.width; i++){
        for(var j = starty; j < canvas.height; j++){
            value = noiseMap.noise(i,j, 0, 42);
            value = linear(value,-1,1,0,255);
            setPixel(id, i, j, 0,0,0,value);
        }
    }

    ctx.putImageData(id,0,0);

    var endTime = new Date().getTime();
    console.timeEnd("Map generated in: ");
    alert("Map generated in: " + (endTime - startTime) + "milliseconds");

});

function setPixel(imageData, x, y, r, g, b, a) {
    index = (x + y * imageData.width) * 4;
    imageData.data[index+0] = r;
    imageData.data[index+1] = g;
    imageData.data[index+2] = b;
    imageData.data[index+3] = a;
}

//This is a port of Ken Perlin's "Improved Noise"
//http://mrl.nyu.edu/~perlin/noise/

//Originally from http://therandomuniverse.blogspot./2007/01/perlin-noise-your-new-best-friend.html
//but the site appears to be down, so here is a mirror of it

//Converted from php to javascript by Christian Moe
//Patched the errors with code from here: http://asserttrue.blogspot.fi/2011/12/perlin-noise-in-javascript_31.html

var PerlinNoise = function(seed) {
this._default_size = 64;
this.seed = seed;       

//Initialize the permutation array.
this.p = new Array(512);

this.permutation = [ 151,160,137,91,90,15,
131,13,201,95,96,53,194,233,7,225,140,36,103,30,69,142,8,99,37,240,21,10,23,
190, 6,148,247,120,234,75,0,26,197,62,94,252,219,203,117,35,11,32,57,177,33,
88,237,149,56,87,174,20,125,136,171,168, 68,175,74,165,71,134,139,48,27,166,
77,146,158,231,83,111,229,122,60,211,133,230,220,105,92,41,55,46,245,40,244,
102,143,54, 65,25,63,161, 1,216,80,73,209,76,132,187,208, 89,18,169,200,196,
135,130,116,188,159,86,164,100,109,198,173,186, 3,64,52,217,226,250,124,123,
5,202,38,147,118,126,255,82,85,212,207,206,59,227,47,16,58,17,182,189,28,42,
223,183,170,213,119,248,152, 2,44,154,163, 70,221,153,101,155,167, 43,172,9,
129,22,39,253, 19,98,108,110,79,113,224,232,178,185, 112,104,218,246,97,228,
251,34,242,193,238,210,144,12,191,179,162,241, 81,51,145,235,249,14,239,107,
49,192,214, 31,181,199,106,157,184, 84,204,176,115,121,50,45,127, 4,150,254,
138,236,205,93,222,114,67,29,24,72,243,141,128,195,78,66,215,61,156,180
];

for (var i=0; i < 256 ; i++) {
this.p[256+i] = this.p[i] = this.permutation[i];
}
};

PerlinNoise.prototype.noise = function(x,y,z,size) {

if (size == undefined) 
{
size = this._default_size;
}

//Set the initial value and initial size
var value           = 0.0; 
var initialSize     = size;

//Add finer and finer hues of smoothed noise together
while(size >= 1) 
{
value += this.smoothNoise(x / size, y / size, z / size) * size;
size /= 2.0;
}

//Return the result over the initial size
return value / initialSize;

};

//This function determines what cube the point passed resides in
//and determines its value.
PerlinNoise.prototype.smoothNoise = function(x, y, z){
//Offset each coordinate by the seed value
x += this.seed; 
y += this.seed; 
z += this.seed;

var orig_x = x;
var orig_y = y;
var orig_z = z;

var X = Math.floor(x) & 255,                  // FIND UNIT CUBE THAT
    Y = Math.floor(y) & 255,                  // CONTAINS POINT.
    Z = Math.floor(z) & 255;

x -= Math.floor(x);                                // FIND RELATIVE X,Y,Z
y -= Math.floor(y);                                // OF POINT IN CUBE.
z -= Math.floor(z);

var    u = this.fade(x),                                // COMPUTE FADE CURVES
      v = this.fade(y),                                // FOR EACH OF X,Y,Z.
      w = this.fade(z);

var A = this.p[X  ]+Y, AA = this.p[A]+Z, AB = this.p[A+1]+Z,      // HASH COORDINATES OF
   B = this.p[X+1]+Y, BA = this.p[B]+Z, BB = this.p[B+1]+Z;      // THE 8 CUBE CORNERS,

return this.lerp(w, this.lerp(v, this.lerp(u, this.grad(this.p[AA  ], x  , y  , z   ),  // AND ADD
           this.grad(this.p[BA  ], x-1, y  , z   )), // BLENDED
           this.lerp(u, this.grad(this.p[AB  ], x  , y-1, z   ),  // RESULTS
                   this.grad(this.p[BB  ], x-1, y-1, z   ))),// FROM  8
                   this.lerp(v, this.lerp(u, this.grad(this.p[AA+1], x  , y  , z-1 ),  // CORNERS
                           this.grad(this.p[BA+1], x-1, y  , z-1 )), // OF CUBE
                           this.lerp(u, this.grad(this.p[AB+1], x  , y-1, z-1 ),
                                   this.grad(this.p[BB+1], x-1, y-1, z-1 ))));
};

PerlinNoise.prototype.fade = function(t) { 
return t * t * t * ( ( t * ( (t * 6) - 15) ) + 10);
};

PerlinNoise.prototype.lerp = function(t, a, b) { 
//Make a weighted interpolaton between points
return a + t * (b - a); 
};

PerlinNoise.prototype.grad = function(hash, x, y, z) {
h = hash & 15;                      // CONVERT LO 4 BITS OF HASH CODE
u = h<8 ? x : y;                 // INTO 12 GRADIENT DIRECTIONS.
v = h<4 ? y : (h==12||h==14 ? x : z);

return ((h&1) == 0 ? u : -u) + ((h&2) == 0 ? v : -v);
};

PerlinNoise.prototype.scale = function(n) { 
return (1 + n)/2; 
};

function linear(int, s1, s2, t1, t2)
{
t = [t1, t2];
s = [s1, s2];

rangeS = s1 - s2;
rangeT = t1 - t2;

if((s1 < s2 && t1 > t2) || (s1>s2 && t1<t2))
{
        interpolated = ((int - s1) / rangeS*rangeT) + t1;
}
else
{
        interpolated = ((int - s1) / rangeS)*rangeT + t1;
}

if(interpolated > Math.max.apply(Math, t))
{
        interpolated = Math.max.apply(Math, t);
}

if(interpolated < Math.min.apply(Math, t))
{
        interpolated = Math.min.apply(Math, t);
}

return interpolated;

}
</script>
</body>
</html>

I get 33 ms on Chrome, and 1051ms on Firefox 24 Nightly

The results are inconsistent though. Sometimes the Nightly results is as fast as chrome...

Do you know why there is so much variation in this particular instance? I don't know enough about the theory of perlin noise to try optimizing the code, so don't know what to do.

Share Improve this question asked May 18, 2013 at 14:29 LongIntLongInt 1,7792 gold badges16 silver badges30 bronze badges 8
  • Ok, I'm getting very inconsistent results... Right now firefox is returning the map in almost the same time as chrome - might be a ram issue? – LongInt Commented May 18, 2013 at 14:40
  • You're missing a lot of var declarations for local variables. – Pointy Commented May 18, 2013 at 14:41
  • Do you mean in the functions? I will add those, just in case. Could they have any performance issues? – LongInt Commented May 18, 2013 at 14:43
  • YES. A variable used declared inside a function without a `var statement pollutes the global namespace. – DougM Commented May 18, 2013 at 14:46
  • 1 BTW, another typical JS optimization is to avoid objects and their this parameter. Notice that once initialized, perlin noise is essentially just a single function - so you could instead use local variables and a locally declared function closing over them, rather than an object. This is often slightly faster, and it's better encapsulation to boot as all those internal variables remain local. – Eamon Nerbonne Commented May 18, 2013 at 15:03
 |  Show 3 more ments

1 Answer 1

Reset to default 14

I have found the culprit. The slowdown occurs when I have Firebug enabled. That extension must weigh it down.

发布评论

评论列表(0)

  1. 暂无评论