ggerganov commited on
Commit
68dae1f
·
unverified ·
1 Parent(s): d161cee

bench.wasm : same as "bench" but runs in the browser (#89)

Browse files
README.md CHANGED
@@ -459,7 +459,7 @@ Some of the examples are even ported to run in the browser using WebAssembly. Ch
459
  | Example | Web | Description |
460
  | --- | --- | --- |
461
  | [main](examples/main) | [whisper.wasm](examples/whisper.wasm) | Tool for translating and transcribing audio using Whisper |
462
- | [bench](examples/bench) | | Benchmark the performance of Whisper on your machine |
463
  | [stream](examples/stream) | [stream.wasm](examples/stream.wasm) | Real-time transcription of raw microphone capture |
464
  | [command](examples/command) | [command.wasm](examples/command.wasm) | Basic voice assistant example for receiving voice commands from the mic |
465
  | [talk](examples/talk) | [talk.wasm](examples/talk.wasm) | Talk with a GPT-2 bot |
 
459
  | Example | Web | Description |
460
  | --- | --- | --- |
461
  | [main](examples/main) | [whisper.wasm](examples/whisper.wasm) | Tool for translating and transcribing audio using Whisper |
462
+ | [bench](examples/bench) | [bench.wasm](examples/bench.wasm) | Benchmark the performance of Whisper on your machine |
463
  | [stream](examples/stream) | [stream.wasm](examples/stream.wasm) | Real-time transcription of raw microphone capture |
464
  | [command](examples/command) | [command.wasm](examples/command.wasm) | Basic voice assistant example for receiving voice commands from the mic |
465
  | [talk](examples/talk) | [talk.wasm](examples/talk.wasm) | Talk with a GPT-2 bot |
bindings/javascript/whisper.js CHANGED
The diff for this file is too large to render. See raw diff
 
examples/CMakeLists.txt CHANGED
@@ -23,6 +23,7 @@ if (EMSCRIPTEN)
23
  add_subdirectory(stream.wasm)
24
  add_subdirectory(command.wasm)
25
  add_subdirectory(talk.wasm)
 
26
  else()
27
  add_subdirectory(main)
28
  add_subdirectory(stream)
 
23
  add_subdirectory(stream.wasm)
24
  add_subdirectory(command.wasm)
25
  add_subdirectory(talk.wasm)
26
+ add_subdirectory(bench.wasm)
27
  else()
28
  add_subdirectory(main)
29
  add_subdirectory(stream)
examples/bench.wasm/CMakeLists.txt ADDED
@@ -0,0 +1,47 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #
2
+ # libbench
3
+ #
4
+
5
+ set(TARGET libbench)
6
+
7
+ add_executable(${TARGET}
8
+ emscripten.cpp
9
+ )
10
+
11
+ target_link_libraries(${TARGET} PRIVATE
12
+ whisper
13
+ )
14
+
15
+ unset(EXTRA_FLAGS)
16
+
17
+ if (WHISPER_WASM_SINGLE_FILE)
18
+ set(EXTRA_FLAGS "-s SINGLE_FILE=1")
19
+ message(STATUS "Embedding WASM inside bench.js")
20
+
21
+ add_custom_command(
22
+ TARGET ${TARGET} POST_BUILD
23
+ COMMAND ${CMAKE_COMMAND} -E copy
24
+ ${CMAKE_BINARY_DIR}/bin/libbench.js
25
+ ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/bench.wasm/bench.js
26
+ )
27
+ endif()
28
+
29
+ set_target_properties(${TARGET} PROPERTIES LINK_FLAGS " \
30
+ --bind \
31
+ -s USE_PTHREADS=1 \
32
+ -s PTHREAD_POOL_SIZE=8 \
33
+ -s INITIAL_MEMORY=1024MB \
34
+ -s TOTAL_MEMORY=1024MB \
35
+ -s FORCE_FILESYSTEM=1 \
36
+ -s EXPORTED_RUNTIME_METHODS=\"['print', 'printErr', 'ccall', 'cwrap']\" \
37
+ ${EXTRA_FLAGS} \
38
+ ")
39
+
40
+ #
41
+ # bench.wasm
42
+ #
43
+
44
+ set(TARGET bench.wasm)
45
+
46
+ configure_file(${CMAKE_CURRENT_SOURCE_DIR}/index-tmpl.html ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/${TARGET}/index.html @ONLY)
47
+ configure_file(${CMAKE_CURRENT_SOURCE_DIR}/../helpers.js ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/${TARGET}/helpers.js @ONLY)
examples/bench.wasm/README.md ADDED
@@ -0,0 +1,22 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # bench.wasm
2
+
3
+ Benchmark the performance of whisper.cpp in the browser using WebAssembly
4
+
5
+ Link: https://whisper.ggerganov.com/bench/
6
+
7
+ Terminal version: [examples/bench](/examples/bench)
8
+
9
+ ## Build instructions
10
+
11
+ ```bash
12
+ # build using Emscripten (v3.1.2)
13
+ git clone https://github.com/ggerganov/whisper.cpp
14
+ cd whisper.cpp
15
+ mkdir build-em && cd build-em
16
+ emcmake cmake ..
17
+ make -j
18
+
19
+ # copy the produced page to your HTTP path
20
+ cp bin/bench.wasm/* /path/to/html/
21
+ cp bin/libbench.worker.js /path/to/html/
22
+ ```
examples/bench.wasm/emscripten.cpp ADDED
@@ -0,0 +1,80 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #include "whisper.h"
2
+
3
+ #include <emscripten.h>
4
+ #include <emscripten/bind.h>
5
+
6
+ #include <cmath>
7
+ #include <string>
8
+ #include <thread>
9
+ #include <vector>
10
+
11
+ constexpr int N_THREAD = 8;
12
+
13
+ // TODO: get rid of this vector of contexts - bad idea in the first place
14
+ std::vector<struct whisper_context *> g_contexts(4, nullptr);
15
+
16
+ std::thread g_worker;
17
+
18
+ void bench_main(size_t index) {
19
+ const int n_threads = std::min(N_THREAD, (int) std::thread::hardware_concurrency());
20
+
21
+ // whisper context
22
+ auto & ctx = g_contexts[index];
23
+
24
+ fprintf(stderr, "%s: running benchmark with %d threads - please wait...\n", __func__, n_threads);
25
+
26
+ if (int ret = whisper_set_mel(ctx, nullptr, 0, WHISPER_N_MEL)) {
27
+ fprintf(stderr, "error: failed to set mel: %d\n", ret);
28
+ return;
29
+ }
30
+
31
+ if (int ret = whisper_encode(ctx, 0, n_threads) != 0) {
32
+ fprintf(stderr, "error: failed to encode model: %d\n", ret);
33
+ return;
34
+ }
35
+
36
+ whisper_print_timings(ctx);
37
+
38
+ fprintf(stderr, "\n");
39
+ fprintf(stderr, "If you wish, you can submit these results here:\n");
40
+ fprintf(stderr, "\n");
41
+ fprintf(stderr, " https://github.com/ggerganov/whisper.cpp/issues/89\n");
42
+ fprintf(stderr, "\n");
43
+ fprintf(stderr, "Please include the following information:\n");
44
+ fprintf(stderr, "\n");
45
+ fprintf(stderr, " - CPU model\n");
46
+ fprintf(stderr, " - Operating system\n");
47
+ fprintf(stderr, " - Browser\n");
48
+ fprintf(stderr, "\n");
49
+ }
50
+
51
+ EMSCRIPTEN_BINDINGS(bench) {
52
+ emscripten::function("init", emscripten::optional_override([](const std::string & path_model) {
53
+ for (size_t i = 0; i < g_contexts.size(); ++i) {
54
+ if (g_contexts[i] == nullptr) {
55
+ g_contexts[i] = whisper_init(path_model.c_str());
56
+ if (g_contexts[i] != nullptr) {
57
+ if (g_worker.joinable()) {
58
+ g_worker.join();
59
+ }
60
+ g_worker = std::thread([i]() {
61
+ bench_main(i);
62
+ });
63
+
64
+ return i + 1;
65
+ } else {
66
+ return (size_t) 0;
67
+ }
68
+ }
69
+ }
70
+
71
+ return (size_t) 0;
72
+ }));
73
+
74
+ emscripten::function("free", emscripten::optional_override([](size_t index) {
75
+ if (index < g_contexts.size()) {
76
+ whisper_free(g_contexts[index]);
77
+ g_contexts[index] = nullptr;
78
+ }
79
+ }));
80
+ }
examples/bench.wasm/index-tmpl.html ADDED
@@ -0,0 +1,227 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!doctype html>
2
+ <html lang="en-us">
3
+ <head>
4
+ <title>bench : Benchmark whisper.cpp performance in the browser</title>
5
+
6
+ <style>
7
+ #output {
8
+ width: 100%;
9
+ height: 100%;
10
+ margin: 0 auto;
11
+ margin-top: 10px;
12
+ border-left: 0px;
13
+ border-right: 0px;
14
+ padding-left: 0px;
15
+ padding-right: 0px;
16
+ display: block;
17
+ background-color: black;
18
+ color: white;
19
+ font-size: 10px;
20
+ font-family: 'Lucida Console', Monaco, monospace;
21
+ outline: none;
22
+ white-space: pre;
23
+ overflow-wrap: normal;
24
+ overflow-x: scroll;
25
+ }
26
+ </style>
27
+ </head>
28
+ <body>
29
+ <div id="main-container">
30
+ <b>bench : Benchmark whisper.cpp performance in the browser</b>
31
+
32
+ <br><br>
33
+
34
+ You can find more about this project on <a href="https://github.com/ggerganov/whisper.cpp/tree/master/examples/bench.wasm">GitHub</a>.
35
+
36
+ <br><br>
37
+
38
+ <hr>
39
+
40
+ Select the model you would like to use and click the "Bench" button.<br>
41
+ The results will be displayed in the textarea below.
42
+
43
+ <br><br>
44
+
45
+ <div id="model-whisper">
46
+ Whisper model: <span id="model-whisper-status"></span>
47
+ <button id="fetch-whisper-tiny-en" onclick="loadWhisper('tiny.en')">tiny.en (75 MB)</button>
48
+ <button id="fetch-whisper-base-en" onclick="loadWhisper('base.en')">base.en (142 MB)</button>
49
+ <span id="fetch-whisper-progress"></span>
50
+
51
+ <input type="file" id="whisper-file" name="file" onchange="loadFile(event, 'whisper.bin')" />
52
+ </div>
53
+
54
+ <br>
55
+
56
+ <div id="input">
57
+ <button id="bench" onclick="onBench()" disabled>Bench</button>
58
+ <button id="clear" onclick="clearCache()">Clear Cache</button>
59
+ </div>
60
+
61
+ <hr>
62
+
63
+ Debug output:
64
+ <textarea id="output" rows="20"></textarea>
65
+
66
+ <br>
67
+
68
+ <b>Troubleshooting</b>
69
+
70
+ <br><br>
71
+
72
+ The page does some heavy computations, so make sure:
73
+
74
+ <ul>
75
+ <li>To use a modern web browser (e.g. Chrome, Firefox)</li>
76
+ <li>To use a fast desktop or laptop computer (i.e. not a mobile phone)</li>
77
+ <li>Your browser supports WASM <a href="https://webassembly.org/roadmap/">Fixed-width SIMD</a></li>
78
+ </ul>
79
+
80
+ <div class="cell-version">
81
+ <span>
82
+ |
83
+ Build time: <span class="nav-link">@GIT_DATE@</span> |
84
+ Commit hash: <a class="nav-link" href="https://github.com/ggerganov/whisper.cpp/commit/@GIT_SHA1@">@GIT_SHA1@</a> |
85
+ Commit subject: <span class="nav-link">@GIT_COMMIT_SUBJECT@</span> |
86
+ <a class="nav-link" href="https://github.com/ggerganov/whisper.cpp/tree/master/examples/bench.wasm">Source Code</a> |
87
+ </span>
88
+ </div>
89
+ </div>
90
+
91
+ <script type="text/javascript" src="helpers.js"></script>
92
+ <script type='text/javascript'>
93
+ // the bench instance
94
+ var instance = null;
95
+
96
+ // model name
97
+ var model_whisper = null;
98
+
99
+ var Module = {
100
+ print: printTextarea,
101
+ printErr: printTextarea,
102
+ setStatus: function(text) {
103
+ printTextarea('js: ' + text);
104
+ },
105
+ monitorRunDependencies: function(left) {
106
+ },
107
+ preRun: function() {
108
+ printTextarea('js: Preparing ...');
109
+ },
110
+ postRun: function() {
111
+ printTextarea('js: Initialized successfully!');
112
+ }
113
+ };
114
+
115
+ //
116
+ // fetch models
117
+ //
118
+
119
+ let dbVersion = 1
120
+ let dbName = 'whisper.ggerganov.com';
121
+ let indexedDB = window.indexedDB || window.mozIndexedDB || window.webkitIndexedDB || window.msIndexedDB
122
+
123
+ function storeFS(fname, buf) {
124
+ // write to WASM file using FS_createDataFile
125
+ // if the file exists, delete it
126
+ try {
127
+ Module.FS_unlink(fname);
128
+ } catch (e) {
129
+ // ignore
130
+ }
131
+
132
+ Module.FS_createDataFile("/", fname, buf, true, true);
133
+
134
+ printTextarea('storeFS: stored model: ' + fname + ' size: ' + buf.length);
135
+
136
+ model_whisper = fname;
137
+
138
+ document.getElementById('model-whisper-status').innerHTML = 'loaded "' + model_whisper + '"!';
139
+
140
+ if (model_whisper != null) {
141
+ document.getElementById('bench').disabled = false;
142
+ }
143
+ }
144
+
145
+ function loadFile(event, fname) {
146
+ var file = event.target.files[0] || null;
147
+ if (file == null) {
148
+ return;
149
+ }
150
+
151
+ printTextarea("loadFile: loading model: " + file.name + ", size: " + file.size + " bytes");
152
+ printTextarea('loadFile: please wait ...');
153
+
154
+ var reader = new FileReader();
155
+ reader.onload = function(event) {
156
+ var buf = new Uint8Array(reader.result);
157
+ storeFS(fname, buf);
158
+ }
159
+ reader.readAsArrayBuffer(file);
160
+
161
+ document.getElementById('fetch-whisper-tiny-en').style.display = 'none';
162
+ document.getElementById('fetch-whisper-base-en').style.display = 'none';
163
+ document.getElementById('whisper-file' ).style.display = 'none';
164
+ document.getElementById('model-whisper-status' ).innerHTML = 'loaded model: ' + file.name;
165
+ }
166
+
167
+ function loadWhisper(model) {
168
+ let urls = {
169
+ 'tiny.en': 'https://whisper.ggerganov.com/ggml-model-whisper-tiny.en.bin',
170
+ 'base.en': 'https://whisper.ggerganov.com/ggml-model-whisper-base.en.bin',
171
+ };
172
+
173
+ let sizes = {
174
+ 'tiny.en': 75,
175
+ 'base.en': 142,
176
+ };
177
+
178
+ let url = urls[model];
179
+ let dst = 'whisper.bin';
180
+ let size_mb = sizes[model];
181
+
182
+ document.getElementById('fetch-whisper-tiny-en').style.display = 'none';
183
+ document.getElementById('fetch-whisper-base-en').style.display = 'none';
184
+ document.getElementById('model-whisper-status').innerHTML = 'loading "' + model + '" ... ';
185
+
186
+ cbProgress = function(p) {
187
+ let el = document.getElementById('fetch-whisper-progress');
188
+ el.innerHTML = Math.round(100*p) + '%';
189
+ };
190
+
191
+ cbCancel = function() {
192
+ var el;
193
+ el = document.getElementById('fetch-whisper-tiny-en'); if (el) el.style.display = 'inline-block';
194
+ el = document.getElementById('fetch-whisper-base-en'); if (el) el.style.display = 'inline-block';
195
+ el = document.getElementById('model-whisper-status'); if (el) el.innerHTML = '';
196
+ };
197
+
198
+ loadRemote(url, dst, size_mb, cbProgress, storeFS, cbCancel, printTextarea);
199
+ }
200
+
201
+ //
202
+ // main
203
+ //
204
+
205
+ function onBench() {
206
+ if (instance) {
207
+ Module.free(instance);
208
+ }
209
+
210
+ instance = Module.init('whisper.bin');
211
+
212
+ if (instance) {
213
+ printTextarea("js: whisper initialized, instance: " + instance);
214
+ }
215
+
216
+ document.getElementById('bench').disabled = true;
217
+
218
+ if (!instance) {
219
+ printTextarea("js: failed to initialize whisper");
220
+ return;
221
+ }
222
+ }
223
+
224
+ </script>
225
+ <script type="text/javascript" src="bench.js"></script>
226
+ </body>
227
+ </html>
examples/bench/README.md CHANGED
@@ -1,6 +1,8 @@
1
  # bench
2
 
3
- A very basic tool for benchmarking the inference performance on your device. The tool simply runs the Encoder part of the transformer on some random audio data and records the execution time. This way we can have an objective comparison of the performance of the model for various setups.
 
 
4
 
5
  Benchmark results are tracked in the following Github issue: https://github.com/ggerganov/whisper.cpp/issues/89
6
 
 
1
  # bench
2
 
3
+ A very basic tool for benchmarking the inference performance on your device. The tool simply runs the Encoder part of
4
+ the transformer on some random audio data and records the execution time. This way we can have an objective comparison
5
+ of the performance of the model for various setups.
6
 
7
  Benchmark results are tracked in the following Github issue: https://github.com/ggerganov/whisper.cpp/issues/89
8
 
extra/deploy-wasm.sh CHANGED
@@ -25,6 +25,7 @@ scp bin/whisper.wasm/* root@linode0:/var/www/html/whisper/ && scp bin/li
25
  scp bin/stream.wasm/* root@linode0:/var/www/html/whisper/stream/ && scp bin/libstream.worker.js root@linode0:/var/www/html/whisper/stream/
26
  scp bin/command.wasm/* root@linode0:/var/www/html/whisper/command/ && scp bin/libcommand.worker.js root@linode0:/var/www/html/whisper/command/
27
  scp bin/talk.wasm/* root@linode0:/var/www/html/whisper/talk/ && scp bin/libtalk.worker.js root@linode0:/var/www/html/whisper/talk/
 
28
 
29
  echo "Done"
30
  exit
 
25
  scp bin/stream.wasm/* root@linode0:/var/www/html/whisper/stream/ && scp bin/libstream.worker.js root@linode0:/var/www/html/whisper/stream/
26
  scp bin/command.wasm/* root@linode0:/var/www/html/whisper/command/ && scp bin/libcommand.worker.js root@linode0:/var/www/html/whisper/command/
27
  scp bin/talk.wasm/* root@linode0:/var/www/html/whisper/talk/ && scp bin/libtalk.worker.js root@linode0:/var/www/html/whisper/talk/
28
+ scp bin/bench.wasm/* root@linode0:/var/www/html/whisper/bench/ && scp bin/libbench.worker.js root@linode0:/var/www/html/whisper/bench/
29
 
30
  echo "Done"
31
  exit