成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

基于IM場景下的Wasm初探:提升Web應用性能

開發 前端
在大多數場景下我們都不需要用到WebAssembly。因為V8等JS引擎的優化帶來了巨大的性能提升,已經足夠讓JavaScript應對絕大多數的普通場景了,如果要做進一步優化密集計算任務時使用Web worker也都能解決掉。

一、何為Wasm ?

Wasm,全稱 WebAssembly,官網描述是一種用于基于堆棧的虛擬機的二進制指令格式。Wasm被設計為一個可移植的目標,用于編譯C/C++/Rust等高級語言,支持在Web上部署客戶端和服務器應用程序。

Wasm 的開發者參考文檔:https://developer.mozilla.org/en-US/docs/WebAssembly

簡單的來說就是使用C/C++/Rust等語言編寫的代碼,經過編譯后得到匯編指令,再通過JavaScript相關API將文件加載到Web容器中,一句話解釋就是運行在Web容器中的匯編代碼。Wasm是一種可移植、體積小、加載快速的二進制格式,可以將各種編程語言的代碼編譯成Wasm模塊,這些模塊可以在現代瀏覽器中直接運行。尤其在涉及到GPU或CPU計算時優勢相對比較明顯。

二、為什么需要Wasm ?

JavaScript是解釋型語言,相比于編譯型語言需要在運行時轉換,所以解釋型語言的執行速度要慢于編譯型語言。

編譯型語言和解釋型語言代碼執行的大致流程如下:

圖片圖片

如上流程圖所示,解釋型語言每次執行都需要把源碼轉換一次才能執行,而轉換過程非常耗費時間和性能,所以在 JavaScript背景下,Web執行一些高性能應用是非常困難的,如視頻剪輯、3D游戲等。

Wasm具有緊湊的二進制格式,可以接近原生的性能運行,并為C/C++等語言提供一個編譯目標,以便它們可以在Web上運行。被設計為可以與JavaScript共存,允許兩者一起工作。在特定的業務場景下可以完美的彌補JavaScript的缺陷。

三、優勢和限制

優勢:

  • 性能優異:相比JavaScript代碼,Wasm使用節省內存,快速加載和解釋的二進制代碼,具備更快執行速度,它是直接在底層虛擬機中運行的。這使得Web應用程序可以更高效地處理復雜的計算任務,例如圖形渲染、物理模擬等。
  • 跨平臺兼容:Wasm可以在幾乎所有現代瀏覽器中運行,兼容性可參考caniuse,無論是桌面還是移動設備。這意味著開發者可以使用各種編程語言來編寫Web應用程序,而不僅僅局限于JavaScript。
  • 安全性:Wasm運行在沙箱環境中,提供了良好的安全性。使用了一系列安全措施,如內存隔離和沙箱限制,以防止惡意代碼對系統的攻擊。
  • 模塊化:Wasm模塊可以作為獨立的組件進行開發和部署,開發者可以更好地管理和維護代碼庫。模塊化的設計也為將來的性能優化和增量更新提供了便利。

局限性:

  • 生態系統不夠完善:盡管Wasm已經成為Web開發中的關鍵技術之一,但生態系統仍然不夠完善。Wasm的工具、框架和庫的數量遠不如JavaScript。
  • 開發門檻較高:Wasm的開發門檻相對較高。Wasm需要使用一種新的語言來編寫,如C或C++等。這使得學習和使用Wasm的成本相對較高。尤其是在內存管理等方面會增加開發的復雜性。
  • 與JavaScript集成問題:Wasm與JavaScript之間的集成問題是一個挑戰。開發人員需要解決如何在Web應用程序中同時使用Wasm和JavaScript的問題。
  • 兼容性問題:雖然現代瀏覽器已經開始支持Wasm,但是在一些老舊的瀏覽器中可能存在兼容性問題,需要開發者進行額外的處理來確保代碼的兼容性。

四、Wasm工作原理

通過上述的編譯型語言和解釋型語言代碼執行的大致流程我們可以知道Wasm是不需要被解釋的,是由開發者提前編譯為WebAssembly二進制格式,如下圖所示。由于變量類型都是預知的,因此瀏覽器加載WebAssembly文件時,JavaScript引擎無須監測代碼。它可以簡單地將這段代碼的二進制格式編譯為機器碼。

圖片圖片

從這個流程中我們也可以看出,如果將每種編程語言都直接編譯為機器碼的各個版本,這樣效率是不是更高呢?想法是好的,但實現過程確實復雜不堪的。由于瀏覽器是可以在若干不同的處理器 (比如手機和平板等設備) 上運行,因此為每個可能的處理器發布一個WebAssembly代碼的編譯后版本會很難做到。

