tinygo icon indicating copy to clipboard operation
tinygo copied to clipboard

WASM program does not respond when repeatedly creating big data slices

Open Mrooze-zeng opened this issue 4 years ago • 5 comments

I am new to Go and want to use wasm to calculate the md5 of a file, but I found that I can only calculate a 45Mb file twice, and then it will be stopped on this line. dst := make([]byte, args[0].Get("length").Int())I have to refresh the browser to make it work again. Is the memory usage high or what is the reason? I do not know. Is there a way to release it?

I compiled the same code with Go's built-in WASM and it worked well.

My tinygo version: tinygo version 0.19.0 darwin/amd64 (using go version go1.16.5 and LLVM version 11.0.0)

Here is the my code:

main.go:

package main

import (
	"crypto/md5"
	"fmt"
	"syscall/js"
)

func getMD5() js.Func {
	return js.FuncOf(func(this js.Value, args []js.Value) interface{} {
		uint8Array := js.Global().Get("Uint8Array")
		uint8ClampedArray := js.Global().Get("Uint8ClampedArray")
		if len(args) < 1 || !(args[0].InstanceOf(uint8Array) || args[0].InstanceOf(uint8ClampedArray)) {
			return js.Undefined()
		}
		//stop on this line;
		dst := make([]byte, args[0].Get("length").Int())

		js.CopyBytesToGo(dst, args[0])
		if dst == nil {
			return js.Undefined()
		}
		return fmt.Sprintf("%x", md5.Sum(dst))
	})
}

func main() {
	js.Global().Set("getMd5", getMD5())
	select {}
}

index.html:

<!DOCTYPE html>
<html>
<head>
  <meta charset="UTF-8" />
  <title>Hello World - Go</title>
</head>
<body>
  <input
    type="file"
    id="j-file"
  >
  <button id="j-cal">btn</button>
  <script src="./wasm_exec.js"></script>
  <script
    type="module"
    src="./index.js"
  ></script>
</body>
</html>

index.js

(async function () {
  const go = new Go()
  let app
  if (WebAssembly.instantiateStreaming) {
    app = await WebAssembly.instantiateStreaming(fetch("app.wasm"), go.importObject)
  } else {
    const res = await fetch("app.wasm");
    app = await WebAssembly.instantiate(await res.arrayBuffer(), go.importObject)
  }
  const { instance } = app;
  go.run(instance)
  const $input = document.getElementById("j-file")
  const $btn = document.getElementById("j-cal")

  $btn.addEventListener("click", function () {
    const file = $input.files[0] || new Blob([]);
    const fileReader = new FileReader()
    fileReader.onload = function () {
      console.log((window.getMd5 || function () { })(new Uint8Array(this.result)))
    }
    fileReader.readAsArrayBuffer(file)
  })
})()

build command:

tinygo build -o ./demo/app.wasm -target wasm .

Mrooze-zeng avatar Jul 02 '21 12:07 Mrooze-zeng

What is the error that you get in the JavaScript console (developer tools)?

aykevl avatar Jul 02 '21 16:07 aykevl

What is the error that you get in the JavaScript console (developer tools)?

There is no error, but the process is getting blocked.

Mrooze-zeng avatar Jul 02 '21 16:07 Mrooze-zeng

What is the error that you get in the JavaScript console (developer tools)?

Sorry to copy and paste the code with issues, I have corrected it. But when I calculate the MD5 of a file larger than 45MB for the second time without refreshing the page, it is still blocked.

Mrooze-zeng avatar Jul 03 '21 03:07 Mrooze-zeng

@Mrooze-zeng have you found a solution? Currently experiencing same problem while repeatedly processing large files (~9MB)

vanelizarov avatar Sep 09 '21 23:09 vanelizarov

I might suggest using a tinygo //export function to do the MD5 instead of re-buffering. Since the memory is shared, you can use an offset/len pair to update the hasher. I think you can find some WebAssembly hash functions that do similarly even if their source isn't TinyGo. Ex. https://github.com/Daninet/hash-wasm

codefromthecrypt avatar Sep 07 '22 08:09 codefromthecrypt