-
-
Notifications
You must be signed in to change notification settings - Fork 1.8k
Faster implementation for Record #286
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
Comments
Yes! Thanks for writing up the whole draft of this, this has been on my wishlist for a while and I appreciate you documenting up the approach. |
I'm also interested in this. Under the hood, when you create many objects with the same properties, v8 makes the same optimization as suggested. I think it would perform best if we just use an normal JavaScript object for storage. I'm not sure about the copying cost though. Here's a good introduction to the topic: http://vimeo.com/43334972 |
@copy Making a copy of an object is very slow: #174 (comment) As shown in these benchmarks, making a copy of an array is much faster than making a copy of an object. This isn't surprising, since arrays are much simpler data structures than objects. |
@Pauan The copy function in that benchmark is pretty bad, using for-in instead of a function specific to the structure of the Record. Optimally, a Record with three properties function copy(p)
{
return {
x: p.x,
y: p.y,
z: p.z,
};
} It probably doesn't matter too much, but your solution has an indirection on every get/set. |
@copy Using You are right that there is a (very minor) indirection, but you'll note that even with the indirection, it blows the pants off of the other immutable data structures, and is close to mutable object speed for I re-ran the benchmarks, this time adding in a custom function as you suggested: https://github.com/Pauan/Immutable/blob/javascript/benchmarks/Record/2015-01-22
As you can see, your idea is slower for In any case, thanks for the idea (and the video), even if it didn't pan out in the end. I'm always willing to learn new techniques to make these data structures faster. |
After I posted that, I remembered that V8 has optimizations for constructors, so I added in |
Thank you for reporting this issue and appreciate your patience. We've notified the core team for an update on this issue. We're looking for a response within the next 30 days or the issue may be closed. |
This is a breaking change refactor of Record which no longer extends a collection type and therefore does not inherit sequence methods - which was a constant source of confusion and breakage. It also refactors the internals to no longer rely on a Map, but instead a fixed size List which should result in dramatically faster performance. This is also a breaking change as `delete()` (aka `remove()`) and `clear()` are no longer available - previously these methods reverted values to their default value, now that can be done with `set(k, undefined)`. This adds a predicate function `isRecord()` and also minorly refactors related functionality such as `Seq()` constructors. Fixes #505 Fixes #286
This is a breaking change refactor of Record which no longer extends a collection type and therefore does not inherit sequence methods - which was a constant source of confusion and breakage. It also refactors the internals to no longer rely on a Map, but instead a fixed size List which should result in dramatically faster performance. This is also a breaking change as `delete()` (aka `remove()`) and `clear()` are no longer available - previously these methods reverted values to their default value, now that can be done with `set(k, undefined)`. This adds a predicate function `isRecord()` and also minorly refactors related functionality such as `Seq()` constructors. Fixes #505 Fixes #286
* RFC: Refactor Record. No longer a Collection. Faster guts. This is a breaking change refactor of Record which no longer extends a collection type and therefore does not inherit sequence methods - which was a constant source of confusion and breakage. It also refactors the internals to no longer rely on a Map, but instead a fixed size List which should result in dramatically faster performance. This is also a breaking change as `delete()` (aka `remove()`) and `clear()` are no longer available - previously these methods reverted values to their default value, now that can be done with `set(k, undefined)`. This adds a predicate function `isRecord()` and also minorly refactors related functionality such as `Seq()` constructors. Fixes #505 Fixes #286 * Add perf test
* RFC: Refactor Record. No longer a Collection. Faster guts. This is a breaking change refactor of Record which no longer extends a collection type and therefore does not inherit sequence methods - which was a constant source of confusion and breakage. It also refactors the internals to no longer rely on a Map, but instead a fixed size List which should result in dramatically faster performance. This is also a breaking change as `delete()` (aka `remove()`) and `clear()` are no longer available - previously these methods reverted values to their default value, now that can be done with `set(k, undefined)`. This adds a predicate function `isRecord()` and also minorly refactors related functionality such as `Seq()` constructors. Fixes #505 Fixes #286 * Add perf test
@leebyron this is in the 4.0 releases now, yeah? Is it something that you've seen big enough performance gains with that it should be added to the release history? Others might be interested if so 😄 thanks! |
Record is currently implemented with a Map. But because it has a fixed size, and because the keys are always strings, it's possible to use a much simpler and faster implementation.
Each Record would have two properties,
_keys
and_values
._keys
is a mutable JavaScript object mapping string keys to indexes, and_values
is a mutable JavaScript array that contains the actual values.To lookup a key in the Record, you first look it up in
_keys
which gives you an index, and then you lookup that index in_values
.To set a key in a Record, you first look it up in
_keys
, which gives you an index. You then make a copy of the_values
array, mutate the index to be the new value, then return a new Record with the new_values
array.There are a few reasons this is fast:
_keys
at all: you can simply pass it as-is to the new Record, because it never changes.I have implemented this technique in my library, and you can find the benchmarks here:
https://github.com/Pauan/Immutable/blob/javascript/benchmarks/Record/2015-01-16
Getting is always super fast. Setting is fast until you reach around 1,000 keys or so, and it's extremely rare to have Records with 1,000+ keys in them, so that isn't a problem.
The text was updated successfully, but these errors were encountered: