You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
The automatic currying of functions is an awesome part of this language and functional in general. But in javascript it comes at a serious cost (particularly if your paradigm is to use shit tons of function application).
Some languages (Fay and Elm) flatten out curries at compile time if they are not used. So
add3 x y z = x + y + z
trace 1 2 3
instead of becoming:
var add3 = function(x){
return function(y){
return function(z){
return x + y + z;
};
};
};
trace(add3(1)(2)(3));
becomes:
var add3 = function(x,y,z){
return x + y + z;
};
trace(add3(1,2,3));
are there any plans to add optimization around currying? I believe this could be done without breaking changes.
@paf31 did some work on this here, however the result was mixed to say the least... it seemed to be slower than the curried version when we tried using it on purescript-in-purescript, in v8 anyway. We'll probably look into it again yet.
We currently have mkFn2, 3, 4... and runFn2, 3, 4... helpers in Prelude that we're going to add compiler support for in the near future (probably this week), but they'll have to be used manually rather than applied automatically.
Yeah, it definitely shouldn't be, so we need to spend some time on it to work out why. It could have been interfering with the optimizer or something, as that also runs on the JS AST at the moment.
I'd like to avoid automatically uncurrying functions if possible, but rather work on the Data.Function library in the optimizer, and have developers explicitly type their multiple argument functions.
@paf31 that makes sense from a 'writing code in purescript' perspective. But there is a very real performance cost to all the currying (see OP). All I am suggesting is a psc compiler flag to unroll curried functions as much as possible for performance. The default would be without this optimization of course.
Curried functions in JavaScript create a new function object for each intermediate function application. This has a cost in current JS virtual machines. I don't know any which can optimise it away, yet.
Well the multi arg functions stuff is now optimized in the Haskell compiler, so feel free to give that a try and see how that helps.
I'm closing this as a duplicate since the functionality is covered by multi arg functions, and I don't particularly want to uncurry anything automatically, as it makes the mapping from PureScript to JavaScript less explicit.
How does it not affect predictability? "A function becomes a JS function of one argument" (simple) becomes "a function becomes a JS function of one argument except when all of its call sites across all modules are fully applied" (not even modular).
Also, I don't see any rationale for your statement "only valuable as an application wide optimization".
Its only valuable as an appication wide optimization because the vast majority of code is curried. A version that has a limited impact via 'mkFn' does not address the real performance cost.
I agree that an uncurry optimization adds complexity, but more complex does not necissarily mean less predictable. My conception of how to impliment is not intended as perscriptive, there may be a simpler way to have the same benefit. It sounds like there is already a branch for this. Could I get sample output from that branch?
I may be able to help identify why the performance improvement did not materialize.
Actually, I just tried it out again. uncurried is ~200ms faster than the master of the same age on ps-in-ps after all... But, contrast that with ~750ms out of it by something much simpler - removing {} values for empty __superclasses values. There are almost certainly other things we should worry about before we start looking at the cost of function applications, even after the prototypes-with-typeclasses-and-misc-optimizations branch gets merged.
Looking a the jsperf results for cost in v8, it's 56% slower than uncurried, but we still 45m ops/sec out of that, it's not exactly slow. The Firefox case is interesting, as curried is 96% slower than uncurried, but then it's also a lot faster than v8 at function application... in fact, curried is almost as fast as uncurried in v8.
My approach of partially applying at call sites would fix the modularity issue, but I'm not sure if it will be as performant as what uncurried produces.
Fair warning: I made the uncurried branch as an experiment, I'm not happy with it, and it will take a lot of convincing to get me to merge it to master.
Warning appreciated. ;) I am just curious about the performance. If it really truly turns out that in a real world use case, the cost of function returns is as negligible as @garyb made it sound, then there is no need for the optimization.
Something progress has been made on this end: We've implemented an "uncurry" optimization in psc-bundle, which seems a more suitable place for it than the compiler itself.
Please do confirm if the optimization works for you, or share figures showing that it doesn't.
Our interest is to get this as an option marked experimental into the mainline bundle tool some time soon, if the community agrees it's the right way to go.
Reacted by Kirill Pertsev, Felix Schlitter, Pauan and James Brock
I think it makes a lot more sense to implement this inside psc-bundle. If we can gain confidence that it works as intended (the JS AST is a bit unstructured, after all), then I think we would be interested to merge it into master.
Oh my god yes!!! Make PureScript perform!!! This is HUGE!
On Sun, Apr 10, 2016 at 11:47 AM Phil Freeman notifications@github.com
wrote:
I think it makes a lot more sense to implement this inside psc-bundle.
If we can gain confidence that it works as intended (the JS AST is a bit
unstructured, after all), then I think we would be interested to merge it
into master.
—
You are receiving this because you were mentioned.
Reply to this email directly or view it on GitHub #479 (comment)
If anybody is still interested in this, rollup-plugin-purs now does both uncurrying and inlining. It can't uncurry every function, but it can uncurry most functions.
Reacted by John Winston, Felipe S. S. Schneider and Mohammad Hasani
Something progress has been made on this end: We've implemented an "uncurry" optimization in psc-bundle, which seems a more suitable place for it than the compiler itself.
Please do confirm if the optimization works for you, or share figures showing that it doesn't.
Our interest is to get this as an option marked experimental into the mainline bundle tool some time soon, if the community agrees it's the right way to go.
I have to agree that having an option that optimizes all functions to be uncurried as much as possible would be nice. During development, that likely wouldn't be desirable because debugging the outputted JS would be easier. However, if one is going to minify the outputted JS anyway in production, then performance and not readability is generally their concern. Also, if this is an option, then even those outputting code in production may choose not to use it whereas those where it's a fundamental business concern can use it.
At the very least, I think this is something that should be further discussed.
Given that functions using the primitives from Data.Function.Uncurried are currently optimized in this way, I have the impression that the best way forward would be to finish #4092 and then anyone can implement this as an external, whole-program CoreFn transformation that inserts the appropriate mkFn*/runFn* calls.
(This plan has the downside that, I think, we don't yet know what the pitfalls are in managing an ecosystem of external CoreFn transformations. I think we all expect that it'd be easier than managing a collection of built-in compiler options/extensions, but from a distance the two approaches have enough similarity that I could imagine they might share some of the same problems.)
Activity
garyb commentedon Jul 13, 2014
@paf31 did some work on this here, however the result was mixed to say the least... it seemed to be slower than the curried version when we tried using it on purescript-in-purescript, in v8 anyway. We'll probably look into it again yet.
We currently have
mkFn2, 3, 4... andrunFn2, 3, 4... helpers in Prelude that we're going to add compiler support for in the near future (probably this week), but they'll have to be used manually rather than applied automatically.Fresheyeball commentedon Jul 13, 2014
Slower? That's actually surprising.
garyb commentedon Jul 13, 2014
Yeah, it definitely shouldn't be, so we need to spend some time on it to work out why. It could have been interfering with the optimizer or something, as that also runs on the JS AST at the moment.
paf31 commentedon Jul 14, 2014
I'd like to avoid automatically uncurrying functions if possible, but rather work on the Data.Function library in the optimizer, and have developers explicitly type their multiple argument functions.
Fresheyeball commentedon Jul 15, 2014
What do you mean by "have developers explicitly type their multiple argument functions." ?
paf31 commentedon Jul 15, 2014
@Fresheyeball I want developers to opt in to multiple argument functions by using
mkFnXandrunFnXfromData.Function.Fresheyeball commentedon Jul 15, 2014
@paf31 that makes sense from a 'writing code in purescript' perspective. But there is a very real performance cost to all the currying (see OP). All I am suggesting is a
psccompiler flag to unroll curried functions as much as possible for performance. The default would be without this optimization of course.puffnfresh commentedon Jul 15, 2014
Curried functions in JavaScript create a new function object for each intermediate function application. This has a cost in current JS virtual machines. I don't know any which can optimise it away, yet.
paf31 commentedon Jul 15, 2014
Well the multi arg functions stuff is now optimized in the Haskell compiler, so feel free to give that a try and see how that helps.
I'm closing this as a duplicate since the functionality is covered by multi arg functions, and I don't particularly want to uncurry anything automatically, as it makes the mapping from PureScript to JavaScript less explicit.
17 remaining items
paf31 commentedon Jul 15, 2014
How does it not affect predictability? "A function becomes a JS function of one argument" (simple) becomes "a function becomes a JS function of one argument except when all of its call sites across all modules are fully applied" (not even modular).
Also, I don't see any rationale for your statement "only valuable as an application wide optimization".
Fresheyeball commentedon Jul 15, 2014
Its only valuable as an appication wide optimization because the vast majority of code is curried. A version that has a limited impact via 'mkFn' does not address the real performance cost.
I agree that an uncurry optimization adds complexity, but more complex does not necissarily mean less predictable. My conception of how to impliment is not intended as perscriptive, there may be a simpler way to have the same benefit. It sounds like there is already a branch for this. Could I get sample output from that branch?
I may be able to help identify why the performance improvement did not materialize.
garyb commentedon Jul 15, 2014
Actually, I just tried it out again.
uncurriedis ~200ms faster than themasterof the same age onps-in-psafter all... But, contrast that with ~750ms out of it by something much simpler - removing{}values for empty__superclassesvalues. There are almost certainly other things we should worry about before we start looking at the cost of function applications, even after theprototypes-with-typeclasses-and-misc-optimizations branch gets merged.Looking a the jsperf results for cost in v8, it's 56% slower than uncurried, but we still 45m ops/sec out of that, it's not exactly slow. The Firefox case is interesting, as curried is 96% slower than uncurried, but then it's also a lot faster than v8 at function application... in fact, curried is almost as fast as uncurried in v8.
My approach of partially applying at call sites would fix the modularity issue, but I'm not sure if it will be as performant as what
uncurriedproduces.garyb commentedon Jul 15, 2014
@Fresheyeball you can check out the uncurried branch here: https://github.com/purescript/purescript/tree/uncurried
Fresheyeball commentedon Jul 15, 2014
Ok interesting. Im going to dive deeper.
paf31 commentedon Jul 15, 2014
Fair warning: I made the
uncurriedbranch as an experiment, I'm not happy with it, and it will take a lot of convincing to get me to merge it tomaster.Fresheyeball commentedon Jul 15, 2014
Warning appreciated. ;) I am just curious about the performance. If it really truly turns out that in a real world use case, the cost of function returns is as negligible as @garyb made it sound, then there is no need for the optimization.
mgmeier commentedon Apr 8, 2016
Something progress has been made on this end: We've implemented an "uncurry" optimization in
psc-bundle, which seems a more suitable place for it than the compiler itself.If you're interested in the topic, here's the read-up: http://symbolian.net/2016/04/07/513
and this is the branch to check out: https://github.com/jutaro/purescript/tree/BundleAndOptimize
Please do confirm if the optimization works for you, or share figures showing that it doesn't.
Our interest is to get this as an option marked experimental into the mainline bundle tool some time soon, if the community agrees it's the right way to go.
paf31 commentedon Apr 10, 2016
I think it makes a lot more sense to implement this inside
psc-bundle. If we can gain confidence that it works as intended (the JS AST is a bit unstructured, after all), then I think we would be interested to merge it intomaster.Fresheyeball commentedon Apr 10, 2016
Oh my god yes!!! Make PureScript perform!!! This is HUGE!
On Sun, Apr 10, 2016 at 11:47 AM Phil Freeman notifications@github.com
wrote:
philipcunningham commentedon Oct 12, 2016
For anyone else following along, the new PR is at #2219.
Pauan commentedon Feb 4, 2017
If anybody is still interested in this,
rollup-plugin-pursnow does both uncurrying and inlining. It can't uncurry every function, but it can uncurry most functions.jamesdbrock commentedon Sep 26, 2021
It seems that @mgmeier 's Symbolian read-up on uncurrying has move to here: Optimizing Purescript´s Runtime Performance
JordanMartinez commentedon Sep 28, 2021
I have to agree that having an option that optimizes all functions to be uncurried as much as possible would be nice. During development, that likely wouldn't be desirable because debugging the outputted JS would be easier. However, if one is going to minify the outputted JS anyway in production, then performance and not readability is generally their concern. Also, if this is an option, then even those outputting code in production may choose not to use it whereas those where it's a fundamental business concern can use it.
At the very least, I think this is something that should be further discussed.
rhendric commentedon Sep 28, 2021
Given that functions using the primitives from
Data.Function.Uncurriedare currently optimized in this way, I have the impression that the best way forward would be to finish #4092 and then anyone can implement this as an external, whole-program CoreFn transformation that inserts the appropriatemkFn*/runFn*calls.(This plan has the downside that, I think, we don't yet know what the pitfalls are in managing an ecosystem of external CoreFn transformations. I think we all expect that it'd be easier than managing a collection of built-in compiler options/extensions, but from a distance the two approaches have enough similarity that I could imagine they might share some of the same problems.)