Hello Louis, Mario Lang, a Spirit developer, played around with Hana and investigated on it's suitability for Spirit. His conclusion is that "Hana is really quite nice!". Then, he adds that: "However, Hana lacks a way to retrieve the members of a struct as references currently, which will pretty much kill a Hana based Spirit effort." So the question is: will Hana support references without resorting to using reference wrappers? I do appreciate the FP nature of Hana, but please be reminded that we're in C++. I also recall some discussion that Hana also does not support Fusion-like lightweight views. Is there a plan to support them? I haven't fully investigated yet what else Hana does not support that Fusion does, but at least these two important aspects of fusion come to mind. At the very least, I don't think Hana should advertize itself as a superset of Boost.Fusion. Faster runtime performance is also questionable, when lightweight views and reference members come into play. Regards, -- Joel de Guzman http://www.ciere.com http://boost-spirit.com http://www.cycfi.com/
On Tue, Jun 23, 2015 at 5:36 PM, Joel de Guzman
So the question is: will Hana support references without resorting to using reference wrappers? I do appreciate the FP nature of Hana, but please be reminded that we're in C++.
I also recall some discussion that Hana also does not support Fusion-like lightweight views. Is there a plan to support them?
I haven't fully investigated yet what else Hana does not support that Fusion does, but at least these two important aspects of fusion come to mind. At the very least, I don't think Hana should advertize itself as a superset of Boost.Fusion. Faster runtime performance is also questionable, when lightweight views and reference members come into play.
Hana has an adaption layer for boost fusion, that has no documentation. This adaption code could use some work - it should be able to use fusion traits for more generic adaption, making all fusion sequences (including views and adapted structs) compatible with Hana. `concat_impl` could return a sequence-view (currently it moves/copies elements into a new sequence), `remove_at_impl` (not currently implemented) could use `fusion::filter_if<>` to avoid element moving/copying, etc. I think this could be useful in a number of scenarios similar to the transform in place discussion from another thread. I'm not sure that Hana should provide this view functionality directly, but it seems like it should list these differences in the documentation. In fact, should Hana leverage fusion::tuple instead of its own tuple implemention (Fusion is undergoing a C++11 upgrade so compilation time difference should be negligible)? Something to consider since it looks like multiple tuple implementations are going to be maintained in boost. Lee
Lee Clagett
On Tue, Jun 23, 2015 at 5:36 PM, Joel de Guzman
wrote: So the question is: will Hana support references without resorting to using reference wrappers? I do appreciate the FP nature of Hana, but please be reminded that we're in C++.
I also recall some discussion that Hana also does not support Fusion-like lightweight views. Is there a plan to support them?
I haven't fully investigated yet what else Hana does not support that Fusion does, but at least these two important aspects of fusion come to mind. At the very least, I don't think Hana should advertize itself as a superset of Boost.Fusion. Faster runtime performance is also questionable, when lightweight views and reference members come into play.
Hana has an adaption layer for boost fusion, that has no documentation. This adaption code could use some work - it should be able to use fusion traits for more generic adaption, making all fusion sequences (including views and adapted structs) compatible with Hana. `concat_impl` could return a sequence-view (currently it moves/copies elements into a new sequence), `remove_at_impl` (not currently implemented) could use `fusion::filter_if<>` to avoid element moving/copying, etc. I think this could be useful in a number of scenarios similar to the transform in place discussion from another thread.
Indeed; the adaptation layer for Fusion could be improved and it might be possible to support views in this way. However, I'd have to make sure that views can be made to fit the concepts provided by Hana (which might be the case if you consider views to be equivalent to normal sequences, except everything is computed lazily). For example, in practice it is totally possible to have `concat` return a view, but I'd like to make sure it makes sense mathematically.
I'm not sure that Hana should provide this view functionality directly, but it seems like it should list these differences in the documentation. In fact, should Hana leverage fusion::tuple instead of its own tuple implemention (Fusion is undergoing a C++11 upgrade so compilation time difference should be negligible)?
Some algorithms have to be tightly coupled to the tuple's implementation to ensure good compile-time performance, so I don't think this would be feasible. I'd also like to stay in control of tuple, since it is literally THE most core structure of the library, rather than rely on another library for that.
Something to consider since it looks like multiple tuple implementations are going to be maintained in boost.
Lee
Regards, Louis
Joel de Guzman
Hello Louis,
Mario Lang, a Spirit developer, played around with Hana and investigated on it's suitability for Spirit. His conclusion is that "Hana is really quite nice!". Then, he adds that: "However, Hana lacks a way to retrieve the members of a struct as references currently, which will pretty much kill a Hana based Spirit effort."
Indeed, this will change once this issue [1] is resolved.
So the question is: will Hana support references without resorting to using reference wrappers? I do appreciate the FP nature of Hana, but please be reminded that we're in C++.
I also recall some discussion that Hana also does not support Fusion-like lightweight views. Is there a plan to support them?
If we find use cases where views would really be helpful, then that's a feature that could be added. I'm open to the possibility, but right now there are (1) more urgent things to improve (2) no clear use case that require views. However, I haven't given a lot of thought to it, so I might be missing some obvious use cases. Also note that views like in Fusion would actually take Hana further away from C++, where we are used to eager evaluation. Adding laziness to Hana would give it an even more FP taste. This is just an observation.
I haven't fully investigated yet what else Hana does not support that Fusion does, but at least these two important aspects of fusion come to mind. At the very least, I don't think Hana should advertize itself as a superset of Boost.Fusion.
The first aspect is a bug, and the second aspect is more like a different design choice, since Fusion supports only "lightweight views", and no eager algorithms. What is meant is that all of Fusions's algorithms are implemented in Hana (and more), which is true. I don't think saying Hana is a superset of Fusion is detrimental to Fusion, but I could reword that sentence of the tutorial if you feel like it is detrimental.
Faster runtime performance is also questionable, when lightweight views and reference members come into play.
Hana claims to be at least as good as Fusion runtime-wise, which is backed by the benchmarks presented in the tutorial. It is possible that idiomatic usage of Hana leads to worse runtime performance than with Fusion, in which case the library and its idioms will have to be adjusted. Like I said a few times, Hana is a new library with a new paradigm, and I think we're only starting to discover how it can be used (and used efficiently). Regards, Louis [1]: https://github.com/ldionne/hana/issues/90
On 6/26/15 5:24 AM, Louis Dionne wrote:
Joel de Guzman
writes: Hello Louis,
Mario Lang, a Spirit developer, played around with Hana and investigated on it's suitability for Spirit. His conclusion is that "Hana is really quite nice!". Then, he adds that: "However, Hana lacks a way to retrieve the members of a struct as references currently, which will pretty much kill a Hana based Spirit effort."
Indeed, this will change once this issue [1] is resolved.
So the question is: will Hana support references without resorting to using reference wrappers? I do appreciate the FP nature of Hana, but please be reminded that we're in C++.
I also recall some discussion that Hana also does not support Fusion-like lightweight views. Is there a plan to support them?
If we find use cases where views would really be helpful, then that's a feature that could be added. I'm open to the possibility, but right now there are (1) more urgent things to improve (2) no clear use case that require views. However, I haven't given a lot of thought to it, so I might be missing some obvious use cases.
Also note that views like in Fusion would actually take Hana further away from C++, where we are used to eager evaluation. Adding laziness to Hana would give it an even more FP taste. This is just an observation.
So Fusion is more FP than Hana in that regard. Views are not new in C++. There are examples of it. Eric's Ranges come to mind.
I haven't fully investigated yet what else Hana does not support that Fusion does, but at least these two important aspects of fusion come to mind. At the very least, I don't think Hana should advertize itself as a superset of Boost.Fusion.
The first aspect is a bug, and the second aspect is more like a different design choice, since Fusion supports only "lightweight views", and no eager algorithms. What is meant is that all of Fusions's algorithms are implemented in Hana (and more), which is true. I don't think saying Hana is a superset of Fusion is detrimental to Fusion, but I could reword that sentence of the tutorial if you feel like it is detrimental.
Faster runtime performance is also questionable, when lightweight views and reference members come into play.
Hana claims to be at least as good as Fusion runtime-wise, which is backed by the benchmarks presented in the tutorial. It is possible that idiomatic usage of Hana leads to worse runtime performance than with Fusion, in which case the library and its idioms will have to be adjusted. Like I said a few times, Hana is a new library with a new paradigm, and I think we're only starting to discover how it can be used (and used efficiently).
Your tests focused on a mere subset of Fusion which is the least optimized: fusion::vector. Your tests do not cover other areas such as views, and other containers. I can imagine a case, when say you push_back or insert where views should shine. It's the difference between copy by value and pass by reference. Try doing that with large tuple elements (those without resources and are not efficiently moved). Actually, have you tried reverse with large tuples like bitmaps and stuff? Hmmmm, how did you test reverse in Fusion BTW? Fusion reverse should return a view with ZERO(!) copy. Did you create another vector from the reverse_view? I guess so, since your test subject is Fusion vector. In that case, you are not doing it right! You don't copy to another vector. You should use the results as-is. Lazily. For that matter, the same is true with transform! Fusion returns transform_view. You should get zero overhead with those because they are lazy. If you are copying back to a fusion vector, then you are not doing it right. Have you tried accessing, say only the Nth element instead of (presumably) copying back to a vector before accessing the Nth element? or how about transforming only a range within the container? It seems you are doing your tests unfairly using eager evaluation (Hana) instead of lazy evaluation (Fusion). Regards, -- Joel de Guzman http://www.ciere.com http://boost-spirit.com http://www.cycfi.com/
On Thu, Jun 25, 2015 at 5:41 PM, Joel de Guzman
On 6/26/15 5:24 AM, Louis Dionne wrote:
Faster runtime performance is also questionable, when lightweight views and reference members come into play.
Hana claims to be at least as good as Fusion runtime-wise, which is backed by the benchmarks presented in the tutorial. It is possible that idiomatic usage of Hana leads to worse runtime performance than with Fusion, in which case the library and its idioms will have to be adjusted. Like I said a few times, Hana is a new library with a new paradigm, and I think we're only starting to discover how it can be used (and used efficiently).
Your tests focused on a mere subset of Fusion which is the least optimized: fusion::vector. Your tests do not cover other areas such as views, and other containers. I can imagine a case, when say you push_back or insert where views should shine. It's the difference between copy by value and pass by reference. Try doing that with large tuple elements (those without resources and are not efficiently moved). Actually, have you tried reverse with large tuples like bitmaps and stuff?
</snip>
Hi Joel and Louis, How about a similar analysis but for the Hana vs. Fusion compile-times? In my experience compile-times are much (much!) more of a problem than run-times with TMP in production code (because I can always do something "smart" to optimize run-times, while such optimizations are much more difficult, and much more compile-dependent for compile-times). I'd be nice to confirm/check Hana's compile-time improvements over Fusion shown by the Tutorial graphs, and also to be able to guess if we'll see similar numbers on MSVC, not just Clang (Louis already commented that should be the case...). Thanks. --Lorenzo P.S. As I said in my review, I think Hana has its place in Boost regardless of the promised performance improvements (because of its easier/new TMP, great SFINAE facilities, etc.). But the the improved compile-times will essentially "force" me to use Hana if it becomes available on MSVC, so that's a lot of extra motivation.
On 6/26/15 10:57 PM, Lorenzo Caminiti wrote:
On Thu, Jun 25, 2015 at 5:41 PM, Joel de Guzman
wrote: On 6/26/15 5:24 AM, Louis Dionne wrote:
Faster runtime performance is also questionable, when lightweight views and reference members come into play.
Hana claims to be at least as good as Fusion runtime-wise, which is backed by the benchmarks presented in the tutorial. It is possible that idiomatic usage of Hana leads to worse runtime performance than with Fusion, in which case the library and its idioms will have to be adjusted. Like I said a few times, Hana is a new library with a new paradigm, and I think we're only starting to discover how it can be used (and used efficiently).
Your tests focused on a mere subset of Fusion which is the least optimized: fusion::vector. Your tests do not cover other areas such as views, and other containers. I can imagine a case, when say you push_back or insert where views should shine. It's the difference between copy by value and pass by reference. Try doing that with large tuple elements (those without resources and are not efficiently moved). Actually, have you tried reverse with large tuples like bitmaps and stuff?
</snip>
Hi Joel and Louis,
How about a similar analysis but for the Hana vs. Fusion compile-times? In my experience compile-times are much (much!) more of a problem than run-times with TMP in production code (because I can always do something "smart" to optimize run-times, while such optimizations are much more difficult, and much more compile-dependent for compile-times).
I'd be nice to confirm/check Hana's compile-time improvements over Fusion shown by the Tutorial graphs, and also to be able to guess if we'll see similar numbers on MSVC, not just Clang (Louis already commented that should be the case...).
Thanks. --Lorenzo
P.S. As I said in my review, I think Hana has its place in Boost regardless of the promised performance improvements (because of its easier/new TMP, great SFINAE facilities, etc.). But the the improved compile-times will essentially "force" me to use Hana if it becomes available on MSVC, so that's a lot of extra motivation.
Hi Lorenzo, Two things. 1) Hana's tests focuses on the least optimized aspect of Fusion: vector. I won't be surprised tough if Hana is still faster with the other containers at the moment. because 2) It is undergoing a C++11, C++14 transformation right now, which should level the playing field somewhat, except probably for the abstraction penalties of the underlying Iterator interface. There's some talk about getting rid of the fancy Iterator concepts of Fusion in favor of a simpler (CT friendly) interface. maybe for Fusion V3. It might also be possible to have a Fusion-lite, with 95+% of the functionality but with a very lightweight interface. For runtime, there are things that a library's design limitation imposes that you can't do anything about. So you can't simply say: "I can always do something "smart" to optimize run-times". The example I presented are views. If you use views (properly!) you can have zero copies/moves. If you have seen Eric's presentation on the ranges V3 library, that is a compelling example using lazy views. For example, imagine a complex algorithm chain f1 | f2 | f3 | f4. If all these algos compute eagerly, you will have four(!) temporary results. With views, you have zero. Most of Fusion algorithms return views. Hana's algorithms on the other hand are always eagerly evaluated. Regards, -- Joel de Guzman http://www.ciere.com http://boost-spirit.com http://www.cycfi.com/
Joel de Guzman
writes: [...] Your tests focused on a mere subset of Fusion which is the least optimized: fusion::vector. Your tests do not cover other areas such as views, and other containers.
Frankly, I thought fusion::vector would be the most efficient container. Isn't vector documented as such? Quoting from the documentation: vector is the simplest of the Fusion sequence container (a vector with N elements is just a struct with N members), and in many cases the most efficient.
I can imagine a case, when say you push_back or insert where views should shine. It's the difference between copy by value and pass by reference. Try doing that with large tuple elements (those without resources and are not efficiently moved). Actually, have you tried reverse with large tuples like bitmaps and stuff?
Hmmmm, how did you test reverse in Fusion BTW? Fusion reverse should return a view with ZERO(!) copy. Did you create another vector from the reverse_view? I guess so, since your test subject is Fusion vector. In that case, you are not doing it right! You don't copy to another vector. You should use the results as-is. Lazily.
For that matter, the same is true with transform! Fusion returns transform_view. You should get zero overhead with those because they are lazy. If you are copying back to a fusion vector, then you are not doing it right. Have you tried accessing, say only the Nth element instead of (presumably) copying back to a vector before accessing the Nth element? or how about transforming only a range within the container?
It seems you are doing your tests unfairly using eager evaluation (Hana) instead of lazy evaluation (Fusion).
There seems to be a larger issue here. That issue is: How to benchmark lazy computations and eager computations? To illustrate the problem, consider the following "benchmark": transforming a vector in C++: #include <algorithm> #include <vector> int main() { std::vector<int> xs(1000); std::vector<int> ys; ys.reserve(xs.size()); std::transform(xs.begin(), xs.end(), std::back_inserter(ys), [](int x) { return x + 1; }); } transforming a list in Haskell: import Data.List main = do let xs = take 1000 (repeat 0) let ys = fmap (+1) xs return () Written this way, the Haskell code will always be faster because it does not do anything, since it is lazy. However, that does not tell us much about what we're interested in. What we'd like to know is how the above code behaves when I _actually_ access the elements of the lazy list. So, of course I copy the result into vectors when benchmarking Fusion algorithms, since otherwise there's nothing to benchmark! I think this ought to be explained in the documentation, but I don't think it invalidates Hana's claims. For equivalent functionality (i.e. when accessing all the members of the resulting container, which is assumed to usually be the case), Hana will perform as good as Fusion. Regards, Louis -- View this message in context: http://boost.2283326.n4.nabble.com/Hana-Views-and-References-tp4677490p46775... Sent from the Boost - Dev mailing list archive at Nabble.com.
Le 27/06/15 16:48, Louis Dionne a écrit :
Joel de Guzman
writes: [...] Your tests focused on a mere subset of Fusion which is the least optimized: fusion::vector. Your tests do not cover other areas such as views, and other containers. Frankly, I thought fusion::vector would be the most efficient container. Isn't vector documented as such? Quoting from the documentation:
vector is the simplest of the Fusion sequence container (a vector with N elements is just a struct with N members), and in many cases the most efficient.
I can imagine a case, when say you push_back or insert where views should shine. It's the difference between copy by value and pass by reference. Try doing that with large tuple elements (those without resources and are not efficiently moved). Actually, have you tried reverse with large tuples like bitmaps and stuff?
Hmmmm, how did you test reverse in Fusion BTW? Fusion reverse should return a view with ZERO(!) copy. Did you create another vector from the reverse_view? I guess so, since your test subject is Fusion vector. In that case, you are not doing it right! You don't copy to another vector. You should use the results as-is. Lazily.
For that matter, the same is true with transform! Fusion returns transform_view. You should get zero overhead with those because they are lazy. If you are copying back to a fusion vector, then you are not doing it right. Have you tried accessing, say only the Nth element instead of (presumably) copying back to a vector before accessing the Nth element? or how about transforming only a range within the container?
It seems you are doing your tests unfairly using eager evaluation (Hana) instead of lazy evaluation (Fusion). There seems to be a larger issue here. That issue is: How to benchmark lazy computations and eager computations? To illustrate the problem, consider the following "benchmark":
transforming a vector in C++:
#include <algorithm> #include <vector>
int main() { std::vector<int> xs(1000); std::vector<int> ys; ys.reserve(xs.size()); std::transform(xs.begin(), xs.end(), std::back_inserter(ys), [](int x) { return x + 1; }); }
transforming a list in Haskell:
import Data.List
main = do let xs = take 1000 (repeat 0) let ys = fmap (+1) xs return ()
Written this way, the Haskell code will always be faster because it does not do anything, since it is lazy. However, that does not tell us much about what we're interested in. What we'd like to know is how the above code behaves when I _actually_ access the elements of the lazy list. So, of course I copy the result into vectors when benchmarking Fusion algorithms, since otherwise there's nothing to benchmark!
I think this ought to be explained in the documentation, but I don't think it invalidates Hana's claims. For equivalent functionality (i.e. when accessing all the members of the resulting container, which is assumed to usually be the case), Hana will perform as good as Fusion.
Instead of using a fusion::vector as a return of transform or reverse, you could just iterate on the result to e.g. calculate something simple. Vicente
On 6/27/15 10:48 PM, Louis Dionne wrote:
Joel de Guzman
writes: [...] Your tests focused on a mere subset of Fusion which is the least optimized: fusion::vector. Your tests do not cover other areas such as views, and other containers.
Frankly, I thought fusion::vector would be the most efficient container. Isn't vector documented as such? Quoting from the documentation:
vector is the simplest of the Fusion sequence container (a vector with N elements is just a struct with N members), and in many cases the most efficient.
That was true a decade ago in a purely c++03 world. The C++ world has changed since then and fusion has evolved since then. I'm sorry for the confusion. Anyway, Fusion is undergoing transformation again following advances in C++. The documentation will have to be updated. Anyway, it's not just containers I am referring to. And you know now that I am referring to views, and on runtime performance.
I can imagine a case, when say you push_back or insert where views should shine. It's the difference between copy by value and pass by reference. Try doing that with large tuple elements (those without resources and are not efficiently moved). Actually, have you tried reverse with large tuples like bitmaps and stuff?
Hmmmm, how did you test reverse in Fusion BTW? Fusion reverse should return a view with ZERO(!) copy. Did you create another vector from the reverse_view? I guess so, since your test subject is Fusion vector. In that case, you are not doing it right! You don't copy to another vector. You should use the results as-is. Lazily.
For that matter, the same is true with transform! Fusion returns transform_view. You should get zero overhead with those because they are lazy. If you are copying back to a fusion vector, then you are not doing it right. Have you tried accessing, say only the Nth element instead of (presumably) copying back to a vector before accessing the Nth element? or how about transforming only a range within the container?
It seems you are doing your tests unfairly using eager evaluation (Hana) instead of lazy evaluation (Fusion).
There seems to be a larger issue here. That issue is: How to benchmark lazy computations and eager computations? To illustrate the problem, consider the following "benchmark":
transforming a vector in C++:
#include <algorithm> #include <vector>
int main() { std::vector<int> xs(1000); std::vector<int> ys; ys.reserve(xs.size()); std::transform(xs.begin(), xs.end(), std::back_inserter(ys), [](int x) { return x + 1; }); }
transforming a list in Haskell:
import Data.List
main = do let xs = take 1000 (repeat 0) let ys = fmap (+1) xs return ()
Written this way, the Haskell code will always be faster because it does not do anything, since it is lazy. However, that does not tell us much about what we're interested in. What we'd like to know is how the above code behaves when I _actually_ access the elements of the lazy list. So, of course I copy the result into vectors when benchmarking Fusion algorithms, since otherwise there's nothing to benchmark!
And so you are doing it wrong! What you are getting is the extra construction. The result *IS* already a sequence that can be traversed. That extra copy is not necessary. If you've traversed over the elements of the sequence instead, you should see a gain with Fusion because Hana will have to create a new tuple while Fusion will not.
I think this ought to be explained in the documentation, but I don't think it invalidates Hana's claims. For equivalent functionality (i.e. when accessing all the members of the resulting container, which is assumed to usually be the case), Hana will perform as good as Fusion.
Try getting a single element, or a range, for example and, how about trying multiple concatenated algorithms, or how about push_back and insert multiple times? I can think of a LOT of examples where views will blow eager evaluation out the water. I'm sure you've seen Eric's range V3 presentation? Imagine doing all that computation eagerly and you'll know what I mean! Bottom line, what you did in your tests is force an apple to be an orange and test it against an orange. That said, I'm still surprised that Fusion withstood the tests and performed admirably despite the added construction step. The test on reverse deserves investigation and optimization though, so I'll take the liberty of using your words, changing "Hana" to "Fusion": Performance is important to us: if you ever encounter a scenario where Fusion causes bad code to be generated (and the fault is not on the compiler), please open an issue so the problem can be addressed. Regards, -- Joel de Guzman http://www.ciere.com http://boost-spirit.com http://www.cycfi.com/
participants (5)
-
Joel de Guzman
-
Lee Clagett
-
Lorenzo Caminiti
-
Louis Dionne
-
Vicente J. Botet Escriba