The pending question Is Rust/Wasm fast enough to even bother when comparing to Javascript? has now yet another biased benchmark. I've decided to take one of my Julia/Mandelbrot demos, benchmark it "as-is" and then, port to Rust and compare.
The HTML/Javascript source is short enough to post it here, the original demo code has some additional features (zooming) which are not present here
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Mandelbrot</title> <script type="text/javascript"> var cx = 0, cy = 0, kat1 = 0, kat2 = 1; var WJulia = 1024; var HJulia = 1024; var frame = 0; var startDate; var contextJulia; var contextSmall; var pixJulia; var imgdJulia; var iStart; var iEnd; var jStart; var jEnd; var iDelta; var jDelta; function setInitialScale() { iStart = -1.92; iEnd = 1.92; jStart = -1.92; jEnd = 1.92; iDelta = (iEnd-iStart)/1024; jDelta = (jEnd-jStart)/1024; startDate = new Date(); } function SetupMandel() { var elemJulia = document.getElementById('JuliaCanvas'); if (elemJulia && elemJulia.getContext) { contextJulia = elemJulia.getContext('2d'); if (contextJulia) { imgdJulia = contextJulia.createImageData(WJulia, HJulia); pixJulia = imgdJulia.data; } } setInitialScale(); /* start */ requestAnimationFrame( LoopMandel ); } function LoopMandel() { kat1 += 0.0021; kat2 += 0.0039; cx = .681 * Math.sin(kat1); cy = .626 * Math.cos(kat2); frame++; document.getElementById('fps').innerHTML = `FPS: ${1000*frame/(new Date()-startDate)}`; RysujMandel(); requestAnimationFrame( LoopMandel ); } /* tworzenie bitowego obrazu */ function RysujMandel() { /* obliczenia */ var wi, wj; var i, j; var iterations = 255; var px = 0; for (i = iStart, wi = 0; wi < 1024; i += iDelta, wi++) { var py = 0; for (j = jStart, wj = 0; wj < 1024; j += jDelta, wj++) { var c = 0; var x = cx; var y = cy; while (((x*x + y*y) < 4) && (c < iterations)) { [x, y] = [x * x - y * y + i, 2 * x * y + j]; c++; } SetPixelColor( pixJulia, (py * WJulia + px) << 2, 255, 255-c, 255-c, 255 - (c/2) ); py++; } px++; } /* kopiowanie bitowego obrazu do context/canvas */ contextJulia.putImageData(imgdJulia, 0, 0); } function SetPixelColor(pix,offs, a, r, g, b) { pix[offs++] = r; pix[offs++] = g; pix[offs++] = b; pix[offs] = a; } window.addEventListener( 'load', SetupMandel ); </script> </head> <body> <span id="fps" style='display:block'></span> <canvas id="JuliaCanvas" width="1024" height="1024"> </canvas> </body> </html>
When run on my machine and the Edge Browser, it makes ~10-11 frames per second. Not bad assuming 1024x1024 image and 255 iterations.
Same code ported to Rust and compiled with wasm-pack
// https://rustwasm.github.io/wasm-bindgen/examples/dom.html use wasm_bindgen::prelude::*; use wasm_bindgen::Clamped; use web_sys::{CanvasRenderingContext2d, ImageData}; /* tworzenie bitowego obrazu */ #[wasm_bindgen] pub fn mandel( ctx: &CanvasRenderingContext2d, width: u32, height: u32, cx: f64, cy: f64) -> Result<(), JsValue> { let i_start = -1.92; let i_end = 1.92; let j_start = -1.92; let j_end = 1.92; let i_delta = (i_end-i_start)/width as f64; let j_delta = (j_end-j_start)/height as f64; /* obliczenia */ let iterations = 255; let mut pix_julia = Vec::with_capacity( usize::try_from(width * height).unwrap() ); let mut j = j_start; for _wj in 0..height { let mut i = i_start; for _wi in 0..width { let mut c: u8 = 0; let mut x = cx; let mut y = cy; while ( (x*x + y*y) < 4.0) && (c < iterations) { let _tx = x; x = x * x - y * y + i; y = 2.0 * _tx * y + j; c += 1; } pix_julia.push( 255-c ); pix_julia.push( 255-c ); pix_julia.push( 255-(c/2) ); pix_julia.push( 255 ); i += i_delta; } j += j_delta; } let data = ImageData::new_with_u8_clamped_array_and_sh(Clamped(&pix_julia), width, height)?; ctx.put_image_data(&data, 0.0, 0.0) }
The cargo.tomlwas
[package] name = "hello-wasm" version = "0.1.0" edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [lib] crate-type = ["cdylib"] [dependencies] wasm-bindgen = "0.2.87" [dependencies.web-sys] version = "0.3.4" features = [ 'Document', 'Element', 'HtmlElement', 'Node', 'Window', 'ImageData', 'CanvasRenderingContext2d' ] [profile.release] opt-level = 3 lto = true [package.metadata.wasm-pack.profile.release] wasm-opt = ["-O4", "--enable-mutable-globals"]
and the HTML
<!doctype html> <html lang="en-US"> <head> <meta charset="utf-8" /> <title>Mandelbrot</title> </head> <body> <script type="module"> import init, { mandel } from "./pkg/hello_wasm.js"; (async function() { await init(); SetupMandel(); })(); var cx = 0, cy = 0, kat1 = 0, kat2 = 1; var WJulia = 1024; var HJulia = 1024; var frame = 0; var startDate; var contextJulia; var contextSmall; var pixJulia; var imgdJulia; function SetupMandel() { var elemJulia = document.getElementById('JuliaCanvas'); if (elemJulia && elemJulia.getContext) { contextJulia = elemJulia.getContext('2d'); if (contextJulia) { imgdJulia = contextJulia.createImageData(WJulia, HJulia); pixJulia = imgdJulia.data; } } startDate = new Date(); /* start */ requestAnimationFrame( LoopMandel ); } function LoopMandel() { kat1 += 0.0021; kat2 += 0.0039; cx = .681 * Math.sin(kat1); cy = .626 * Math.cos(kat2); frame++; document.getElementById('fps').innerHTML = `FPS: ${1000*frame/(new Date()-startDate)}`; mandel(contextJulia, WJulia, HJulia, cx, cy); requestAnimationFrame( LoopMandel ); } </script> <span id="fps" style='display: block'></span> <canvas id="JuliaCanvas" width="1024" height="1024"> </canvas> </body> </html>
And the result? Similar, it also makes 10-11 frames per second. Additional tests (different resolution and number of iterations) and possible optimizations can be applied here.