Last active
December 18, 2015 02:29
-
-
Save boutell/5711141 to your computer and use it in GitHub Desktop.
Mandelbrot Set with core routine in asm.js. Does not appear to be any faster than not using asm.js in Firefox 22 beta, even though the console reports asm.js was successfully compiled.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!doctype html> | |
<html> | |
<head> | |
<title>Mandelbrot set with asm.js</title> | |
<meta charset="utf-8" /> | |
</head> | |
<body> | |
<canvas id="c"></canvas> | |
<div id="status"></div> | |
<script> | |
b = document.body; | |
c = document.getElementsByTagName('canvas')[0]; | |
a = c.getContext('2d'); | |
document.body.clientWidth; // fix bug in webkit: http://qfox.nl/weblog/218 | |
// Mandelbrot plotter with asm.js-optimized core routine, | |
// see ApointModule below. Apologies for the ugliness of the rest | |
// of the code, this was originally a JS1K contest submission (: | |
// This is revision 4. In addition to a tight inner loop with no | |
// setTimeout calls, this version uses canvas.putImageData for | |
// much better performance, so calculation is once again the | |
// limiting factor. | |
// Although it is much faster now, asm.js still does not make it | |
// faster. And it is still faster in Chrome. | |
// Firefox 22 beta says: | |
// | |
// Warning: Error: Successfully compiled asm.js code | |
// | |
// In the console (I had to tweak quite a bit to get there). | |
// | |
// Is it possible the standard optimizer is able to figure out | |
// so much about this code that 'use asm' is an unnecessary hint? | |
c.width = 700; | |
c.height = 400; | |
s = c.style; | |
s.position = 'absolute'; | |
w = window; | |
k = w.innerWidth / 2 - 350; | |
t = w.innerHeight / 2 - 200; | |
s.left = k + 'px'; | |
s.top = t + 'px'; | |
n = document; | |
d = n.createElement('div'); | |
d.innerHTML = "Click: in Shift-Click: out<form id='f'><input size=40 id='j'/></form>"; | |
s = d.style; | |
s.marginTop = '20px'; | |
s.textAlign = 'center'; | |
b.appendChild(d); | |
j = ge('j'); | |
function ge(s) | |
{ | |
return n.getElementById(s); | |
} | |
cx = -0.75; | |
cy = to = 0; | |
m = 1.0; | |
function microtime() | |
{ | |
var now = new Date().getTime() / 1000; | |
return now; | |
} | |
function z() | |
{ | |
var start = microtime(); | |
j.value = cx + ',' + cy + ',' + m; | |
var py = 0; | |
apoint.processImage(); | |
var py; | |
var px; | |
var i = 0; | |
var p = 0; | |
var pixelsObject = a.getImageData(0, 0, 700, 400); | |
var pixels = pixelsObject.data; | |
for (py = 0; (py < 400); py++) { | |
for (px = 0; (px < 700); px++) { | |
var iterations = image[i++]; | |
pixels[p++] = (iterations & 7) * 32; | |
pixels[p++] = (iterations & 31) * 8; | |
pixels[p++] = (iterations & 63) * 4; | |
pixels[p++] = 255; | |
// a.fillStyle = 'rgb(' + r + ',' + g + ',' + b + ')'; | |
// a.fillRect(px, py, 1, 1); | |
} | |
} | |
a.putImageData(pixelsObject, 0, 0); | |
end = microtime(); | |
document.getElementById('status').innerHTML = (end - start); | |
} | |
function ApointModule(stdlib, foreign, heap) { | |
// "use asm"; | |
var m = 0.0; | |
var cx = 0.0; | |
var cy = 0.0; | |
var image = new stdlib.Int32Array(heap); | |
function setParameters(mArg, cxArg, cyArg) { | |
mArg = +mArg; | |
cxArg = +cxArg; | |
cyArg = +cyArg; | |
m = mArg; | |
cx = cxArg; | |
cy = cyArg; | |
} | |
function tx(x) { | |
x = +x; | |
return +(m * (x / 200.0 - 1.75) + cx); | |
} | |
function ty(y) { | |
y = +y; | |
return +(m * (y / 200.0 - 1.0) + cy); | |
} | |
function point(px, py) { | |
// type the parameters as floats via the +n annotation | |
px = +px; | |
py = +py; | |
// local variables are typed by the constant value we assign to them | |
var ox = 0.0; | |
var oy = 0.0; | |
var x = 0.0; | |
var y = 0.0; | |
var i = 0; | |
var xt = 0.0; | |
ox = tx(px); | |
oy = ty(py); | |
while (1) { | |
if (x*x + y*y >= 4.0) { | |
break; | |
} | |
if ((i|0) >= 767) { | |
break; | |
} | |
xt = x*x - y*y + ox; | |
y = 2.0*x*y + oy; | |
x = xt; | |
// Yes asm.js can be wacky! This is i++; | |
i = ((i|0) + 1)|0; | |
} | |
return i|0; | |
} | |
function processImage() { | |
var p = 0; | |
var y = 0.0; | |
var x = 0.0; | |
while (y < 400.0) { | |
x = 0.0; | |
while (x < 700.0) { | |
// Shift right the correct number of times for | |
// dividing by 4 (asm.js is actually checking for this) | |
image[p >> 2] = point(x, y)|0; | |
x = x + 1.0; | |
// sizeof int is 4 | |
p = ((p|0) + 4) | 0; | |
} | |
y = y + 1.0; | |
} | |
} | |
return { | |
setParameters: setParameters, | |
processImage: processImage, | |
tx: tx, | |
ty: ty | |
}; | |
} | |
// We really need 700x400x4 bytes, but asm.js insists the | |
// heap size be a power of 2 | |
var heap = new ArrayBuffer(1024 * 1024 * 2); | |
// Our view into it (asm.js constructs its own view) | |
var image = new Int32Array(heap); | |
var apoint = ApointModule(window, {}, heap); | |
apoint.setParameters(m, cx, cy); | |
z(); | |
c.onclick = function(e) { | |
cx = apoint.tx(e.clientX - k); | |
cy = apoint.ty(e.clientY - t); | |
m *= (e.shiftKey ? 2 : 0.5); | |
apoint.setParameters(m, cx, cy); | |
z(); | |
}; | |
ge('f').onsubmit = function(e) { | |
p = j.value.split(','); | |
if (p.length) | |
{ | |
cx = p[0] - 0; | |
cy = p[1] - 0; | |
m = p[2] - 0; | |
} | |
z(); | |
return false; | |
}; | |
</script> | |
</body> | |
</html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
C version, less than 2x faster, which means standard JS optimization is awfully good now: https://gist.github.com/boutell/5726634