应用于前端的自然断点法:wasm应用示例
某月某日,虾神本虾接到了这样一个需求,前端获取数据之后,在渲染之前,要对数据进行分类,分类的方法可以选择常用几种,例如等距法、自然断点法、标准差分类法等……
问:为什么要在前端?这种功能不是一般都是在后台实现的么?虽然JavaScript号称能够重写一切,但是这种写算法的事情,本身不是它的领域啊。
答:前端的数据来源可能是很多不同的系统,组合之后得到的,不一定来源于一个系统或者一个库表,如果让后台做,前后台之间的数据传递就太繁杂了,所以甲方想在前端把这事情给做了,再说:
所以呢,虾神你不是做算法的么?来来来,帮忙给写一个……
虽然我手动写过自然断点法的计算方法,但是我不会JavaScript啊……你要不让我先花两个月,学习一下JavaScript?
虽然自然断点的算法还是比较简单的,我也写过:(见以前的文章:) 但是对于这种造轮子的活动,我一般是努力拒绝的:
所以我果断的把Python
推荐给了他,毕竟Python里面已经有好几个身经百战的自然断点法的实现了,然后就毫无意外被拍回来了……
好吧,作为码农,不能说不行……我虽然不会写JavaScript,但是我会写Rust啊……好巧不巧,Rust写的wasm,就正好能在前端用。
首先简单介绍一下wasm
wasm: WebAssembly的简写,是一种新型的浏览器端代码:
用JavaScript的运行原理来说,它实际上是在JS的编译器中动态编译,然后在JS的VM中执行的,那么wasm可以让C/C++/Rust一类的高性能编译语言,转换成一种称之为IR的虚拟指令集,在需要的时候,在转换成JS VM可以运行的机器指令:
这种IR的编译指令,能够最大化的利用客户端的底层(如CPU\内存\显卡等)硬件,所以很多时候,比原生态的JavaScript性能更高。
2019年的时候,wasm就已经正式成为了W3C标准,成为了Web开发的“第四门语言”
有关wasm的其他介绍,大家有兴趣的可以查阅其他资料,反正一句话:这玩意儿就是一个可以运行在前端浏览器上的编译级语言功能。
所以,我们就可以利用一些Rust写的东西,编译成JS可以用的脚本了。
秉承着有的轮子,我们就不用自己去造的原则,首先我们去看看Rust的官方仓库crates.io里面,有没有我们需要的东西:
很快,我告诉你,真的很快啊,就让我找到了这个东西:
然后就简单了——
我们直接去全球最大的同性交友网站gayhub……阿不,github,把这个包clone下来(或者你直接下载zip,然后解压也行),因为作者已经把所有的wasm相关代码都写好,所以你只需要运行编译打包就可以了,注意,官方文档上说-features如下:
wasm-pack build --release -- -features js
在我这里最新的Rust版本里面已经不好使了,直接编译为web就行,命令行如下:
wasm-pack build --release target web
然后看着cargo自动安装一堆东西,自动下载一堆东西,自动编译一堆东西,直到显示:
然后可以了,我们可以看见,在工程根目录下,会得到一个pkg包,里面有我们需要的wasm文件:
其中,index.html是调用的示例文件,搞前端的同学一眼就明白:
然后我们启动一个小http服务器,就可以看见效果了,我这里启动的是python自带的http服务器:
打开浏览器,F12看console:
我们可以简单解析一下这个工程(如果你Rust没有基础也没有兴趣,就可以跳过这一部分了),首先在src/jenks.rs文件中,写了一个函数,做了算法的实现:
//src/jenks.rs pub fn get_jenks_breaks<T: ToPrimitive>(num_bins: usize, data: &[T]) -> Vec<f64> { //jenks的算法见文章开头的链接,这里不解释了 //这里的就是用Rust把jenks的实现过程写了一遍。 }
这个函数在Rust工程里面是可以直接用的了,我们可以在下面写一个测试方法,来看看效果:
但是我们要在前端调用它,所以必须还要封装成wasm,所以还需要一个对外封装的接口: src/wasm.rs: 前面的#[wasm_bindgen]
特性,就是声明该方法,是一个wasm的绑定,这样这个方法编译之后,就可以被前端调用了。
#[wasm_bindgen] pub fn get_jenks_breaks(no_bins: usize, data: &[f64]) -> Box<[f64]> { let breaks = crate::jenks::get_jenks_breaks(no_bins, data); breaks.into_boxed_slice() }
接下去,编译完成之后,会生成.wasm文件,这个文件是一个二进制的文件:
也就是我们前面说的IR(中间过程)文件。
然后前端需要调用的话,还得将它给调度到JS VM里面去,所以Rust的wasm工具包还会编译出方便前端调用的js/ts接口:
这样,你就得到了一个标准的Javascript接口,那么前端同学要调用,就没有任何的难度了……
至此,一个用后台高性能编译型语言编写的算法,就完成了前端封装和调用。
如果你有兴趣研究一下webassembly的话,这个例子我觉得可以当成hello world还好懂……起码你不用写一行代码,而具体里面的技术细节,有兴趣的同学可以查阅:
https://developer.mozilla.org/zh-CN/docs/WebAssembly
打完收工……