Skip to content

Conversation

@annxingyuan
Copy link
Contributor

@annxingyuan annxingyuan commented Jul 20, 2020

This fixes #3718

Changes

  • adds support for multithreading in the WASM backend.
  • deprecates setWasmPath in favor of setWasmPaths which allows users to specify location of multiple WASM binaries.
  • generates Emscripten modules for vanilla / SIMD in standalone mode so the same module can be used for both vanilla and SIMD.

Bundle sizes:

master

  • WASM plain
    • js bundle size: 200k
    • WASM binary size: 155k
  • WASM SIMD
    • js bundle size: 200k
    • WASM binary size: 213k

threads_wasm

  • WASM plain
    • js bundle size: 251k
    • WASM binary size: 156k
  • WASM SIMD
    • js bundle size: 251k
    • WASM binary size: 214k
  • WASM threaded SIMD
    • js bundle size: 251k
    • WASM binary size: 230k

To see the logs from the Cloud Build CI, please join either our discussion or announcement mailing list.


This change is Reviewable

@annxingyuan annxingyuan requested a review from Maratyszcza July 22, 2020 14:45
@annxingyuan
Copy link
Contributor Author

Hey @Maratyszcza - just wanted to add you as an FYI - no need to fully review :)

[`var _scriptDir = undefined; var WasmBackendModuleSimd = ` +
wasmFactoryThreadedSimd.toString()],
{type: 'text/javascript'});
// } else if (simdSupported) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you still need this else if branch. E.g. Chrome Android supports only SIMD, but not threads

}

size_t xnn_operator_count = 0;
pthreadpool *threadpool = pthreadpool_create(4);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is best to not hardcode the number of threads, but get it dynamically from emscripten_num_logical_cores. I suggest to use std::min(emscripten_num_logical_cores(), 4).

@annxingyuan annxingyuan mentioned this pull request Aug 4, 2020
Copy link
Contributor

@tafsiri tafsiri left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed 16 of 32 files at r1, 17 of 25 files at r3.
Reviewable status: 0 of 1 approvals obtained (waiting on @annxingyuan, @lina128, @pyu10055, and @tafsiri)


tfjs-backend-wasm/README.md, line 193 at r3 (raw file):

### Do you support SIMD and multi-threading?
Yes. We take advantage of SIMD and multi-threading wherever they are supported by testing the capabilities of your runtime and serving the appropriate WASM binary. If you intend to serve the WASM binaries from a custom location (via `setWasmPaths`), please note that the SIMD-enabled and threading-enabled binaries are separate from the vanilla binary.

nit: serving => loading.
vanilla binary => regular (or non-simd/non-threaded) wasm binary


tfjs-backend-wasm/scripts/build-wasm.sh, line 33 at r3 (raw file):

# Threaded + SIMD build.
yarn bazel build -c opt //src/cc:tfjs-backend-wasm-threaded-simd.js --config=wasm --copt="-pthread" --copt="-msimd128"

Question, during local development which build is used in testing? I'm guessing it could depend on your browser? Is there a way to split these out so that locally you don't have to build everything for a local test run?


tfjs-backend-wasm/src/backend_wasm.ts, line 208 at r3 (raw file):