我們可以通過替代方法即取得IR代碼。IR即為中間代碼(Intermediate Representation),它是編譯器中很重要的一種數據結構。編譯器在做完前端工作以后,首先就生成IR,并在此基礎上執行各種優化算法,最后再生成目標代碼。可以簡化為如下流程:

圖片圖片

編譯器將IR代碼轉換為一種專用字節碼并放入后綴為.wasm的文件中。此時Wasm文件中的字節碼還不是機器碼,它只是支持WebAssembly的瀏覽器能夠理解的一組虛擬指令。當加載到支持WebAssembly的瀏覽器中時,瀏覽器會驗證這個文件的合法性,然后這些字節碼會繼續編譯為瀏覽器所運行的設備上的機器碼。

更加詳情的原理和使用方式可以前往https://developer.mozilla.org/en-US/docs/WebAssembly/JavaScript_interface查閱。

五、應用場景

在Web開發中,可以使用Wasm來提高應用程序的性能。以下是一些使用Wasm的常見場景:

  • 高性能計算:如果應用程序需要進行大量的數值計算、圖像處理或者復雜的算法運算,可以將這部分代碼編譯成Wasm模塊,以提高計算性能。
  • 游戲開發:Wasm可以用于創建高性能的HTML5游戲,通過將游戲邏輯編譯成Wasm模塊,可以實現更流暢的游戲體驗。
  • 跨平臺應用:使用Wasm可以實現跨平臺的應用程序,無論是桌面還是移動設備,用戶都可以通過瀏覽器來訪問和使用。
  • 移植現有代碼:如果已經有用其他編程語言編寫的代碼,可以通過將其編譯成Wasm模塊,將其集成到現有的Web應用程序中,而無需重寫整個應用程序。

六、產品案例

  • 設計工具Figma-Wasm文件大小為27.7M

圖片圖片

  • Google Earth-Wasm文件總計大小為192.M
  • 支持各大瀏覽器的3D地圖,而且運行流暢
  • B站-視頻處理和播放也有使用Wasm,Wasm文件大小為344kb

圖片圖片

  • 跨平臺的OpenGL圖形引擎Magnum-Wasm文件大小為844kb

圖片圖片

七、實踐案例

這里我們通過使用Rust + Wasm實現Wasm與JavaScript之間的數據調用,理解Rust和Wasm的交互過程。

使用Rust就需要做一些前置的環境配置,詳情的步驟可參考Rust官網:

https://www.rust-lang.org/zh-CN/tools/install。

安裝wasm-pack,wasm-pack是一個構建、測試和發布Wasm的Rust CLI工具,我們將使用wasm-pack相關的命令來構建Wasm二進制內容。這有助于將代碼編譯為WebAssembly,并生成在瀏覽器中使用的正確包。

Rust項目初始化

執行cargo new rust_wasm初始化Rust項目,自動生成配置文件Cargo.toml,項目結構如下:

/Users/admin/RustroverProjects/rust_wasm
├── Cargo.lock
├── Cargo.toml
├── src
|  └── lib.rs
└── target
   ├── CACHEDIR.TAG
   └── debug
      ├── build
      ├── deps
      ├── examples
      └── incremental

配置包文件

我們可以在Cargo.toml文件中加上下列代碼并保存,保存之后Cargo會自動下載依賴。

  • crate-type = ["cdylib"],表示編譯時候使用C標準的動態庫。
  • #[wasm_bindgen]是一個屬性宏,來自于wasm_bindgen這個crate,是一個簡化Rust WASM與JS之間交互的庫。 
[lib]
crate-type = ["cdylib"]


[dependencies]
wasm-bindgen = { version = "0.2.89", features = [] }

 編寫代碼

編寫代碼之前我們先明確Rust中crate包的概念,Rust中包管理系統將crate包分為二進制包(Binary)和庫包(Library)兩種,二者可以在同一個項目中同時存在。

二進制包:

  • main.rs是二進制項目的入口
  • 二進制項目可直接執行
  • 一個項目中二進制包可以有多個,所以在Cargo.toml中通過雙方括號標識 [[bin]]

庫包:

  • lib.rs是庫包的入口
  • 庫項目不可直接執行,通常用來作為一個模塊被其他項目引用
  • 一個項目中庫包僅有1個,在Cargo.toml中通過單方括號標識 [lib]

因為我們這里希望將 Wasm 轉為一個可以在JS項目中使用的模塊,所以需要使用庫包 lib.rs 的命名,代碼如下。

use wasm_bindgen::prelude::*;
#[wasm_bindgen]
pub extern "C" fn rust_add(left: i32, right: i32) -> i32 {
    println!("Hello from Rust!");
    left + right
}

執行編譯

這里我們要使用到wasm-pack,將上述的Rust代碼編譯為能夠被JS導入的模塊,根據wasm-pack提供的target方式可以指定構建的產物,如截圖所示:

