宝贝,带上WebAssembly,换个姿势来优化你的前端应用

在你没崛起之前,脸是用来丢的

大家好,我是「柒八九」。一个「专注于前端开发技术/RustAI应用知识分享」Coder

此篇文章所涉及到的技术有

  1. WebAssembly
  2. Rust
  3. Web Worker( comlink)
  4. wasm-pack
  5. Photon
  6. ffmpeg.wasm
  7. 脚手架生成前端项目

因为,行文字数所限,有些概念可能会一带而过亦或者提供对应的学习资料。请大家酌情观看。


前言

说起,「前端性能优化」,大家可能第一时间就会从网络/资源加载/压缩资源等角度考虑。

正如下面所展示的一样。 alt

alt
alt

上面所列的措施,是我们常规优化方案。针对上面的内容我们有机会来讲讲该如何做。

而今天呢,我们和大家唠唠利用WebAssembly来优化前端渲染链路或者针对关键节点进行调优处理。


好了,天不早了,干点正事哇。

alt

我们能所学到的知识点

  1. WebAssembly是个啥?
  2. 项目初始化&配置
  3. Rust项目初始化
  4. 处理耗时任务
  5. 图像处理
  6. 优化音视频
  7. 优化游戏体验

1. WebAssembly是个啥?

之前,我们在浏览器第四种语言-WebAssembly已经对WebAssembly有过介绍,为了行文的完整,我们再用简短的内容解释一下它。

WebAssembly是一种「二进制指令格式」,旨在在浏览器中高效执行。

  • 「作为JavaScript的补充」,允许我们用 RustC++C等语言编写性能关键代码,并在浏览器中运行(还记得我们前几天的文章 Rust 赋能前端 -- 写一个 File 转 Img 的功能分别讲了将 C/Rust编写成 wasm用于文档解析)。
  • 通过将代码编译成 Wasm,它变得 「平台无关」,并且可以以接近本地的速度运行。
  • Rust是一种以安全性和性能著称的系统编程语言,由于其强大的保证和与 Wasm的无缝集成,已经在 WebAssembly生态系统中获得了广泛的关注。(如果想了解更多 Rust相关内容,可以参考我们的 Rust学习笔记系列文章)
  • WebAssembly为网络开发开辟了新的可能性,在一些复杂任务如游戏引擎、图像处理等方面有着显著的性能提升。

WebAssembly 的优势

WebAssembly的一个最具说服力的特点是其在「计算密集型任务」中的性能提升。例如,在对庞大数据集进行复杂的统计计算时,WebAssembly 可能比常规的 JavaScript 快得多。这是因为 WebAssembly 的高度优化设计使得代码执行速度远远超过 JavaScript

WebAssembly 的另一个优点是其「可移植性」。跨平台应用程序的开发变得非常简单,因为可以从多种语言生成 WebAssembly 代码,并在任何平台上执行。

最后,「安全性」也是 WebAssembly 架构中的一个重要考虑因素。由于 WebAssembly 提供了沙箱执行环境,代码无法访问敏感数据或运行恶意代码。

下面是了解和学习WebAssemblyRoadMap

alt

2. 项目初始化&配置

进入正题之前,我们还是和之前一样,使用我们自己的脚手架-f_cli_f[1]构建一个以Vite为打包工具的前端项目。

在本地合适的目录下执行如下代码:

npx f_cli_f create wasm_preformance

然后,我们在pages中新建如下的目录结构 alt

其中wasm存放的是我们已经构建好的wasm的资源。

配置Web Worker

由于我们在项目中会用到Web Worker,所以我们还需要对其做一定的配置。之前呢,我们在React中使用多线程—Web Worker中介绍过,如何在React+Vite的项目中使用Web Worker

而今天,我们再介绍另外一种更加优雅的方式 - Comlink[2]

Comlink是一个由Google Chrome Labs开发的轻量级库,它旨在简化Web Worker与主线程之间的通信,让我们能够充分利用多线程处理的威力,提升前端应用性能。

由于,我们是用Vite搭建的前端项目,所以我们还需要在项目中借助vite-plugin-comlink[3]

我们可以通过如下代码安装对应的依赖。

yarn add -D vite-plugin-comlink
yarn add comlink

然后,将对应的库配置到vite.config.js中。

import { comlink } from "vite-plugin-comlink";

export default {
  plugins: [comlink()],
  worker: {
    plugins() => [comlink()],
  },
};

这里有一点需要额外注意,comlink要放置在plugins第一个位置。

针对TypeScript项目,我们还需要在vite-env.d.ts中新增/// <reference types="vite-plugin-comlink/client" />

然后我们就可以用优雅的方式来使用WebWorker了。

alt

可以看到,使用了comlink后,我们在使用多线程能力时,不需要写那么多模板代码,而是通过Promise来接收从子线程返回的数据。

关于Web Worker的相关内容,可以看我们之前的文章

  • Web性能优化之Worker线程(上)
  • Web性能优化之Worker线程(下)
  • React中使用多线程—Web Worker

配置WebAssembly

如果看过我们之前的文章(Rust 赋能前端 -- 写一个 File 转 Img 的功能)就对这块不会陌生。

Vite项目中使用WebAssembly我们需要配置vite-plugin-wasm[4]vite-plugin-top-level-await[5]

然后,也是需要在vite.config.jspluginworker中进行相关处理。这里就不展开说明了。之前的文章有过解释。


3. Rust项目初始化

在讲项目页面结构时说过,我们在组件目录中特意有一个wasm目录用于存放编译好的wasm信息。

我们选择wasm代码和前端项目分离的方式,也就是我们会重新启动一个Rust项目。

通过如下代码在合适的文件目录下执行。

cargo new --lib rust_comformation2web

然后,因为我们想要把Rust编译成wasm并且还需要操作对应的dom等。所以,我们需要按照对应的crate

安装依赖

所以,我们来更新对应的Cargo.toml

[package]
name = "rust_comformation2web"
version = "0.1.0"
edition = "2021"

[lib]
crate-type = ["cdylib"]
[dependencies]
wasm-bindgen = "0.2.92"
console_error_panic_hook = "0.1.7"
js-sys = "0.3.69"

[dependencies.web-sys]
version = "0.3.69"
features = [
    'Document',
    'TextMetrics',
    'CanvasRenderingContext2d',
    'HtmlCanvasElement',
    'Window'
]

然后,我们就可以在src/lib.rs写我们对应的代码了。

如果对自己的代码质量不是很放心,并且又不想写Test模块了,我们将Rust所在的文件目录,构建成一个Node项目(通过npm init),并配合对应的打包软件(Webpack)来直接验证wasm的效果。

alt

对应的webpack.config.js的配置如下:

const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const webpack = require('webpack');
const WasmPackPlugin = require("@wasm-tool/wasm-pack-plugin");

module.exports = {
    entry'./index.js',
    output: {
        path: path.resolve(__dirname, 'dist'),
        filename'index.js',
    },
    plugins: [
         new HtmlWebpackPlugin({
            template'index.html'
        }),
        new WasmPackPlugin({
            crateDirectory: path.resolve(__dirname, ".")
        }),
        // 让这个示例在不包含`TextEncoder`或`TextDecoder`的Edge浏览器中正常工作。
        new webpack.ProvidePlugin({
          TextDecoder: ['text-encoding''TextDecoder'],
          TextEncoder: ['text-encoding''TextEncoder']
        })
    ],
    mode'development',
    experiments: {
        asyncWebAssemblytrue
   }
};

然后,我们在package.json新增两个命令

"scripts": {
    "build""webpack",
    "serve""webpack serve"
  },

我们就可以通过yarn serve查看效果亦或者yarn build执行对应的rust打包。

能够实现这一切的功劳都是-@wasm-tool/wasm-pack-plugin[6]所赐予的。

编译处理

但是呢,我们对Rust编译处理不使用之前的yarn build,而是使用cargo自己的构建工具 - wasm-pack[7]

wasm-pack build --target web --release

如果一切都正常的话,对应的wasm就会被打包到pkg文件夹下面了。

然后,我们就可以将所有文件复制到Vite项目中的wasm/xx目录下。

最后,我们就可以在React组件中通过

import init, { fib } from './wasm/xx';

引入对应的wasm函数了。


前面铺垫了那么多,其实为了更好的讲下面的内容,我们先把一些和逻辑代码不相关的配置内容提前介绍了,这样我们就可以将更过的注意力放在代码实现上了。


