目錄
- 引言
- 本文重點
- 準備工作
- 測試代碼
- 純js測試代碼
- wasm(go)源碼
- js+wasm測試代碼
- 測試條件
- 測試目標
- chrome (版本:103.0.5060.114)
- firefox (版本號:103.0.1 (64 位))
- 分段計算測試代碼
- 測試結論
- 最終結論
引言
在過去的幾年裡,wasm的話題那真是從早上聊到晚上,可以說處於異常興奮的狀態,但是幾年過去瞭,它慢慢的被大多數人們忘記,原因比較簡單——落地難
今天就wasm能給js加多少分這個問題,做一個小型的討論,今天的專註點是,前端js獲取一個文件的md5值,也就是上傳文件時所需要的秒傳功能的核心
簡單來說,文件上傳秒傳不僅僅是網盤公司的專屬,平時我們上傳文件給後端也是很常用的,前端通過對目標文件md5計算後與後端進行對比,如果已經上傳過,則直接返回已有地址,這樣,大大節省瞭服務器空間。基本思路如下:
- 前端input type="file"獲取文件
- 通過md5工具庫進行計算,得到md5值
- 請求接口,後端判斷此md5是否已經在數據庫裡
- 如果在數據庫裡,則直接告訴前端,已存在(秒傳)
本文重點
今天的重點是如何快速獲取一個文件的md5值,這裡就涉及到小文件,大文件的問題瞭。所以,我將以下面文件體積為例來測試js與wasm對文件md5計算的速度對比。
wasm我使用golang進行開發,因為golang打包成wasm會把運行時也加進去,所以,打包的結果2.2M,我們暫時忽略這個體積,因為如果能落地,那麼換成rust,換成c++都不是難事,如果不能落地,那麼,golang不行,c++也照樣不行。
準備工作
通過ffmeg 從一個2G+的文件上截取不同體積的文件,用於測試。
ffmpeg -i /path/sourch.mp4 -fs 1M -c:v copy -c:a copy /path/1M.mp4
ffmpeg -i /path/sourch.mp4 -fs 5M -c:v copy -c:a copy /path/5M.mp4
ffmpeg -i /path/sourch.mp4 -fs 20M -c:v copy -c:a copy /path/20M.mp4
ffmpeg -i /path/sourch.mp4 -fs 50M -c:v copy -c:a copy /path/50M.mp4
ffmpeg -i /path/sourch.mp4 -fs 100M -c:v copy -c:a copy /path/100M.mp4
ffmpeg -i /path/sourch.mp4 -fs 200M -c:v copy -c:a copy /path/200M.mp4
ffmpeg -i /path/sourch.mp4 -fs 400M -c:v copy -c:a copy /path/400M.mp4
ffmpeg -i /path/sourch.mp4 -fs 600M -c:v copy -c:a copy /path/500M.mp4
ffmpeg -i /path/sourch.mp4 -fs 800M -c:v copy -c:a copy /path/800M.mp4
ffmpeg -i /path/sourch.mp4 -fs 900M -c:v copy -c:a copy /path/900M.mp4
ffmpeg -i /path/sourch.mp4 -fs 1024M -c:v copy -c:a copy /path/1024M.mp4
ffmpeg -i /path/sourch.mp4 -fs 1280M -c:v copy -c:a copy /path/1280M.mp4
ffmpeg -i /path/sourch.mp4 -fs 1536M -c:v copy -c:a copy /path/1536M.mp4
ffmpeg -i /path/sourch.mp4 -fs 1792M -c:v copy -c:a copy /path/1792M.mp4
ffmpeg -i /path/sourch.mp4 -fs 2048M -c:v copy -c:a copy /path/2048M.mp4
測試代碼
純js測試代碼
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>文件md5</title>
<script src="./SparkMD5.js"></script>
</head>
<body>
<input id="file" type="file" />
<script>
document.querySelector('#file').addEventListener('change', e => {
let startTime = Date.now()
const file = e.target.files[0];
const fileReader = new FileReader()
console.log('size', file.size / 1024 / 1024 / 1024, "G")
fileReader.onprogress = e => {
console.log(`${Math.floor((e.loaded / e.total) * 100)}%`)
}
let usedTime = 0
const md5 = new SparkMD5();
fileReader.readAsBinaryString(file);
fileReader.onload = e => {
md5.appendBinary(e.target.result);
const md5Str = md5.end()
usedTime += Date.now() - startTime
console.log('usedTime', usedTime, 'ms')
console.log('md5', md5Str)
}
});
</script>
</body>
</html>
wasm(go)源碼
請參考:
github.com/butoften/wa…
js+wasm測試代碼
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>文件md5</title>
<script src="./wasm_exec.js"></script>
</head>
<body>
<script>
function handleSayHello(message) {
console.lof('str from go', message)
}
const go = new Go();
WebAssembly.instantiateStreaming(fetch('wasm/md5.wasm'), go.importObject)
.then(res => {
go.run(res.instance);
});
</script>
<input id="file" type="file" />
<script>
document.querySelector('#file').addEventListener('change', e => {
let startTime = Date.now()
const file = e.target.files[0];
const fileReader = new FileReader()
console.log('size', file.size / 1024 / 1024 / 1024, "G")
fileReader.onprogress = e => {
console.log(`${Math.floor((e.loaded / e.total) * 100)}%`)
}
let usedTime = 0
fileReader.readAsArrayBuffer(file);
fileReader.onload = e => {
const bytes = new Uint8Array(e.target.result)
wasmMd5Add(bytes)
const md5Hash = wasmMd5End()
usedTime += Date.now() - startTime
console.log('usedTime', usedTime, 'ms')
console.log('md5', md5Hash)
}
});
</script>
</body>
</html>
測試條件
- 從FileReader開始讀取算起到md5計算結束,因為現實中,我們需要做loading條動畫比例
- mac 2.7 GHz 雙核Intel Core i5
- mac 8 GB 1867 MHz DDR3
測試目標
chrome (版本:103.0.5060.114)
- 2048M 測試5次分別用時:
- 如果分段計算,每段使用512M
序號 |
純js |
純js分段 |
js+wasm |
js+wasm分段 |
1 |
37477 ms |
25638 ms |
31680 ms |
22898 ms |
2 |
32926 ms |
28088 ms |
32516 ms |
25168 ms |
3 |
33413 ms |
31412 ms |
33424 ms |
20547 ms |
4 |
35054 ms |
35821 ms |
33906 ms |
23130 ms |
5 |
35986 ms |
36895 ms |
29014 ms |
22011 ms |
序號 |
純js |
純js分段 |
js+wasm |
js+wasm分段 |
1 |
16298 ms |
19441 ms |
27322 ms |
19233 ms |
2 |
11593 ms |
29424 ms |
28955 ms |
18602 ms |
3 |
24589 ms |
28685 ms |
28192 ms |
18472 ms |
4 |
24725 ms |
29892 ms |
28931 ms |
18260 ms |
5 |
24695 ms |
31453 ms |
36166 ms |
19474 ms |
序號 |
純js |
純js分段 |
js+wasm |
js+wasm分段 |
1 |
19856 ms |
19591 ms |
21259 ms |
15920 ms |
2 |
15119 ms |
26283 ms |
20821 ms |
15634 ms |
3 |
21387 ms |
25861 ms |
22473 ms |
16893 ms |
4 |
19550 ms |
25797 ms |
21793 ms |
17239 ms |
5 |
20363 ms |
26402 ms |
20782 ms |
15786 ms |
序號 |
純js |
純js分段 |
js+wasm |
js+wasm分段 |
1 |
6449 ms |
12169 ms |
22856 ms |
16621 ms |
2 |
14695 ms |
17558 ms |
19147 ms |
18014 ms |
3 |
17792 ms |
20326 ms |
17203 ms |
14683 ms |
4 |
18094 ms |
16452 ms |
18396 ms |
14399 ms |
5 |
15830 ms |
19006 ms |
19241 ms |
14119 ms |
序號 |
純js |
純js分段 |
js+wasm |
js+wasm分段 |
1 |
5003 ms |
9441 ms |
16233 ms |
9252 ms |
2 |
6240 ms |
14917 ms |
11145 ms |
9316 ms |
3 |
8563 ms |
10849 ms |
12653 ms |
10963 ms |
4 |
10261 ms |
12155 ms |
11607 ms |
9108 ms |
5 |
8775 ms |
11138 ms |
9869 ms |
10451 ms |
序號 |
純js |
純js分段 |
js+wasm |
js+wasm分段 |
1 |
4632 ms |
7721 ms |
9590 ms |
7887 ms |
2 |
5858 ms |
3312 ms |
7161 ms |
7963 ms |
3 |
2859 ms |
10808 ms |
7646 ms |
7973 ms |
4 |
3531 ms |
8614 ms |
7904 ms |
8197 ms |
5 |
5744 ms |
7612 ms |
7131 ms |
10714 ms |
序號 |
純js |
純js分段 |
js+wasm |
js+wasm分段 |
1 |
3329 ms |
5884 ms |
9318 ms |
7270 ms |
2 |
7222 ms |
9917 ms |
6897 ms |
7096 ms |
3 |
2602 ms |
6066 ms |
6295 ms |
6908 ms |
4 |
2757 ms |
6662 ms |
6551 ms |
8164 ms |
5 |
2509 ms |
8730 ms |
7126 ms |
7039 ms |
序號 |
純js |
純js分段 |
js+wasm |
js+wasm分段 |
1 |
2721 ms |
2824 ms |
6557 ms |
5019 ms |
2 |
3241 ms |
6867 ms |
4943 ms |
5026 ms |
3 |
1803 ms |
3012 ms |
4902 ms |
5052 ms |
4 |
1930 ms |
3010 ms |
5007 ms |
5022 ms |
5 |
1807 ms |
2885 ms |
4881 ms |
5238 ms |
序號 |
純js |
js+wasm |
1 |
6406 ms |
3358 ms |
2 |
6435 ms |
3599 ms |
3 |
6450 ms |
3283 ms |
4 |
6286 ms |
3952 ms |
5 |
6408 ms |
3207 ms |
序號 |
純js |
js+wasm |
1 |
3497 ms |
1705 ms |
2 |
3412 ms |
1643 ms |
3 |
3263 ms |
1825 ms |
4 |
3284 ms |
1710 ms |
5 |
3376 ms |
1768 ms |
序號 |
純js |
js+wasm |
1 |
1873 ms |
923 ms |
2 |
1776 ms |
928 ms |
3 |
1772 ms |
913 ms |
4 |
1682 ms |
923 ms |
5 |
1742 ms |
898 ms |
序號 |
純js |
js+wasm |
1 |
1043 ms |
516 ms |
2 |
877 ms |
479 ms |
3 |
907 ms |
504 ms |
4 |
872 ms |
459 ms |
5 |
865 ms |
495 ms |
序號 |
純js |
js+wasm |
1 |
487 ms |
209 ms |
2 |
387 ms |
209 ms |
3 |
410 ms |
225 ms |
4 |
512 ms |
268 ms |
5 |
399 ms |
225 ms |
序號 |
純js |
js+wasm |
1 |
147 ms |
92 ms |
2 |
133 ms |
90 ms |
3 |
177 ms |
94 ms |
4 |
157 ms |
42 ms |
5 |
175 ms |
84 ms |
序號 |
純js |
js+wasm |
1 |
71 ms |
20 ms |
2 |
66 ms |
24 ms |
3 |
45 ms |
33 ms |
4 |
80 ms |
30 ms |
5 |
97 ms |
29 ms |
firefox (版本號:103.0.1 (64 位))
- 2048M 加載到52%時頁面崩潰
- 采用Blob.slice方式分段計算
- 每512M為一段,測試5次
序號 |
純js分段 |
js+wasm分段 |
1 |
51398 ms |
17338 ms |
2 |
41282 ms |
16385 ms |
3 |
42358 ms |
16966 ms |
4 |
43363 ms |
15843 ms |
5 |
40802 ms |
16551 ms |
- 1792M 加載到59%時頁面崩潰
- 采用Blob.slice方式分段計算
- 每512M為一段,測試5次
序號 |
純js分段 |
js+wasm分段 |
1 |
33690 ms |
13251 ms |
2 |
37423 ms |
13636 ms |
3 |
42903 ms |
13487 ms |
4 |
32684 ms |
13662 ms |
5 |
36691 ms |
14984 ms |
- 1536M 加載到69%時頁面崩潰
- 采用Blob.slice方式分段計算
- 每512M為一段,測試5次
序號 |
純js分段 |
js+wasm分段 |
1 |
28051 ms |
11425 ms |
2 |
27822 ms |
11337 ms |
3 |
28331 ms |
12508 ms |
4 |
30089 ms |
11520 ms |
5 |
32890 ms |
11507 ms |
- 1280M 加載到83%時頁面崩潰
- 采用Blob.slice方式分段
- 計算512M為一段
序號 |
純js分段 |
js+wasm分段 |
1 |
25680 ms |
9571 ms |
2 |
23956 ms |
9549 ms |
3 |
28829 ms |
10070 ms |
4 |
23518 ms |
9449 ms |
5 |
23200 ms |
9540 ms |
序號 |
純js |
js+wasm |
1 |
38277 ms |
7776 ms |
2 |
40936 ms |
11254 ms |
3 |
29861 ms |
7653 ms |
4 |
25630 ms |
7517 ms |
5 |
18934 ms |
11443 ms |
6 |
24849 ms |
8039 ms |
7 |
18214 ms |
7727 ms |
8 |
18617 ms |
12987 ms |
9 |
33281 ms |
7523 ms |
10 |
40757 ms |
8895 ms |
序號 |
純js |
js+wasm |
1 |
22752 ms |
8605 ms |
2 |
16669 ms |
9313 ms |
3 |
15716 ms |
6678 ms |
4 |
16940 ms |
6521 ms |
5 |
16732 ms |
9269 ms |
6 |
15805 ms |
6582 ms |
7 |
15718 ms |
6519 ms |
8 |
15795 ms |
9377 ms |
9 |
15641 ms |
6773 ms |
10 |
15622 ms |
7489 ms |
序號 |
純js |
js+wasm |
1 |
15181 ms |
8333 ms |
2 |
14031 ms |
5880 ms |
3 |
14214 ms |
5987 ms |
4 |
33812 ms |
5935 ms |
5 |
14167 ms |
8666 ms |
6 |
14666 ms |
8031 ms |
7 |
28640 ms |
5991 ms |
8 |
13992 ms |
5840 ms |
9 |
13926 ms |
6032 ms |
10 |
14216 ms |
6637 ms |
序號 |
純js |
js+wasm |
1 |
11418 ms |
4457 ms |
2 |
11199 ms |
5370 ms |
3 |
10717 ms |
4654 ms |
4 |
10607 ms |
4436 ms |
5 |
10611 ms |
4479 ms |
6 |
10718 ms |
4368 ms |
7 |
10560 ms |
5494 ms |
8 |
11519 ms |
5044 ms |
9 |
10802 ms |
4426 ms |
10 |
11779 ms |
4971 ms |
序號 |
純js |
js+wasm |
1 |
8362 ms |
2981 ms |
2 |
7516 ms |
2999 ms |
3 |
7335 ms |
3030 ms |
4 |
7357 ms |
3150 ms |
5 |
7444 ms |
3001 ms |
6 |
8456 ms |
3223 ms |
7 |
7376 ms |
3120 ms |
8 |
7313 ms |
3072 ms |
9 |
7349 ms |
3240 ms |
10 |
7447 ms |
3352 ms |
序號 |
純js |
js+wasm |
1 |
4066 ms |
1525 ms |
2 |
4440 ms |
1516 ms |
3 |
4223 ms |
1510 ms |
4 |
3916 ms |
1610 ms |
5 |
3917 ms |
1509 ms |
6 |
4028 ms |
1588 ms |
7 |
3964 ms |
1514 ms |
8 |
4037 ms |
1507 ms |
9 |
3957 ms |
1506 ms |
10 |
3987 ms |
1642 ms |
序號 |
純js |
js+wasm |
1 |
2280 ms |
761 ms |
2 |
2331 ms |
820 ms |
3 |
2193 ms |
798 ms |
4 |
2242 ms |
777 ms |
5 |
2197 ms |
752 ms |
6 |
2330 ms |
769 ms |
7 |
2236 ms |
758 ms |
8 |
2364 ms |
798 ms |
9 |
2278 ms |
783 ms |
10 |
2384 ms |
785 ms |
序號 |
純js |
js+wasm |
1 |
1366 ms |
397 ms |
2 |
1355 ms |
378 ms |
3 |
1445 ms |
460 ms |
4 |
1468 ms |
437 ms |
5 |
1417 ms |
406 ms |
6 |
1525 ms |
478 ms |
7 |
1381 ms |
393 ms |
8 |
1450 ms |
430 ms |
9 |
1417 ms |
428 ms |
10 |
1378 ms |
431 ms |
序號 |
純js |
js+wasm |
1 |
921 ms |
168 ms |
2 |
871 ms |
162 ms |
3 |
859 ms |
163 ms |
4 |
864 ms |
162 ms |
5 |
1025 ms |
177 ms |
6 |
910 ms |
158 ms |
7 |
904 ms |
150 ms |
8 |
931 ms |
187 ms |
9 |
1014 ms |
182 ms |
10 |
871 ms |
159 ms |
序號 |
純js |
js+wasm |
1 |
127 ms |
48 ms |
2 |
124 ms |
50 ms |
3 |
140 ms |
44 ms |
4 |
129 ms |
47 ms |
5 |
127 ms |
51 ms |
6 |
129 ms |
50 ms |
7 |
126 ms |
46 ms |
8 |
119 ms |
54 ms |
9 |
121 ms |
46 ms |
10 |
118 ms |
50 ms |
序號 |
純js |
js+wasm |
1 |
46 ms |
18 ms |
2 |
41 ms |
22 ms |
3 |
43 ms |
13 ms |
4 |
40 ms |
15 ms |
5 |
44 ms |
11 ms |
6 |
47 ms |
15 ms |
7 |
42 ms |
11 ms |
8 |
42 ms |
20 ms |
9 |
45 ms |
13 ms |
10 |
44 ms |
16 ms |
分段計算測試代碼
純js
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>文件md5</title>
<script src="./SparkMD5.js"></script>
</head>
<body>
<input id="file" type="file" />
<script>
document.querySelector('#file').addEventListener('change', e => {
let startTime = Date.now()
const file = e.target.files[0];
const fileReader = new FileReader()
console.log('size', file.size / 1024 / 1024 / 1024, "G")
fileReader.onprogress = e => {
console.log(`${Math.floor((e.loaded / e.total) * 100)}%`)
}
let usedTime = 0
const md5 = new SparkMD5();
let index = 0
const chunkSize = 512 * 1024 * 1024;//file.size / count
let count = Math.ceil(file.size / chunkSize)
console.log('分幾份', count)
loadSliceFile();
function loadSliceFile() {
const sliceFile = file.slice(index * chunkSize, index * chunkSize + chunkSize)
fileReader.readAsBinaryString(sliceFile);
}
fileReader.onload = e => {
index += 1;
md5.appendBinary(e.target.result);
if (index < count) {
loadSliceFile()
}
else {
const md5Str = md5.end()
usedTime += Date.now() - startTime
console.log('usedTime', usedTime, 'ms')
console.log('md5', md5Str)
}
}
});
</script>
</body>
</html>
js+wasm
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>文件md5</title>
<script src="./wasm_exec.js"></script>
<!-- <script src="./wasm_exec_tiny.js"></script> -->
</head>
<body>
<script>
function handleSayHello(message) {
console.lof('str from go', message)
}
const go = new Go();
WebAssembly.instantiateStreaming(fetch('wasm/md5.wasm'), go.importObject)
.then(res => {
go.run(res.instance); // 執行 golang裡 main 方法
});
</script>
<input id="file" type="file" />
<script>
document.querySelector('#file').addEventListener('change', e => {
let startTime = Date.now()
const file = e.target.files[0];
const fileReader = new FileReader()
console.log('size', file.size / 1024 / 1024 / 1024, "G")
fileReader.onprogress = e => {
console.log(`${Math.floor((e.loaded / e.total) * 100)}%`)
}
let usedTime = 0
let index = 0
const sliceSize = 512
const chunkSize = sliceSize * 1024 * 1024;//file.size / count
let count = Math.ceil(file.size / chunkSize)
console.log('分幾份', count)
loadSliceFile();
function loadSliceFile() {
const sliceFile = file.slice(index * chunkSize, index * chunkSize + chunkSize)
fileReader.readAsArrayBuffer(sliceFile);
}
fileReader.onload = e => {
index += 1;
const bytes = new Uint8Array(e.target.result)
wasmMd5Add(bytes)
if (index < count) {
loadSliceFile()
}
else {
const md5Hash = wasmMd5End()
usedTime += Date.now() - startTime
console.log('usedTime', usedTime, 'ms')
console.log('md5', md5Hash)
}
}
});
</script>
</body>
</html>
測試結論
firefox
- 超過1G的文件,直接崩潰,隻能通過分段計算最終合並計算
- 從1M到2G,wasm的速度是純js計算的2-3倍
- 20M,wasm是純js的 6倍
chrome
- 0-400M時,wasm是純js的2-3倍
- 600M-1024M時,純js不分段比wasm要快
- 分段js比不分段wasm快一點點
- 分段js比分段wasm慢一點點
- 1280M,差不太多
- 大於1280M,js比wasm分段慢
- 對於js,分段要慢一些
- 對於wasm,分段要快一些
最終結論
- chrome對js的優化,使得在600M-1024M期間的大文件純js計算md5速度要快於wasm,其他范圍還是wasm性能好一些
- 由於firefox超過1G就崩潰瞭,所以我們平時寫代碼時,還是要做分段加載的。
- 業務中,還是可以使用wasm來提升性能的
- 可以針對 chrome與其他瀏覽器來制作不同的方案
- 其實golang 計算md5基本上是js的7-9倍,但js給wasm復制數據的時間占用瞭太多,導致wasm被降低瞭速度,文件越大,復制時間越長,越慢
wasm 還是可以使用的,眾觀全局,速度提升2-3倍。chrome可以針對性處理
以上就是wasm+js實現文件獲取md5示例詳解的詳細內容,更多關於wasm js獲取md5的資料請關註WalkonNet其它相關文章!
推薦閱讀: