- 作者:老汪软件技巧
- 发表时间:2024-08-21 15:03
- 浏览量:
引言
在现代 Web 开发中,将高性能计算和智能分析集成到前端应用中已经成为一种趋势。本文将介绍在超大型远洋航运颗/货船舶管理系统中,借助于 Rust 和 AI 技术实现 Web 侧 端智能数据分析处理海量传感器数据进行可视化界面的渲染工作,通过一系列的数据清洗、转换和分析,提升用户体验。
背景数据分析与预处理的需求
在现代 Web 应用中,特别是在数据密集型的应用场景下,处理和展示海量数据是一项具有挑战性的任务。传统的前端技术堆栈,如 JavaScript 和框架库,虽然能提供基本的数据处理功能,但在面对复杂的数据分析任务时可能显得力不从心。这些任务包括数据清洗、转换、聚合、以及实时分析等。
随着数据规模的增大和分析需求的复杂化,前端应用需要能够高效地处理大量数据,同时保持良好的用户体验。例如,电商平台需要实时处理用户行为数据来提供个性化推荐,社交媒体应用需要分析用户生成内容以进行情感分析,金融应用则需要实时处理市场数据以进行预测和决策支持。
技术挑战
在浏览器环境中进行复杂的数据处理面临几个主要挑战:
Rust 与 WebAssembly 的优势
为了应对这些挑战,Rust 和 WebAssembly (WASM) 的组合提供了一种解决方案。Rust 是一种系统编程语言,具有高效的内存管理和高性能的计算能力。WebAssembly 是一种可以在浏览器中运行的低级字节码格式,它允许开发者用非 JavaScript 语言编写代码,并将其编译为可以在浏览器中执行的高效二进制格式。
主要脉络
熟悉或者已经阅读过 Mdn 上面关于如何实现“Compiling from Rust to WebAssembly”的小伙伴可以跳过前面这一部分。
环境准备
无需过多心理压力,本文中的代码会尽可能得将注释和用意阐述清晰完整,纵使对于 Rust 这门编程语言一窍不通的读者也不会有过多的阅读障碍。
首先我们需要安装 Rust 和 WebAssembly 工具链:
# 安装 Rust
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
# 安装 wasm-pack
cargo install wasm-pack
# add WebAssembly target
rustup target add wasm32-unknown-unknown
此处对于不同操作系统的 PC 如何安装 Rust 可以参考…
wasm-pack,由于我们需要打包工具,所有这里引出了一个额外的工具--wasm-pack,有了 wasm-pack,我们可以将 rust code 编译为 WebAssembly,供给 WEB 端进行调用;
Rust 项目初始化
接下来我们在命令行输入类似于如下的命令:
cargo new --lib your-rust-project
此处为了方面演示,使用了 cargo new --lib rust-social-emotion-analysis 这个名称作为 Rust 项目根目录在 cargo new --lib your-rust-project 命令执行过后,我们会看到本地多出了以下几个文件:
├── Cargo.toml
└── src
└── lib.rs
这里我们可以使用 Cargo.toml 对构建进行配置,Cargo.toml 类似于前端工程的 package.json 文件。
cd src,进入到 src 目录中之后,我们会发现 lib.rs 内部的几行代码:
#[cfg(test)]
mod tests {
#[test]
fn it_works() {
assert_eq!(2 + 2, 4);
}
}
这部分代码直接删除即可,然后替换为我们需要的 demo code:
// 引入 wasm_bindgen 库,这允许 Rust 代码与 WebAssembly 的 JavaScript 进行无缝交互
use wasm_bindgen::prelude::*;
// 使用 #[wasm_bindgen] 属性声明外部 JavaScript 函数
// 这里声明了一个名为 `alert` 的函数,该函数会在浏览器中弹出alert框
#[wasm_bindgen]
extern {
// 外部函数 `alert`,用于显示alert对话框,接受一个字符串参数
// 在浏览器环境中,这会弹出一个alert框显示传入的消息
pub fn alert(s: &str);
}
// 使用 #[wasm_bindgen] 属性公开 Rust 函数,使其可被 JavaScript 代码调用
// 这个函数将用于向用户打招呼
#[wasm_bindgen]
pub fn greet(name: &str) {
// 使用 Rust 的 `format!` 宏创建一个格式化的greet字符串
// `format!` 宏将 `name` 参数插入到字符串中,生成类似 "Hello, John!" 的greet信息
// 调用外部的 `alert` 函数来弹出显示该greet信息
alert(&format!("Hello, {}!", name));
}
这里之所以引入 wasm_bindgen 这一 Crate 的原因是 wasm-pack 需要借助于 wasm-bindgen 来生成桥接 Javascript 与 Rust 的代码,进而使得我们可以在 JS 中调用 Rust API 或者 Rust 函数。
配置 Cargo.toml
打开项目的 cargo.toml 文件按照如下进行配置:
[package]
name = "hello-wasm"
version = "0.1.0"
authors = ["Your Name " ]
description = "A sample project with wasm-pack"
license = "MIT/Apache-2.0"
repository = "https://github.com/yourgithubusername/hello-wasm"
edition = "2018"
[lib]
crate-type = ["cdylib"]
[dependencies]
wasm-bindgen = "0.2"
通过以上代码,首先我们添加了[package],其次通过[lib]这一部分是为了告知 Rust 我们想要打包成 cdylib 的版本。最后的[dependencies]是为了让 Cargo 知道我们依赖于 wasm-bindgen 0.2.x 版本。
构建
这里我们通过以下一段简短的命令进行构建:
wasm-pack build --target web
根据 Mozilla Hacks 上面的描述,简而言之以上代码大致做了以下五件事:
截至目前为止,我们的 Rust 项目目录基本如下:
├── Cargo.lock
├── Cargo.toml
├── index.html <-- 新建一个 index.html 文件放置于Rust工程的根目录
├── pkg
│ ├── hello_wasm.d.ts
│ ├── hello_wasm.js
│ ├── hello_wasm_bg.wasm
│ ├── hello_wasm_bg.wasm.d.ts
│ └── package.json
├── src
│ └── lib.rs
└── target
├── CACHEDIR.TAG
├── release
└── wasm32-unknown-unknown
在根目录新建一个 index.html 文件,并在其中编写如下代码:
html>
<html lang="en-US">
<head>
<meta charset="utf-8" />
<title>hello-wasm exampletitle>
head>
<body>
<script type="module">
import init, { greet } from "./pkg/hello_wasm.js";
init().then(() => {
greet("WebAssembly");
});
script>
body>
html>
OK,现在我们可以借助于 http-server 启动一个本地的静态资源服务,浏览页面效果:
npx http-server
至此,我们的第一步,即--“将 Rust Code 编译为 WASM,编译为 WebAssembly 并在 WEB 端集成”基本实现完成了。这部分为了方面演示省略了很多譬如压缩 Rust 转化为 WASM 文件的体积、以及老生常谈的 Rust 所有权和借用系统。
接下来我们看一下如何借助于 rust 实现海量数据的预处理与数据分析工作,以满足我们页面可视化渲染相关的需求。
大批量数据的预处理与分析
这一部分我们将重点讨论如何使用 Rust 的并发特性来提高数据处理的效率,并将结果集成到 WebAssembly 中以用于浏览器端的数据分析。
引入所需的库
// 引入 wasm-bindgen 库的预处理模块,用于与 JavaScript 交互
use wasm_bindgen::prelude::*;
定义数据结构
// 定义一个结构体来表示传感器数据项
// 结构体包含一个 id 和一个值,分别表示数据项的唯一标识符和传感器读取的值
#[derive(Clone, Debug)]
struct SensorData {
id: u32,
value: f64,
}
基础数据预处理
// 通过一个函数来对数据进行预处理,例如筛选出值大于某个阈值的数据
#[wasm_bindgen]
pub fn preprocess_sensor_data(data: &[SensorData], threshold: f64) -> Vec {
// 使用迭代器和过滤方法筛选出符合条件的数据
data.iter()
.filter(|item| item.value > threshold)
.cloned()
.collect()
}
并发数据预处理
为了提高处理海量数据的效率,我们可以利用 Rust 的并发特性。使用 rayon 库,可以轻松实现数据的并行处理。
首先,添加 rayon 依赖到 Cargo.toml:
[dependencies]
wasm-bindgen = "0.2"
rayon = "1.5" // 添加 rayon 依赖
接下来,修改数据预处理函数以利用并发处理:
// 引入 rayon 库
use rayon::prelude::*;
// 并发数据预处理逻辑
#[wasm_bindgen]
pub fn parallel_preprocess_sensor_data(data: &[SensorData], threshold: f64) -> Vec {
// 使用 rayon 的并行迭代器来加速数据预处理
data.par_iter() // 使用并行迭代器
.filter(|item| item.value > threshold)
.cloned()
.collect()
}
这里主要做了两件事:
数据分析
基础数据分析:
// 对数据进行分析,例如计算数据的平均值
#[wasm_bindgen]
pub fn analyze_sensor_data(data: &[SensorData]) -> f64 {
// 计算总和
let sum: f64 = data.iter().map(|item| item.value).sum();
// 计算平均值
sum / data.len() as f64
}
并发数据分析
对于大数据集的并发分析,特别是需要进行复杂计算时,利用并发可以显著提高性能:
// 引入 rayon 库
use rayon::prelude::*;
// 并发分析计算数据的标准差
#[wasm_bindgen]
pub fn parallel_analyze_sensor_data(data: &[SensorData]) -> f64 {
// 使用 rayon 的并行迭代器来加速数据分析
let mean = analyze_sensor_data(data);
let variance: f64 = data.par_iter() // 使用并行迭代器
.map(|item| (item.value - mean).powi(2)) // 计算每个数据点与均值的平方差
.sum(); // 汇总平方差
// 计算标准差
(variance / data.len() as f64).sqrt()
}
与前端工程的 Webpack 集成
在项目的 webpack.config.js 类似的配置文件中,添加 WebAssembly 支持:
const path = require("path");
const WasmPackPlugin = require("@wasm-tool/wasm-pack-plugin");
module.exports = {
configureWebpack: {
plugins: [
new WasmPackPlugin({
crateDirectory: path.resolve(__dirname, "rust_data_processing"),
}),
],
experiments: {
asyncWebAssembly: true,
},
},
};
在 Vue 项目中的调用:
<template>
<div>
<h1>数据分析h1>
<p>预处理后的数据: {{ processedData }}p>
<p>分析结果: {{ analysisResult }}p>
div>
template>
<script lang="ts">
import { defineComponent, onMounted, ref } from 'vue';
import init, { parallel_preprocess_sensor_data, parallel_analyze_sensor_data } from 'rust_data_processing';
export default defineComponent({
setup() {
const processedData = ref<SensorData[]>([]);
const analysisResult = refnull >(null);
onMounted(async () => {
await init(); // 初始化 WebAssembly 模块
const rawData: SensorData[] = [/* 海量传感器数据 */];
const threshold = 10.0;
// 预处理数据
processedData.value = parallel_preprocess_sensor_data(rawData, threshold);
// 分析数据
analysisResult.value = parallel_analyze_sensor_data(processedData.value);
});
return { processedData, analysisResult };
},
});
script>
<style scoped>
/* 样式 */
style>
端侧 AI 能力探索
这一部分我们主要探索如何编写数据处理代码
定义数据结构和实现数据预处理功能,例如数据标准化和特征提取:
// 引入必要的库
use wasm_bindgen::prelude::*;
use ndarray::Array2;
use ndarray_rand::RandomExt;
use ndarray_rand::rand_distr::Uniform;
// 定义数据结构
#[derive(Debug, Clone)]
pub struct SensorData {
id: u32,
value: f64,
}
// 实现数据预处理函数
#[wasm_bindgen]
pub fn preprocess_sensor_data(data: &[SensorData], threshold: f64) -> Vec {
// 过滤掉值低于阈值的数据
data.iter()
.filter(|item| item.value > threshold)
.cloned()
.collect()
}
K-Means 聚类是一种广泛使用的无监督学习算法,用于将数据分组为 K 个簇。一个 Rust 实现的 K-Means 聚类库,适用于高性能计算。Rust 版 K-Means 实现主要有Rust K-Means —- Rust 实现的 K-Means 聚类库,适用于高性能计算以及rust-kmeans -- Crates.io 上的 rust-kmeans 库,可用于 K-Means 聚类算法的实现。
此处我们使用 ndarray 实现基本的聚类算法(K-Means)作为数据分析的一部分:
// 引入必要的库
use wasm_bindgen::prelude::*;
use ndarray::Array2;
use ndarray_rand::RandomExt;
use ndarray_rand::rand_distr::Uniform;
// 定义数据结构
#[derive(Debug, Clone)]
pub struct SensorData {
id: u32,
value: f64,
}
// 数据预处理
#[wasm_bindgen]
pub fn preprocess_sensor_data(data: &[SensorData], threshold: f64) -> Vec {
// 过滤掉值低于阈值的数据
data.iter()
.filter(|item| item.value > threshold)
.cloned()
.collect()
}
结语
本文主要探索了如何利用 Rust 和 WebAssembly 的组合,将数据处理能力引入 Web 端。通过 Rust 的并发计算和 WASM 的高执行效率优势,尝试克服传统 JavaScript 的性能瓶颈,进而实现了在浏览器中进行复杂数据分析的可能性。随着 AI 快速发展的当下,Rust + WASM 凭借着高并发、高执行效率以及跨平台的优势,将会是端智能甚至是边缘计算中一个非常不错的选择。
参考文档