function getWasmPath(
    simdSupported: boolean, threadsSupported: boolean, prefix = '') {
  let path = 'tfjs-backend-wasm.wasm';

could this have a more descriptive name, reading this section it looks more like this is wasmModuleName or something like this, and together with a prefix that forms a path.

Related to naming should prefix be called path or even folderPath/wasmModuleFolder for more clarity? I think prefix is too ambiguous.

Also small nit here could you move this below the early return just below.


tfjs-backend-wasm/src/backend_wasm.ts, line 228 at r3 (raw file):

    }

    console.warn(

I think this should be an error. If a user is trying to customize this, they should probably fail fast rather than fallback to something they may not anticipate. Also the if they have custom paths the default location may still not have the expected file.


tfjs-backend-wasm/src/backend_wasm.ts, line 234 at r3 (raw file):

  }

  if (wasmPathPrefix != null) {

Why not just call this function with wasmPathPrefix as the value for prefix? (and default it to empty string), then you can remove the default value from this functions signature. Another confusion that this may help resolve for me is that as I read it, it looks like passing in prefix in factoryConfig.locateFile = (path, prefix) => { part cannot override wasmPathPrefix, makes wonder why its passed around (as opposed to just setting wasmPathPrefix).


tfjs-backend-wasm/src/backend_wasm.ts, line 351 at r3 (raw file):

let wasmPath: string = null;
let wasmPathPrefix: string = null;
let wasmFileMap: {[key: string]: string} = {};

could you enumerate the valid keys? i'm assuming they are some fixed set that the loader will look for.


tfjs-backend-wasm/src/backend_wasm.ts, line 404 at r3 (raw file):

/** @doc {heading: 'Environment', namespace: 'wasm'} */
export function setWasmPaths(
    prefix: string, fileMap: {[key: string]: string} = {},

are prefix and fileMap exclusive params? can you pass both and have that work? If not (which is my suspicion since the doc says its full paths in fileMap), can you take one param that is either a string or your filemap. Then a user does not need to pass null if they want to use fileMap.


tfjs-backend-wasm/src/flags_wasm.ts, line 42 at r3 (raw file):

    new MessageChannel().port1.postMessage(new SharedArrayBuffer(1));
    return WebAssembly.validate(new Uint8Array([
      0, 97, 115, 109, 1, 0,  0,  0, 1, 4, 1,  96, 0,   0,  3, 2, 1,  0, 5,

Just wondering, is there significance to these specific numbers? if so i'd mention that in the comment so that people aren't tempted to change it in future.


tfjs-backend-wasm/src/cc/backend.cc, line 64 at r3 (raw file):

pthreadpool *threadpool =
    pthreadpool_create(std::min(std::max(num_cores, 1), 4));

nit: could we turn the 4 into a named variable.

Copy link
Contributor Author

@annxingyuan annxingyuan left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewable status: 0 of 1 approvals obtained (waiting on @annxingyuan, @lina128, @Maratyszcza, @pyu10055, and @tafsiri)


tfjs-backend-wasm/README.md, line 193 at r3 (raw file):

Previously, tafsiri (Yannick Assogba) wrote…

nit: serving => loading.
vanilla binary => regular (or non-simd/non-threaded) wasm binary

Done


tfjs-backend-wasm/scripts/build-wasm.sh, line 33 at r3 (raw file):

Previously, tafsiri (Yannick Assogba) wrote…

Question, during local development which build is used in testing? I'm guessing it could depend on your browser? Is there a way to split these out so that locally you don't have to build everything for a local test run?

It will depend on your browser, but for the near term it should end up using the regular binary. I added an option yarn test-dev to build only the regular binary.


tfjs-backend-wasm/src/backend_wasm.ts, line 249 at r2 (raw file):

Previously, Maratyszcza (Marat Dukhan) wrote…

I think you still need this else if branch. E.g. Chrome Android supports only SIMD, but not threads

Done

Although now we don't need the else if branch because in standalone mode the same module works for SIMD and vanilla.


tfjs-backend-wasm/src/backend_wasm.ts, line 208 at r3 (raw file):

Previously, tafsiri (Yannick Assogba) wrote…

could this have a more descriptive name, reading this section it looks more like this is wasmModuleName or something like this, and together with a prefix that forms a path.

Related to naming should prefix be called path or even folderPath/wasmModuleFolder for more clarity? I think prefix is too ambiguous.

Also small nit here could you move this below the early return just below.

Done


tfjs-backend-wasm/src/backend_wasm.ts, line 228 at r3 (raw file):

Previously, tafsiri (Yannick Assogba) wrote…

I think this should be an error. If a user is trying to customize this, they should probably fail fast rather than fallback to something they may not anticipate. Also the if they have custom paths the default location may still not have the expected file.

What if the user just wants to supply a custom URL for one of the 3 binaries though?


tfjs-backend-wasm/src/backend_wasm.ts, line 234 at r3 (raw file):

Previously, tafsiri (Yannick Assogba) wrote…

Why not just call this function with wasmPathPrefix as the value for prefix? (and default it to empty string), then you can remove the default value from this functions signature. Another confusion that this may help resolve for me is that as I read it, it looks like passing in prefix in factoryConfig.locateFile = (path, prefix) => { part cannot override wasmPathPrefix, makes wonder why its passed around (as opposed to just setting wasmPathPrefix).

Done

I think it still makes sense to have wasmPathPrefix default to null though, to distinguish between it being unset, and a user setting it to empty string? That way if locateFile is called with a prefix and wasmPathPrefix has not been set, we can pass in the prefix to getPathToWasmBinary.


tfjs-backend-wasm/src/backend_wasm.ts, line 351 at r3 (raw file):

Previously, tafsiri (Yannick Assogba) wrote…

could you enumerate the valid keys? i'm assuming they are some fixed set that the loader will look for.

Done


tfjs-backend-wasm/src/backend_wasm.ts, line 404 at r3 (raw file):

Previously, tafsiri (Yannick Assogba) wrote…

are prefix and fileMap exclusive params? can you pass both and have that work? If not (which is my suspicion since the doc says its full paths in fileMap), can you take one param that is either a string or your filemap. Then a user does not need to pass null if they want to use fileMap.

Done

Copy link
Contributor

@tafsiri tafsiri left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

Reviewed 1 of 25 files at r3, 5 of 6 files at r4.
Reviewable status: :shipit: complete! 1 of 1 approvals obtained (waiting on @annxingyuan, @lina128, @Maratyszcza, and @pyu10055)


tfjs-backend-wasm/scripts/build-wasm.sh, line 33 at r3 (raw file):

Previously, annxingyuan (Ann Yuan) wrote…

It will depend on your browser, but for the near term it should end up using the regular binary. I added an option yarn test-dev to build only the regular binary.

Thanks!


tfjs-backend-wasm/src/backend_wasm.ts, line 228 at r3 (raw file):

Previously, annxingyuan (Ann Yuan) wrote…

What if the user just wants to supply a custom URL for one of the 3 binaries though?

ah so in this scheme the user can set one custom wasm module file path to whatever they want and the rest will be resolved against a default wasmModuleFolder value? (which i guess is the empty string) and they need to make sure those binaries are at that location? That makes sense.

I wonder if you can warn about this when reading the wasmFileMap. I say this because unless they dev tests in all these environments they will only see one warning (for whatever their main browser is) and may not realize that the others are falling back (or indeed may come into play at all) until they deploy. Just a suggestion for you to think about.


tfjs-backend-wasm/src/backend_wasm.ts, line 351 at r3 (raw file):

Previously, annxingyuan (Ann Yuan) wrote…

Done

So this will give you typescript checking, but you may want to add a javascript run time check that it is one of those values if a fileMap is passed in. This might avoid issues being filed because of typos.


tfjs-backend-wasm/src/flags_wasm.ts, line 42 at r3 (raw file):

Previously, tafsiri (Yannick Assogba) wrote…

Just wondering, is there significance to these specific numbers? if so i'd mention that in the comment so that people aren't tempted to change it in future.

Still curious about this, adding a comment about these may be sufficient.

Copy link
Contributor Author

@annxingyuan annxingyuan left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewable status: :shipit: complete! 1 of 1 approvals obtained (waiting on @annxingyuan, @lina128, @Maratyszcza, @pyu10055, and @tafsiri)


tfjs-backend-wasm/src/backend_wasm.ts, line 228 at r3 (raw file):

Previously, tafsiri (Yannick Assogba) wrote…

ah so in this scheme the user can set one custom wasm module file path to whatever they want and the rest will be resolved against a default wasmModuleFolder value? (which i guess is the empty string) and they need to make sure those binaries are at that location? That makes sense.

I wonder if you can warn about this when reading the wasmFileMap. I say this because unless they dev tests in all these environments they will only see one warning (for whatever their main browser is) and may not realize that the others are falling back (or indeed may come into play at all) until they deploy. Just a suggestion for you to think about.

Yeah that makes a lot of sense! Done.


tfjs-backend-wasm/src/backend_wasm.ts, line 351 at r3 (raw file):

Previously, tafsiri (Yannick Assogba) wrote…

So this will give you typescript checking, but you may want to add a javascript run time check that it is one of those values if a fileMap is passed in. This might avoid issues being filed because of typos.

Done

The warning I just added to address the previous comment should take care of this case I think.


tfjs-backend-wasm/src/flags_wasm.ts, line 42 at r3 (raw file):

Previously, tafsiri (Yannick Assogba) wrote…

Still curious about this, adding a comment about these may be sufficient.

Done


tfjs-backend-wasm/src/cc/backend.cc, line 64 at r3 (raw file):

Previously, tafsiri (Yannick Assogba) wrote…

nit: could we turn the 4 into a named variable.

Done

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

It should be possible to specify custom paths for multiple WASM binaries.

4 participants