-
-
Notifications
You must be signed in to change notification settings - Fork 84
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
Improve the performance of composing computations #128
Conversation
Codecov Report
@@ Coverage Diff @@
## master #128 +/- ##
=====================================
Coverage 100% 100%
=====================================
Files 47 48 +1
Lines 1088 1097 +9
=====================================
+ Hits 1088 1097 +9
Continue to review full report at Codecov.
|
src/internal/list.js
Outdated
this.head = head; | ||
this.tail = tail; | ||
this.size = tail.size + 1; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@safareli, you once suggested using a List over an Array. You may be interested in seeing it. :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nice!
@@ -11,6 +11,6 @@ export const ordinal = ['first', 'second', 'third', 'fourth', 'fifth']; | |||
|
|||
export const namespace = 'fluture'; | |||
export const name = 'Future'; | |||
export const version = 2; | |||
export const version = 3; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@davidchambers Note how I'm incrementing the version of the Fluture @@type
. This is because in changing the internal structure of the Sequence
instances, I've broken backwards compatibility. interpret
expects _actions
to be a List
now, and not an Array
.
Despite this technically being a "breaking change", I will not release it as a major version, but use a minor version instead. Detecting incompatible versions is really just there to improve error messages.
src/internal/list.js
Outdated
|
||
List.prototype.isEmpty = false; | ||
|
||
List.prototype.add = function List$add(x){ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why add
rather than cons
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Because I have no idea about conventions. ;)
⚡
src/internal/list.js
Outdated
Empty.prototype.tail = null; | ||
Empty.prototype.size = 0; | ||
|
||
export default new Empty; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I find this export confusing. I would not expect require('./internal/list')
to give me this value.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's because I'm implementing the bare minimum, so to construct a list one does list.add(1).add(2).add(3)
. No need to export the internals, or a specialized constructor.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I suggest renaming the file empty.js, in that case.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You don't need Empty constructor, can just have special object created from List.prototype with desired props.
let str = this._spawn.toString(), tail = this._actions; | ||
|
||
while(!tail.isEmpty){ | ||
str = `${str}.${tail.head.toString()}`; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we use Z.toString
here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We can safely avoid unnecessary dispatching, because we know that every item in this._actions
has a custom toString
function defined which we want to use. If not, that would be a bug in Fluture.
src/internal/list.js
Outdated
empty.isEmpty = true; | ||
empty.size = 0; | ||
|
||
export default empty; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@davidchambers I'm still quite fond of exporting empty
from list.js
. Essentially we're exporting []
and :
(cons
), and those are all the necessary building blocks for creating lists. Not only do I feel like this is elegant in a way, it's also quite convenient because I don't have to do anything before using it the way I want to in core.js
.
@@ -0,0 +1,2 @@ | |||
export const empty = ({isEmpty: true, size: 0, head: null, tail: null}); | |||
export const cons = (head, tail) => ({isEmpty: false, size: tail.size + 1, head, tail}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Surprisingly, defining the list type like this has zero impact on performance. What do you think @davidchambers and @safareli? I think it addresses both of your points of feedback. :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Beautiful! 🦄
The last thing to consider is s/empty/nil/
to avoid confusion with Z.empty
.
The parens around the object literal assigned to empty
are not required (although you may have included them for aesthetic reasons).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I did include them for aesthetic reasons. :)
I'll keep it as empty, as I consider it more descriptive within the context of a list. I renamed it emptyList
during import to core.js
, to preserve context.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nice
* #128 The performance of transforming Futures has improved significantly. * #131 Future.parallel has been made stack-safe. * #131 Future.parallel now guarantees execution order. * #132 Improve version interoperability. Note that 6.3.x futures will not be compatible with 6.2.x Futures, so when upgrading, make sure to upgrade all producers of Future instances. To avoid having to do this with future releases, I recommend setting Fluture as a devDependency if you are maintaining a library which exposes Future instances to its users.
Instead of using an array, and forcing it to be "immutable" by using
.concat([item])
, I've defined a very simple immutable linked list and use that to build up the list of operations to perform during interpretation.As an example, I took the stack safety example from Fluture's own README:
Before
$ time node test.js 100001 real 0m30.145s user 0m23.878s sys 0m8.083s
After
$ time node test.js 100001 real 0m0.153s user 0m0.148s sys 0m0.019s
For this type of script, the performance improves significantly. In practice, this improvement will probably be encountered in scripts using this pattern I've seen here and there:
The general performance as measured by the Doxbee testing suite has improved by about 11%.