圖片圖片

編譯過程效果:

編譯完成后,我們會發現根目錄下多了一個pkg/ 文件夾,里面就是我們的Wasm產物所在的npm包了。目錄結構如下:

/Users/admin/RustroverProjects/rust_wasm/pkg
├── package.json
├── rust_wasm.d.ts
├── rust_wasm.js
├── rust_wasm_bg.wasm
└── rust_wasm_bg.wasm.d.ts

rust_wasm.d.ts文件內容:

/* tslint:disable */
/* eslint-disable */
/**
* @param {number} num
* @returns {string}
*/
export function msg_insert(num: number): string;
/**
* @param {number} left
* @param {number} right
* @returns {number}
*/
export function rust_add(left: number, right: number): number;
/**
*/
export function rust_thread(): void;


export type InitInput = RequestInfo | URL | Response | BufferSource | WebAssembly.Module;


export interface InitOutput {
  readonly memory: WebAssembly.Memory;
  readonly msg_insert: (a: number, b: number) => void;
  readonly rust_add: (a: number, b: number) => number;
  readonly rust_thread: () => void;
  readonly __wbindgen_add_to_stack_pointer: (a: number) => number;
  readonly __wbindgen_free: (a: number, b: number, c: number) => void;
}


export type SyncInitInput = BufferSource | WebAssembly.Module;
/**
* Instantiates the given `module`, which can either be bytes or
* a precompiled `WebAssembly.Module`.
*
* @param {SyncInitInput} module
*
* @returns {InitOutput}
*/
export function initSync(module: SyncInitInput): InitOutput;


/**
* If `module_or_path` is {RequestInfo} or {URL}, makes a request and
* for everything else, calls `WebAssembly.instantiate` directly.
*
* @param {InitInput | Promise<InitInput>} module_or_path
*
* @returns {Promise<InitOutput>}
*/
export default function __wbg_init (module_or_path?: InitInput | Promise<InitInput>): Promise<InitOutput>;

wasm-pack打包不僅輸出一個ESM規范的模塊,而且還支持自動生成d.ts文件,對模塊的使用者非常友好。如下:

圖片圖片

在前端項目中引入使用

'use client'
/*
 * @Author: wangweiqiang
 * @Date: 2024-06-18 17:03:34
 * @LastEditors: wangweiqiang
 * @LastEditTime: 2024-06-18 23:09:55
 * @Description: app.tsx
 */
import Image from "next/image";
import { useCallback, useEffect, useState } from "react";
import init, * as rustLibrary from 'rust_wasm'
export default function Home() {
  const [addResult, setAddResult] = useState<number | null>(null)
  const [calculateTime, setCalculateTime] = useState<string>('')


  const initRustLibrary = useCallback(() => {
    init().then(() => {
      const result = rustLibrary.rust_add(5, 6)
      const timeStamp = rustLibrary.msg_insert(50000)
      setCalculateTime(timeStamp)
      setAddResult(result)
    })
  }, [])


  useEffect(() => {
    initRustLibrary()
  }, [initRustLibrary]);


  return (
    <main className="flex min-h-screen flex-col items-center p-24">
      {/* .... */}
      <div className="mt-32 grid text-center lg:mb-0 lg:w-full lg:max-w-5xl lg:grid-cols-4 lg:text-left">
        <div>
          rust代碼計算結果:{addResult}
        </div>
        <div style={{ marginTop: '20px' }}>
          二分法方式{calculateTime}
        </div>
      </div>
    </main>
  );
}

圖片圖片

性能比較

在IM場景下,聊天消息中核心的處理流程在于數據的排序、去重,大量的數據查找會非常耗時,在這里我們通過二分法的方式對Rust和JavaScript兩種實現方式的耗時進行一個簡單的對比,Rust代碼如下:

use chrono::{DateTime, Utc};
use rand::Rng;


#[derive()]
#[allow(dead_code)]
struct Data {
    content: String,
    from: String,
    head: String,
    msg_id: String,
    seq: i32,
    sid: String,
    topic: String,
    ts: DateTime<Utc>,
}


impl Data {
    fn new(
        content: String,
        from: String,
        head: String,
        msg_id: &str,
        seq: i32,
        sid: String,
        topic: String,
        ts: DateTime<Utc>,
    ) -> Self {
        Data {
            content,
            from,
            head,
            msg_id: msg_id.to_string(),
            seq,
            sid,
            topic,
            ts,
        }
    }
}


// 獲取原始數據
fn get_origin_data(num: i32) -> Vec<Data> {
    let mut data: Vec<Data> = vec![]; // 存儲數據的向量
    ....                              // 創建 num 個數據
    data
}
// 初始化結構體數據
fn init_struct_data(num: i32, text: &str) -> Data {
    let mut rng = rand::thread_rng();
    let content = format!("{}_{}", rng.gen_range(1000..=9999), text).to_string();
    ....
    let ts = Utc::now();
    Data::new(content, from, head, &msg_id.as_str(), seq, sid, topic, ts)
}


// 二分法插入
fn binary_insert(data: &mut Vec<Data>, new_data: Data) {
    let _insert_pos = match data.binary_search_by_key(&new_data.seq, |d| d.seq) {
        Ok(pos) => {
            data[pos] = new_data;
            pos
        }
        Err(pos) => {
            data.insert(pos, new_data);
            pos
        }
    };
}
#[wasm_bindgen]
pub extern "C" fn msg_insert(num: i32) -> String {
    let mut data: Vec<Data> = get_origin_data(1000);
    let test_mode = [num];
    let start_time = Utc::now().naive_utc().timestamp_micros();
    for test_num in 0..test_mode.len() {
        for num in 0..test_mode[test_num] {
            let data_list = init_struct_data(num, "test");
            binary_insert(&mut data, data_list);
        }
    }
    let duration = Utc::now().naive_utc().timestamp_micros() - start_time;
    let result = format!("插入{}條數據執行耗時:{}微秒", num, duration);
    result
}

數據對比分析:

圖片圖片

可以看到,在數據量不大的場景下,Wasm的耗時是比純JavaScript長的,這是因為瀏覽器需要在VM容器中對 Wasm模塊進行實例化,這一部分會消耗相當的時間,導致性能不如純JavaScript的執行。但隨著運算規模變大,Wasm的優化越來越明顯。這是因為WebAssembly是一種低級別的二進制格式,經過高度優化,并且能夠更好地利用系統資源。相比之下,JavaScript是一種解釋性語言,性能可能會受到解釋器的限制。

八、總結

在大多數場景下我們都不需要用到WebAssembly。因為V8等JS引擎的優化帶來了巨大的性能提升,已經足夠讓JavaScript應對絕大多數的普通場景了,如果要做進一步優化密集計算任務時使用Web worker也都能解決掉。只有在以上的少數場景下,我們才需要做這種“二次提升”。

WebAssembly雖然有天然的優勢,但也有自己的局限性,在使用時我們也需要考慮多方面因素,例如生態、開發成本等等。不過我們依然可以持續關注WebAssembly的發展。

責任編輯:武曉燕 來源: 得物技術
相關推薦

2017-12-13 13:09:36

NginxWeb應用

2013-01-14 12:24:06

Firefox OS

2014-08-26 15:02:04

mAPM移動應用性能監測AppDynamics

2018-08-23 17:45:52

2013-08-09 14:18:33

2023-11-06 08:01:09

Go同步異步

2015-12-14 10:39:14

2024-12-10 08:09:15

2023-09-04 07:30:03

Wasm匯編語言

2023-11-07 11:11:42

Go性能

2009-10-14 20:37:41

sun閃存固態硬盤

2014-04-24 10:11:17

iOS性能調優

2010-08-25 09:48:14

W3CWeb性能工作組

2010-04-02 15:20:44

惠普成功案例

2009-08-25 15:35:45

citrxinetscalerncore

2014-08-04 16:38:37

移動應用

2009-07-16 10:57:04

虛擬化新功能性能

2009-07-17 19:09:42

虛擬化VMware服務器

2015-07-29 15:06:21

2009-04-13 09:09:36

網絡性能萬兆應用性能
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 涩涩鲁亚洲精品一区二区 | 欧美综合视频 | 亚洲国产高清高潮精品美女 | 久久精品二区 | 日本精品视频在线 | 欧美精品三区 | 一区二区成人 | 日韩免费在线 | 高清18麻豆| www.av7788.com| 一本色道精品久久一区二区三区 | 久草热视频| 91不卡| 久久国产精品久久久久久久久久 | 一级久久久久久 | 欧美综合久久久 | 91精品国产综合久久婷婷香蕉 | 超碰伊人久久 | 亚洲成人精选 | 日韩精品在线一区 | 婷婷久久五月天 | 国产高清在线精品 | 欧美一级艳情片免费观看 | 亚洲一区二区三区免费在线观看 | 奇米影视首页 | 国产亚洲一区二区在线观看 | 久久香焦 | 日韩午夜影院 | 国产久视频 | 毛片99| 热99在线| 久久精品中文 | 欧美一级特黄aaa大片在线观看 | 最新中文字幕在线播放 | 一区二区三区四区av | 亚洲国产精品99久久久久久久久 | 久久久精品网站 | 一区二区三区在线播放 | 亚洲国产精品久久久久秋霞不卡 | 国产成视频在线观看 | av中文字幕在线观看 |