4. 处理耗时任务

先说结果

alt

当执行一个处理耗时任务时,WebAssembly/JS WebWorker/JS主线程三者的执行时间是由低到高排列的。

WebAssembly < JS WebWorker<JS主线程

针对上面的我们有几点需要注意

  1. JS WebWorker针对 JS主线程优化率不是很高,(有时候 worker执行时间甚至比 JS主线程长)
  2. WebAssembly通过至极的内存优化,还可以将优化率提高到 50%以上。

听我解释

我们都知道JS是单进程的,所以我们在处理一些处理耗时任务就会很吃力。当然,我们也可以借助Web Worker来开启新的子线程来缓解主线程的计算压力。但是,在一些计算量特别大的功能面前,一切的计算都是收效甚微的。

其实,将一些处理耗时任务放置到Web Worker中只是不想让耗时任务过多的占用主线程资源,从而让页面没有卡顿的感觉。这就是大家所熟悉的浏览器在 1 秒钟内完成 60 次图像的绘制,用户才会感觉页面顺畅

关于浏览器渲染的相关内容,可以看我们之前的文章

  • 浏览器工作原理 [8]
  • 页面是如何生成的(宏观角度)
  • 像素是怎样练成的

为了在前端环境模拟处理耗时任务,我们采用在前端环境中执行一个fibonacci的计算过程。

WasmPerformanceindex.tsx中有如下的页面操作。 alt

也就是说,我们在JS主线程/JS WebWorker/WebAssembly中分别执行一个耗时的fibonacci

我们在tool.ts中构建了一个最简单的fibonacci函数。

function fibJS(n: number): number {
  if (n < 2) {
    return n;
  }
  return fibJS(n - 1) + fibJS(n - 2);
}

对应的页面代码如下

alt

从上面我们看到几个关键的点

我们用state来维护计算的结果时间

const [calculateInfo, setCalculateInfo] = useState<CalculateInfo>({
    js: { result: 0, executionTime: 0 },
    wasm: { result: 0, executionTime: 0 },
    webworker: { result: 0, executionTime: 0 },
  });

然后,我们在handleCalculate中执行不同的操作逻辑。

alt

其中measureExecutionTime是我们在tool定义的用于检测指定函数被执行时的所用时间的函数.

function measureExecutionTime<T extends (...args: any[]) => any>(
  fn: T
): (...args: Parameters<T>) => 
{ result: ReturnType<T>; executionTime: number } {
  return function (...args: Parameters<T>): { result: ReturnType<T>; executionTime: number } {
    const start = performance.now();
    const result = fn.apply(this, args);
    const end = performance.now();
    const executionTime = end - start;
    return { result, executionTime };
  };
}

还有,我们在handleCalculate在接收到type为3时,是触发了一个wasm版本的fibonacci函数。

由于,对应的Rust代码如下:

use wasm_bindgen::prelude::*;
#[wasm_bindgen]
pub fn fib(n: usize) -> usize {
    match n {
        0 => 0,
        1 => 1,
        _ => fib(n - 1) + fib(n - 2),
    }
}

而上面的Rust代码会通过wasm-pack build --target web --release进行打包处理,并且打包后的相关内容被复制到了前端项目中wasm/calculate

然后在组件中通过import init, { fib } from './wasm/calculate';方式来导入。


5. 图像处理

先说结果

alt
alt

我们写了两个示例

  1. 将指定文本信息绘制到图片上
  2. 将特定图形绘制到图片上

无论是哪种情况,我们可以得出一个比较明显的情况。

在图像处理的部分功能点上,WebAssembly的性能远高于JS

因为,我们这里没做WebAssembly的内存优化,当处理数据「超级大」时,由于数据传输的问题,反而WebAssembly的执行时间会比JS长。但是呢,这块不在我们的讨论范围内。后期有机会写相关的文章。

下面,我们就按照上面的示例来分别讲讲它们的代码实现。有些代码的逻辑其实很简单,我们已经有对应的注释,所以也不会用多余的篇幅解释。

绘制文本到图片上

对应的页面结构如下 alt

我们还是用了一个state来维护状态信息。

 const [drawInfo, setDrawInfo] = useState<DrawInfo>({
    js: { url: '', executionTime: 0 },
    wasm: { url: '', executionTime: 0 },
    js_circle: { url: '', executionTime: 0 },
    wasm_circle: { url: '', executionTime: 0 },
  });

