-
Notifications
You must be signed in to change notification settings - Fork 3.4k
Refactor Emval implementation to avoid JS heap allocations. #21205
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 3 commits
c252984
d90ee09
9639b41
9752e50
889193c
488f3a6
fdc625f
3c2e5e6
237bd2f
ece8ec5
a553a6f
6b45975
d823c3e
bb1b625
5e2f3bd
b06380f
1b54b12
a6586c1
d15c4fb
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -12,39 +12,39 @@ | |
/*jslint sub:true*/ /* The symbols 'fromWireType' and 'toWireType' must be accessed via array notation to be closure-safe since craftInvokerFunction crafts functions as strings that can't be closured. */ | ||
|
||
// -- jshint doesn't understand library syntax, so we need to mark the symbols exposed here | ||
/*global getStringOrSymbol, emval_handles, Emval, __emval_unregister, count_emval_handles, emval_symbols, __emval_decref*/ | ||
/*global getStringOrSymbol, emval_freelist, emval_handles, emval_handles_reserved, Emval, __emval_unregister, count_emval_handles, emval_symbols, __emval_decref*/ | ||
/*global emval_addMethodCaller, emval_methodCallers, addToLibrary, global, emval_lookupTypes, makeLegalFunctionName*/ | ||
/*global emval_get_global*/ | ||
|
||
var LibraryEmVal = { | ||
$emval_handles__deps: ['$HandleAllocator'], | ||
$emval_handles: "new HandleAllocator();", | ||
// Stack of handles available for reuse. | ||
$emval_freelist: [], | ||
// Array of alternating pairs (value, refcount). | ||
$emval_handles: [], | ||
// Number of handles reserved for non-use (0) or common values w/o refcount. | ||
$emval_handles_reserved: 5, | ||
$emval_symbols: {}, // address -> string | ||
|
||
$init_emval__deps: ['$count_emval_handles', '$emval_handles'], | ||
$init_emval__deps: ['$count_emval_handles', '$emval_handles', '$emval_handles_reserved'], | ||
$init_emval__postset: 'init_emval();', | ||
$init_emval: () => { | ||
// reserve some special values. These never get de-allocated. | ||
// The HandleAllocator takes care of reserving zero. | ||
emval_handles.allocated.push( | ||
{value: undefined}, | ||
{value: null}, | ||
{value: true}, | ||
{value: false}, | ||
// reserve 0 and some special values. These never get de-allocated. | ||
emval_handles.push( | ||
0, 0, | ||
undefined, 0, | ||
null, 0, | ||
true, 0, | ||
false, 0, | ||
); | ||
Object.assign(emval_handles, /** @lends {emval_handles} */ { reserved: emval_handles.allocated.length }), | ||
#if ASSERTIONS | ||
assert(emval_handles.length / 2 === emval_handles_reserved); | ||
#endif | ||
Module['count_emval_handles'] = count_emval_handles; | ||
}, | ||
|
||
$count_emval_handles__deps: ['$emval_handles'], | ||
$count_emval_handles__deps: ['$emval_freelist', '$emval_handles', '$emval_handles_reserved'], | ||
$count_emval_handles: () => { | ||
var count = 0; | ||
for (var i = emval_handles.reserved; i < emval_handles.allocated.length; ++i) { | ||
if (emval_handles.allocated[i] !== undefined) { | ||
++count; | ||
} | ||
} | ||
return count; | ||
return emval_handles.length / 2 - emval_handles_reserved - emval_freelist.length; | ||
}, | ||
|
||
_emval_register_symbol__deps: ['$emval_symbols', '$readLatin1String'], | ||
|
@@ -61,13 +61,16 @@ var LibraryEmVal = { | |
return symbol; | ||
}, | ||
|
||
$Emval__deps: ['$emval_handles', '$throwBindingError', '$init_emval'], | ||
$Emval__deps: ['$emval_freelist', '$emval_handles', '$throwBindingError', '$init_emval'], | ||
$Emval: { | ||
toValue: (handle) => { | ||
if (!handle) { | ||
throwBindingError('Cannot use deleted val. handle = ' + handle); | ||
} | ||
return emval_handles.get(handle).value; | ||
#if ASSERTIONS | ||
assert(handle === 1 || emval_handles[handle * 2] !== undefined, `invalid handle: ${handle}`); | ||
mrolig5267319 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
#endif | ||
return emval_handles[handle * 2]; | ||
}, | ||
|
||
toHandle: (value) => { | ||
|
@@ -77,23 +80,30 @@ var LibraryEmVal = { | |
case true: return 3; | ||
case false: return 4; | ||
default:{ | ||
return emval_handles.allocate({refcount: 1, value: value}); | ||
const handle = emval_freelist.pop() || emval_handles.length / 2; | ||
emval_handles[handle * 2] = value; | ||
emval_handles[handle * 2 + 1] = 1; | ||
return handle; | ||
} | ||
} | ||
} | ||
}, | ||
|
||
_emval_incref__deps: ['$emval_handles'], | ||
_emval_incref__deps: ['$emval_handles', '$emval_handles_reserved'], | ||
_emval_incref: (handle) => { | ||
if (handle > 4) { | ||
emval_handles.get(handle).refcount += 1; | ||
if (handle >= emval_handles_reserved) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should we just remove this line and initialize all the the reserved handles to refcount == 1? That will safe on code size and remove an extra branch here.. at the cost of redundantly storing/tracking the refcount of the reserved types (could could assert that the refcount of a reserved type never hits zero too). There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Was also on my mind, but was trying to minimize the changes. Seems better to minimize the code paths though. |
||
emval_handles[handle * 2 + 1] += 1; | ||
} | ||
}, | ||
|
||
_emval_decref__deps: ['$emval_handles'], | ||
_emval_decref__deps: ['$emval_freelist', '$emval_handles', '$emval_handles_reserved'], | ||
_emval_decref: (handle) => { | ||
if (handle >= emval_handles.reserved && 0 === --emval_handles.get(handle).refcount) { | ||
emval_handles.free(handle); | ||
if (handle >= emval_handles_reserved && 0 === --emval_handles[handle * 2 + 1]) { | ||
#if ASSERTIONS | ||
assert(emval_handles[handle * 2] !== undefined); | ||
#endif | ||
emval_handles[handle * 2] = undefined; | ||
emval_freelist.push(handle); | ||
} | ||
}, | ||
|
||
|
Uh oh!
There was an error while loading. Please reload this page.