Skip to content

Commit 6fa74b3

Browse files
authored
Make file packager --no-heap-copy always apply, and remove the option (#12027)
The old default was to copy a file package into main memory (into space malloc'd for it). That was not memory-efficient as it meant the main memory needed to be bigger, and also the memory could never be freed. While mmap on that could be fast, we added the --no-heap-copy option to keep file data on the JS heap and not copy it in. That uses less memory but still is just as fast at fread etc. This makes that newer option the default, and removes the option to do it the old way. This is better for memory usage, and in any case people using file data in a heavy way may be rolling their own solutions anyhow. This also removes the file system usage of getMemory(), which would perform allocation during startup from JS, which is a source of complexity I am working to remove for WebAssembly/binaryen#3043
1 parent b8e5396 commit 6fa74b3

File tree

6 files changed

+37
-78
lines changed

6 files changed

+37
-78
lines changed

ChangeLog.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,9 @@ See docs/process.md for how version tagging works.
1717

1818
Current Trunk
1919
-------------
20+
- Enable `--no-heap-copy` file packager option by default, and remove the old
21+
defualt behavior entirely. That is the behavior we should have had from the
22+
beginning as it is more memory-efficient.
2023
- `--no-entry` is now required in `STANDALONE_WASM` mode when building a reactor
2124
(application without a main function). Previously exporting a list of
2225
functions that didn't include `_main` would imply this. Now the list of

emcc.py

Lines changed: 1 addition & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -243,7 +243,6 @@ def __init__(self):
243243
self.memory_profiler = False
244244
self.memory_init_file = None
245245
self.use_preload_cache = False
246-
self.no_heap_copy = False
247246
self.use_preload_plugins = False
248247
self.proxy_to_worker = False
249248
self.default_object_extension = '.o'
@@ -2112,13 +2111,6 @@ def get_final():
21122111
with ToolchainProfiler.profile_block('source transforms'):
21132112
# Embed and preload files
21142113
if len(options.preload_files) or len(options.embed_files):
2115-
2116-
# Also, MEMFS is not aware of heap resizing feature in wasm, so if MEMFS and memory growth are used together, force
2117-
# no_heap_copy to be enabled.
2118-
if shared.Settings.ALLOW_MEMORY_GROWTH and not options.no_heap_copy:
2119-
logger.info('Enabling --no-heap-copy because -s ALLOW_MEMORY_GROWTH=1 is being used with file_packager.py (pass --no-heap-copy to suppress this notification)')
2120-
options.no_heap_copy = True
2121-
21222114
logger.debug('setting up files')
21232115
file_args = ['--from-emcc', '--export-name=' + shared.Settings.EXPORT_NAME]
21242116
if len(options.preload_files):
@@ -2132,8 +2124,6 @@ def get_final():
21322124
file_args += options.exclude_files
21332125
if options.use_preload_cache:
21342126
file_args.append('--use-preload-cache')
2135-
if options.no_heap_copy:
2136-
file_args.append('--no-heap-copy')
21372127
if shared.Settings.LZ4:
21382128
file_args.append('--lz4')
21392129
if options.use_preload_plugins:
@@ -2420,7 +2410,7 @@ def consume_arg():
24202410
options.use_preload_cache = True
24212411
newargs[i] = ''
24222412
elif newargs[i].startswith('--no-heap-copy'):
2423-
options.no_heap_copy = True
2413+
diagnostics.warning('legacy-settings', 'ignoring legacy flag --no-heap-copy (that is the only mode supported now)')
24242414
newargs[i] = ''
24252415
elif newargs[i].startswith('--use-preload-plugins'):
24262416
options.use_preload_plugins = True

src/library_memfs.js

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -289,15 +289,6 @@ mergeInto(LibraryManager.library, {
289289
// memory buffer, as they may get invalidated. That means we
290290
// need to do copy its contents.
291291
if (buffer.buffer === HEAP8.buffer) {
292-
#if ASSERTIONS
293-
// FIXME: this is inefficient as the file packager may have
294-
// copied the data into memory already - we may want to
295-
// integrate more there and let the file packager loading
296-
// code be able to query if memory growth is on or off.
297-
if (canOwn) {
298-
warnOnce('file packager has copied file data into memory, but in memory growth we are forced to copy it again (see --no-heap-copy)');
299-
}
300-
#endif // ASSERTIONS
301292
canOwn = false;
302293
}
303294
#endif // ALLOW_MEMORY_GROWTH

tests/test_browser.py

Lines changed: 28 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -257,7 +257,6 @@ def make_main(path):
257257
make_main(dstpath)
258258
self.compile_btest(['main.cpp', '--preload-file', srcpath, '-o', 'page.html'])
259259
self.run_browser('page.html', 'You should see |load me right before|.', '/report_result?1')
260-
# Test that '--no-heap-copy' works.
261260
if WINDOWS:
262261
# On Windows, the following non-alphanumeric non-control code ASCII characters are supported.
263262
# The characters <, >, ", |, ?, * are not allowed, because the Windows filesystem doesn't support those.
@@ -268,7 +267,7 @@ def make_main(path):
268267
open(os.path.join(self.get_dir(), tricky_filename), 'w').write('''load me right before running the code please''')
269268
make_main(tricky_filename)
270269
# As an Emscripten-specific feature, the character '@' must be escaped in the form '@@' to not confuse with the 'src@dst' notation.
271-
self.compile_btest(['main.cpp', '--preload-file', tricky_filename.replace('@', '@@'), '--no-heap-copy', '-o', 'page.html'])
270+
self.compile_btest(['main.cpp', '--preload-file', tricky_filename.replace('@', '@@'), '-o', 'page.html'])
272271
self.run_browser('page.html', 'You should see |load me right before|.', '/report_result?1')
273272

274273
# By absolute path
@@ -2435,8 +2434,7 @@ def test_preload_module(self):
24352434

24362435
def test_mmap_file(self):
24372436
create_test_file('data.dat', 'data from the file ' + ('.' * 9000))
2438-
for extra_args in [[], ['--no-heap-copy']]:
2439-
self.btest(path_from_root('tests', 'mmap_file.c'), expected='1', args=['--preload-file', 'data.dat'] + extra_args)
2437+
self.btest(path_from_root('tests', 'mmap_file.c'), expected='1', args=['--preload-file', 'data.dat'])
24402438

24412439
def test_emrun_info(self):
24422440
if not has_browser():
@@ -2819,36 +2817,35 @@ def test_sdl2_image_formats(self):
28192817
'-DBITSPERPIXEL=24', '-DNO_PRELOADED', '-s', 'USE_SDL=2', '-s', 'USE_SDL_IMAGE=2', '-s', 'SDL2_IMAGE_FORMATS=["jpg"]'])
28202818

28212819
def test_sdl2_key(self):
2822-
for defines in [[]]:
2823-
create_test_file('pre.js', '''
2824-
Module.postRun = function() {
2825-
function doOne() {
2826-
Module._one();
2827-
setTimeout(doOne, 1000/60);
2828-
}
2820+
create_test_file('pre.js', '''
2821+
Module.postRun = function() {
2822+
function doOne() {
2823+
Module._one();
28292824
setTimeout(doOne, 1000/60);
28302825
}
2826+
setTimeout(doOne, 1000/60);
2827+
}
28312828
2832-
function keydown(c) {
2833-
var event = new KeyboardEvent("keydown", { 'keyCode': c, 'charCode': c, 'view': window, 'bubbles': true, 'cancelable': true });
2834-
var prevented = !document.dispatchEvent(event);
2835-
2836-
//send keypress if not prevented
2837-
if (!prevented) {
2838-
var event = new KeyboardEvent("keypress", { 'keyCode': c, 'charCode': c, 'view': window, 'bubbles': true, 'cancelable': true });
2839-
document.dispatchEvent(event);
2840-
}
2841-
}
2829+
function keydown(c) {
2830+
var event = new KeyboardEvent("keydown", { 'keyCode': c, 'charCode': c, 'view': window, 'bubbles': true, 'cancelable': true });
2831+
var prevented = !document.dispatchEvent(event);
28422832
2843-
function keyup(c) {
2844-
var event = new KeyboardEvent("keyup", { 'keyCode': c, 'charCode': c, 'view': window, 'bubbles': true, 'cancelable': true });
2833+
//send keypress if not prevented
2834+
if (!prevented) {
2835+
var event = new KeyboardEvent("keypress", { 'keyCode': c, 'charCode': c, 'view': window, 'bubbles': true, 'cancelable': true });
28452836
document.dispatchEvent(event);
28462837
}
2847-
''')
2848-
create_test_file('sdl2_key.c', self.with_report_result(open(path_from_root('tests', 'sdl2_key.c')).read()))
2838+
}
28492839
2850-
self.compile_btest(['sdl2_key.c', '-o', 'page.html'] + defines + ['-s', 'USE_SDL=2', '--pre-js', 'pre.js', '-s', '''EXPORTED_FUNCTIONS=['_main', '_one']'''])
2851-
self.run_browser('page.html', '', '/report_result?37182145')
2840+
function keyup(c) {
2841+
var event = new KeyboardEvent("keyup", { 'keyCode': c, 'charCode': c, 'view': window, 'bubbles': true, 'cancelable': true });
2842+
document.dispatchEvent(event);
2843+
}
2844+
''')
2845+
create_test_file('sdl2_key.c', self.with_report_result(open(path_from_root('tests', 'sdl2_key.c')).read()))
2846+
2847+
self.compile_btest(['sdl2_key.c', '-o', 'page.html', '-s', 'USE_SDL=2', '--pre-js', 'pre.js', '-s', '''EXPORTED_FUNCTIONS=['_main', '_one']'''])
2848+
self.run_browser('page.html', '', '/report_result?37182145')
28522849

28532850
def test_sdl2_text(self):
28542851
create_test_file('pre.js', '''
@@ -4629,13 +4626,10 @@ def test_access_file_after_heap_resize(self):
46294626
self.compile_btest(['page.c', '-s', 'WASM=1', '-s', 'ALLOW_MEMORY_GROWTH=1', '--preload-file', 'test.txt', '-o', 'page.html'])
46304627
self.run_browser('page.html', 'hello from file', '/report_result?15')
46314628

4632-
# with separate file packager invocation, letting us affect heap copying
4633-
# or lack thereof
4634-
for file_packager_args in [[], ['--no-heap-copy']]:
4635-
print(file_packager_args)
4636-
self.run_process([PYTHON, FILE_PACKAGER, 'data.js', '--preload', 'test.txt', '--js-output=' + 'data.js'] + file_packager_args)
4637-
self.compile_btest(['page.c', '-s', 'WASM=1', '-s', 'ALLOW_MEMORY_GROWTH=1', '--pre-js', 'data.js', '-o', 'page.html', '-s', 'FORCE_FILESYSTEM=1'])
4638-
self.run_browser('page.html', 'hello from file', '/report_result?15')
4629+
# with separate file packager invocation
4630+
self.run_process([PYTHON, FILE_PACKAGER, 'data.js', '--preload', 'test.txt', '--js-output=' + 'data.js'])
4631+
self.compile_btest(['page.c', '-s', 'WASM=1', '-s', 'ALLOW_MEMORY_GROWTH=1', '--pre-js', 'data.js', '-o', 'page.html', '-s', 'FORCE_FILESYSTEM=1'])
4632+
self.run_browser('page.html', 'hello from file', '/report_result?15')
46394633

46404634
def test_unicode_html_shell(self):
46414635
create_test_file('main.cpp', self.with_report_result(r'''

tests/test_core.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5834,7 +5834,7 @@ def test_mmap(self):
58345834
self.do_run_in_out_file_test('tests', 'core', 'test_mmap.c')
58355835

58365836
def test_mmap_file(self):
5837-
for extra_args in [[], ['--no-heap-copy']]:
5837+
for extra_args in [[]]:
58385838
self.emcc_args += ['--embed-file', 'data.dat'] + extra_args
58395839
x = 'data from the file........'
58405840
s = ''

tools/file_packager.py

Lines changed: 4 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222
2323
Usage:
2424
25-
file_packager.py TARGET [--preload A [B..]] [--embed C [D..]] [--exclude E [F..]]] [--js-output=OUTPUT.js] [--no-force] [--use-preload-cache] [--indexedDB-name=EM_PRELOAD_CACHE] [--no-heap-copy] [--separate-metadata] [--lz4] [--use-preload-plugins]
25+
file_packager.py TARGET [--preload A [B..]] [--embed C [D..]] [--exclude E [F..]]] [--js-output=OUTPUT.js] [--no-force] [--use-preload-cache] [--indexedDB-name=EM_PRELOAD_CACHE] [--separate-metadata] [--lz4] [--use-preload-plugins]
2626
2727
--preload ,
2828
--embed See emcc --help for more details on those options.
@@ -42,10 +42,6 @@
4242
4343
--indexedDB-name Use specified IndexedDB database name (Default: 'EM_PRELOAD_CACHE')
4444
45-
--no-heap-copy If specified, the preloaded filesystem is not copied inside the Emscripten HEAP, but kept in a separate typed array outside it.
46-
The default, if this is not specified, is to embed the VFS inside the HEAP, so that mmap()ing files in it is a no-op.
47-
Passing this flag optimizes for fread() usage, omitting it optimizes for mmap() usage.
48-
4945
--separate-metadata Stores package metadata separately. Only applicable when preloading and js-output file is specified.
5046
5147
--lz4 Uses LZ4. This compresses the data using LZ4 when this utility is run, then the client decompresses chunks on the fly, avoiding storing
@@ -81,7 +77,7 @@
8177
import json
8278

8379
if len(sys.argv) == 1:
84-
print('''Usage: file_packager.py TARGET [--preload A [B..]] [--embed C [D..]] [--exclude E [F..]]] [--js-output=OUTPUT.js] [--no-force] [--use-preload-cache] [--indexedDB-name=EM_PRELOAD_CACHE] [--no-heap-copy] [--separate-metadata] [--lz4] [--use-preload-plugins]
80+
print('''Usage: file_packager.py TARGET [--preload A [B..]] [--embed C [D..]] [--exclude E [F..]]] [--js-output=OUTPUT.js] [--no-force] [--use-preload-cache] [--indexedDB-name=EM_PRELOAD_CACHE] [--separate-metadata] [--lz4] [--use-preload-plugins]
8581
See the source for more details.''')
8682
sys.exit(0)
8783

@@ -177,12 +173,6 @@ def main():
177173
# offline cache instead.
178174
use_preload_cache = False
179175
indexeddb_name = 'EM_PRELOAD_CACHE'
180-
# If set to True, the blob received from XHR is moved to the Emscripten HEAP,
181-
# optimizing for mmap() performance (if ALLOW_MEMORY_GROWTH=0).
182-
# If set to False, the XHR blob is kept intact, and fread()s etc. are performed
183-
# directly to that data. This optimizes for minimal memory usage and fread()
184-
# performance.
185-
heap_copy = True
186176
# If set to True, the package metadata is stored separately from js-output
187177
# file which makes js-output file immutable to the package content changes.
188178
# If set to False, the package metadata is stored inside the js-output file
@@ -209,7 +199,7 @@ def main():
209199
indexeddb_name = arg.split('=', 1)[1] if '=' in arg else None
210200
leading = ''
211201
elif arg == '--no-heap-copy':
212-
heap_copy = False
202+
print('ignoring legacy flag --no-heap-copy (that is the only mode supported now)')
213203
leading = ''
214204
elif arg == '--separate-metadata':
215205
separate_metadata = True
@@ -497,16 +487,7 @@ def was_seen(name):
497487
if has_preloaded:
498488
if not lz4:
499489
# Get the big archive and split it up
500-
if heap_copy:
501-
use_data = '''
502-
// copy the entire loaded file into a spot in the heap. Files will refer to slices in that. They cannot be freed though
503-
// (we may be allocating before malloc is ready, during startup).
504-
var ptr = Module['getMemory'](byteArray.length);
505-
Module['HEAPU8'].set(byteArray, ptr);
506-
DataRequest.prototype.byteArray = Module['HEAPU8'].subarray(ptr, ptr+byteArray.length);
507-
'''
508-
else:
509-
use_data = '''
490+
use_data = '''
510491
// Reuse the bytearray from the XHR as the source for file reads.
511492
DataRequest.prototype.byteArray = byteArray;
512493
'''

0 commit comments

Comments
 (0)