然后在handleDraw中处理事件逻辑。

alt

其中drawTextToCanvas是利用JS来绘制文本到Canvas,而drawTextToCanvasWasm是利用wasm处理相关逻辑。

JS 版本的drawText

alt 该函数定义在tool.ts中,然后就是接收一个String类型的数据,并将其渲染到Canvas中。

Rust 版本的drawText

alt 然后,别忘记在头部引入对应的crate.

use wasm_bindgen::prelude::*;
use wasm_bindgen::JsCast;
use web_sys::{CanvasRenderingContext2d, HtmlCanvasElement};
extern crate console_error_panic_hook;
use std::panic;

其实这块的逻辑,和之前我们讲的Rust 赋能前端 -- 写一个 File 转 Img 的功能的核心功能是类似的。

该函数通过wasm-pack编译到pkg中,然后我们复制对应的文件到React项目的wasm/draw中。

然后我们通过如下代码

import init4Draw, {
  draw_text_to_canvas as drawTextToCanvasWasm,
  draw_circle_to_canvas as drawCircleToCanvasWasm,
from './wasm/draw';

进行函数的导入。

绘制图形到图片上

对应的页面结构和事件回调和之前是类似的,我们就省略了这部分的解释。

JS 版本的drawCircle

该部分也是定义在tool.tsalt

Rust 版本的drawCircle

alt

此函数的处理过程和drawText是一样的。

利用Photon操作图形

针对图片操作,不单单只有绘制文本/绘制图案,其实我们还可以做类似(裁剪/新增水印/图片翻转等)。

我们可以借助一些成熟的WebAssembly来做上述的操作。这里呢,给大家推荐一个库Photon[9]

Photon 是一个高性能的图像处理库,用 Rust 编写并可编译为 WebAssembly,既可以在本地使用 Web 也可以在 Web 上使用。

这是它能做相关功能 alt


6. 优化音视频

写到这里呢,我们就不在罗列相关代码了。所以,我们给出一些针对音视频的优化的解决方案。(当然,我们后期也会有专门的文章)

在这里我们介绍一种wasm库-ffmpeg.wasm[10]

ffmpeg.wasmFFmpeg[11] 的针对 WebAssembly / JavaScript 端口,支持在浏览器中录制、转换和流式传输视频和音频。它利用 Emscripten 来转译 FFmpeg 源代码和许多库得到

具体的功能和库如下: alt


7. 优化游戏体验

得益于WebAssembly极致的内存管理,然后其二进制特性,WebAssembly 提供接近本地执行速度的性能,使得复杂的游戏逻辑和高帧率的图形渲染可以在浏览器中高效运行。

还得之前我们写过Game = Rust + WebAssembly + 浏览器

alt

还有,如果我们想要更多的效果,我们可以选择使用bevy[12] - 一款基于Rust的数据驱动的游戏引擎。

然后我们还在itch.io[13]查看哪些游戏是用Rust写的。 alt


后记

「分享是一种态度」

「全文完,既然看到这里了,如果觉得不错,随手点个赞和“在看”吧。」

alt

Reference

[1]

f_cli_f: https://www.npmjs.com/package/f_cli_f

[2]

Comlink: https://github.com/GoogleChromeLabs/comlink

[3]

vite-plugin-comlink: https://www.npmjs.com/package/vite-plugin-comlink

[4]

vite-plugin-wasm: https://www.npmjs.com/package/vite-plugin-wasm

[5]

vite-plugin-top-level-await: https://www.npmjs.com/package/vite-plugin-top-level-await

[6]

@wasm-tool/wasm-pack-plugin: https://www.npmjs.com/package/@wasm-tool/wasm-pack-plugin

[7]

wasm-pack: https://github.com/rustwasm/wasm-pack

[8]

浏览器工作原理: https://juejin.cn/post/6923953599936954382

[9]

Photon: https://github.com/silvia-odwyer/photon

[10]

ffmpeg.wasm: https://ffmpegwasm.netlify.app/

[11]

FFmpeg: https://www.ffmpeg.org/

[12]

bevy: https://bevyengine.org/

[13]

itch.io: https://itch.io/games/html5/made-with-rust

本文由 mdnice 多平台发布

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/686465.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Yolo-v5模型训练速度,与GeForce的AI算力描述

1.GeForce RTX3070 Ti官网参数&#xff1a; GeForce RTXTM 3070 Ti 和 RTX 3070 显卡采用第 2 代 NVIDIA RTX 架构 - NVIDIA Ampere 架构。该系列产品搭载专用的第 2 代 RT Core &#xff0c;第 3 代 Tensor Core、全新的 SM 多单元流处理器以及高速显存&#xff0c;助您在高性…

Hi3519DV500 学习摘录

文章目录 一、问题1、autoreconf2、open-vm-tools 安装3、NFS4、pushd: not found 一、问题 1、autoreconf automake version mismatch | AM_INIT_AUTOMAKE | 版本不匹配 autoreconf ./configure make2、open-vm-tools 安装 open-vm-tools 安装 # 用于安装和升级的命令是相…

Codeforces Round 951 (Div. 2) C、D(构造、线段树)

1979C - Earning on Bets 构造题&#xff1a;观察到k范围很小&#xff0c;首先考虑最终硬币总数可以是多少&#xff0c;我们可以先假设最终的硬币总数为所有k取值的最小公倍数&#xff0c;这样只需要满足每个结果添加1枚硬币即可赚到硬币。 // Problem: C. Earning on Bets //…

​​​​【动手学深度学习】残差网络(ResNet)的研究详情

目录 &#x1f30a;1. 研究目的 &#x1f30a;2. 研究准备 &#x1f30a;3. 研究内容 &#x1f30d;3.1 残差网络 &#x1f30d;3.2 练习 &#x1f30a;4. 研究体会 &#x1f30a;1. 研究目的 了解残差网络&#xff08;ResNet&#xff09;的原理和架构&#xff1b;探究残…

【Vue】声明式导航-导航链接

文章目录 一、引入二、解决方案三、代码示例四、声明式导航-两个类名1&#xff09;router-link-active2&#xff09;router-link-exact-active 一、引入 但凡说到声明式导航&#xff0c;都需要想到router-link 需求 实现导航高亮效果 如果使用a标签进行跳转的话&#xff0c;需要…

JSONPath使用指南(掌握JSON数据提取)

大家好&#xff0c;在处理 JSON&#xff08;JavaScript Object Notation&#xff09;数据时&#xff0c;有时需要从复杂的结构中提取特定部分。JSONPath 就是一个非常有用的工具&#xff0c;它提供了一种简洁而强大的方式来定位和提取 JSON 数据中的元素。无论是在 Web 开发中处…

【C++ | 析构函数】类的析构函数详解

&#x1f601;博客主页&#x1f601;&#xff1a;&#x1f680;https://blog.csdn.net/wkd_007&#x1f680; &#x1f911;博客内容&#x1f911;&#xff1a;&#x1f36d;嵌入式开发、Linux、C语言、C、数据结构、音视频&#x1f36d; ⏰发布时间⏰&#xff1a;2024-06-06 1…

上海亚商投顾:微盘股指数大跌超6% 全市场仅500余只个股上涨

上海亚商投顾前言&#xff1a;无惧大盘涨跌&#xff0c;解密龙虎榜资金&#xff0c;跟踪一线游资和机构资金动向&#xff0c;识别短期热点和强势个股。 一.市场情绪 沪指昨日震荡调整&#xff0c;创业板指午后一度跌超1%&#xff0c;微盘股指数盘中跌逾7%&#xff0c;小市值个…

YOLO系列模型 pt文件转化为ONNX导出

文章目录 啥是onnx怎么导出导出之后 啥是onnx Microsoft 和合作伙伴社区创建了 ONNX 作为表示机器学习模型的开放标准。许多框架&#xff08;包括 TensorFlow、PyTorch、scikit-learn、Keras、Chainer、MXNet 和 MATLAB&#xff09;的模型都可以导出或转换为标准 ONNX 格式。 在…

09.0手工制作docker镜像-单服务ssh

手动将容器保存为镜像-单服务ssh 本页测试内容&#xff0c;将centos6.9镜像安装ssh服务并提交新的镜像并可使用。 docker commit 容器id或者容器的名字 新的镜像名字[:版本号可选] docker commit test centos6.9-ssh:v11&#xff09;基于容器制作镜像&#xff0c;首先创建一个…

DP:子序列模型

子数组vs子数列 1、子数组&#xff08;n^2&#xff09; 子序列(2^n) 2、子数组是子序列的一个子集 3、子数组必须连续&#xff0c;子序列可以不连续 一、最长递增子序列 . - 力扣&#xff08;LeetCode&#xff09; 算法原理&#xff1a; 1、状态表示&#xff…

使用命令给电脑添加虚拟网卡和IP

目录 1、添加网卡 1-1、windows系统添加网卡 1-2、Linux系统中添加网卡 2、添加IP和DNS 2-1、添加IP 2-2、 设置DNS 3、删除网卡 3-1、Windows: 3-2、Linux 3-3、macOS 4、示例&#xff1a; 首先以管理员方式进入CMD命令行&#xff1b; 点击“开始”->“管理员…

HLA高层体系结构1.0.0版本

名&#xff1a;高层体系结构&#xff08;High Level Architecture&#xff0c;HLA&#xff09; 高层体系结构&#xff08;High Level Architecture&#xff0c;HLA&#xff09;是从体系结构上建立这样一个框架&#xff0c;它能尽量涵盖M&S领域中所涉及的各种不同类型的仿真…

springboot启动配置文件-bootstrap.yml常用基本配置

4.1.5.配置文件 SpringBoot的配置文件支持多环境配置&#xff0c;基于不同环境有不同配置文件&#xff1a; 说明&#xff1a; 文件说明bootstrap.yml通用配置属性&#xff0c;包含服务名、端口、日志等等各环境通用信息bootstrap-dev.yml线上开发环境配置属性&#xff0c;虚…

微服务开发与实战Day01 - MyBatisPlus

一、微服务 概念&#xff1a;微服务是一种软件架构风格&#xff0c;它是以专注于单一职责的很多小型项目为基础&#xff0c;组合除复杂的大型应用。 课程安排&#xff1a; https://www.bilibili.com/video/BV1S142197x7/?spm_id_from333.1007.top_right_bar_window_history.…

41【Aseprite 作图】粉红宫灯——拆解

1 宫灯轮廓 上面三角&#xff0c;下面3 3 3 &#xff08;粉色在后面&#xff0c;做轮廓&#xff09;&#xff0c;棕色在外面&#xff0c;看做是灯骨&#xff08;竖着更长&#xff09;&#xff1b;中间是横着做灯骨 尾部的彩带&#xff0c;下面粉色更浅&#xff0c;上面绿色更浅…

LabVIEW飞机发动机测试与故障诊断系统

LabVIEW飞机发动机测试与故障诊断系统 基于LabVIEW开发了一个飞机发动机测试与故障诊断系统&#xff0c;能够实时监测发动机的运行参数&#xff0c;进行数据采集与分析&#xff0c;并提供故障诊断功能。系统采用高精度传感器和数据采集硬件&#xff0c;适用于发动机的性能测试、…

Kaggle——Deep Learning(使用 TensorFlow 和 Keras 为结构化数据构建和训练神经网络)

1.单个神经元 创建一个具有1个线性单元的网络 #线性单元 from tensorflow import keras from tensorflow.keras import layers #创建一个具有1个线性单元的网络 modelkeras.Sequential([layers.Dense(units1,input_shape[3]) ]) 2.深度神经网络 构建序列模型 #构建序列模型 …

在k8s中部署Logstash多节点示例(超详细讲解)

&#x1f407;明明跟你说过&#xff1a;个人主页 &#x1f3c5;个人专栏&#xff1a;《洞察之眼&#xff1a;ELK监控与可视化》&#x1f3c5; &#x1f516;行路有良友&#xff0c;便是天堂&#x1f516; 目录 一、引言 1、Logstash简介 2、在K8s中部署Logstash多节点实例…

简单聊聊大数据分析的方法有什么

大数据分析是指对规模巨大的数据集合进行的分析过程。 这些数据集合通常具有以下几个特点&#xff0c;可以概括为5个V&#xff1a; 1.数据量大&#xff08;Volume&#xff09;&#xff1a;大数据分析处理的数据量巨大&#xff0c;远远超出了传统数据处理软件的能力范围。 2.…