C++ committee meeting report
The C++ committee met last week in Rapperswil, Switzerland. Actions that might be of interest to Boosters include: * Shipped the File System TS for ISO DTS (i.e. final) ballot. Since it would be very unusual for at DTS ballot to fail, the File System TS is essentially done. The committee is now actively looking for File System TS 2 proposals. My work now shifts to bringing the Boost implementation into compliance. * Shipped the Library Fundamentals TS for ISO PDTS (i.e. preliminary) ballot. This TS includes Boost.Any and Boost.Optional. * Voted to start work Library Fundamentals TS 2. The plan is to issue a continuing stream of Library Fundamentals TSes, with work starting on the next TS as soon as content for the current TS freezes. Hopefully Boost developers will submit proposals for this TS too. It will be a good home for small libraries that don't need a whole TS of their own. * Shipped the C++ Extensions for Parallelism TS for ISO PDTS (i.e. preliminary) ballot. * The Library Evolution Working Group (LEWG) voted to base the Networking TS on Boost.ASIO. That was a major surprise, as a Networking Study Group had been trying to build up a Networking TS from many small proposals. But in LEWG polls, support for ASIO was overwhelming, with no one at all voting against. This was a major vote of confidence for Boost.ASIO and Chris Kohlhoff. And several ASIO supporters were not even in the room at the time because Filesystem issues were being worked on elsewhere. The next C++ committee meeting is November 3rd to 8th in Urbana-Champaign, IL, USA. As always, Boosters welcome. --Beman
On 06/24/2014 07:47 AM, Beman Dawes wrote:
* The Library Evolution Working Group (LEWG) voted to base the Networking TS on Boost.ASIO.
Hallelujah. Maybe we can see meaningful networking support in the standard library in this decade after all. Thanks for the update, Beman! \e
Congrats Chris! That's a huge achievement! Also, thanks for the awesome lib! I use it all the time (indirectly through pion or unicomm, but still). The C++ committee met last week in Rapperswil, Switzerland. Actions that might be of interest to Boosters include: * Shipped the File System TS for ISO DTS (i.e. final) ballot. Since it would be very unusual for at DTS ballot to fail, the File System TS is essentially done. The committee is now actively looking for File System TS 2 proposals. My work now shifts to bringing the Boost implementation into compliance. * Shipped the Library Fundamentals TS for ISO PDTS (i.e. preliminary) ballot. This TS includes Boost.Any and Boost.Optional. * Voted to start work Library Fundamentals TS 2. The plan is to issue a continuing stream of Library Fundamentals TSes, with work starting on the next TS as soon as content for the current TS freezes. Hopefully Boost developers will submit proposals for this TS too. It will be a good home for small libraries that don't need a whole TS of their own. * Shipped the C++ Extensions for Parallelism TS for ISO PDTS (i.e. preliminary) ballot. * The Library Evolution Working Group (LEWG) voted to base the Networking TS on Boost.ASIO. That was a major surprise, as a Networking Study Group had been trying to build up a Networking TS from many small proposals. But in LEWG polls, support for ASIO was overwhelming, with no one at all voting against. This was a major vote of confidence for Boost.ASIO and Chris Kohlhoff. And several ASIO supporters were not even in the room at the time because Filesystem issues were being worked on elsewhere. The next C++ committee meeting is November 3rd to 8th in Urbana-Champaign, IL, USA. As always, Boosters welcome. --Beman _______________________________________________ Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost
On 24 Jun 2014 at 10:47, Beman Dawes wrote:
* Shipped the File System TS for ISO DTS (i.e. final) ballot. Since it would be very unusual for at DTS ballot to fail, the File System TS is essentially done. The committee is now actively looking for File System TS 2 proposals. My work now shifts to bringing the Boost implementation into compliance.
Congrats on this Beman. To get a whole library conceptualised from the beginning into the standard is an enormous commitment and achievement, and one which couldn't have happened with you.
* The Library Evolution Working Group (LEWG) voted to base the Networking TS on Boost.ASIO. That was a major surprise, as a Networking Study Group had been trying to build up a Networking TS from many small proposals. But in LEWG polls, support for ASIO was overwhelming, with no one at all voting against. This was a major vote of confidence for Boost.ASIO and Chris Kohlhoff. And several ASIO supporters were not even in the room at the time because Filesystem issues were being worked on elsewhere.
This is an enormous dose of common sense, and I applaud it. Anyone who has used ASIO to any extent will find complaint with its implementation, documentation and occasionally design, but it is unquestionably and categorically the standard go-to library for C++ for anything involving async. It may be warty, but it has become the common practice. Now a subset of it will become the standard networking library, I hope that all other async proposals before the committee can be amended to assume its existence, and that those async proposals - especially those from proprietary vendors based on their proprietary platform technologies - which can't or won't fit ASIO should be discarded from consideration immediately. Vicente, Pierre and others have been working on a generic monadic continuations framework which will enable seamless interoperation of all things async, such that one could mix together futures, ASIO callbacks, Fiber coroutines and anything else async including proprietary vendor technologies if they choose. I'll be helping them out with hopefully a malloc-free promise/future implementation which uses that framework if I can get such a thing working - this would reduce promise/future to merely a specialised subset use of the monadic framework. If I get that working, I'll next integrate it into ASIO and AFIO so at least those three libraries all have interoperable continuations with general threading. Obviously, I haven't got it written yet, so I make no promises. I'm currently investigating how best to use memory transactions in the implementation and whether supporting transactional GCC is worth it. Niall -- ned Productions Limited Consulting http://www.nedproductions.biz/ http://ie.linkedin.com/in/nialldouglas/
On 27 Jun 2014 at 13:08, Niall Douglas wrote:
* Shipped the File System TS for ISO DTS (i.e. final) ballot. Since it would be very unusual for at DTS ballot to fail, the File System TS is essentially done. The committee is now actively looking for File System TS 2 proposals. My work now shifts to bringing the Boost implementation into compliance.
Congrats on this Beman. To get a whole library conceptualised from the beginning into the standard is an enormous commitment and achievement, and one which couldn't have happened with you.
"happened with you" => "happened without you" Apologies for writing English without drinking coffee first and without my glasses on. And thanks to the kind soul who spotted my typo and told me. Niall -- ned Productions Limited Consulting http://www.nedproductions.biz/ http://ie.linkedin.com/in/nialldouglas/
On Fri, Jun 27, 2014 at 8:39 AM, Niall Douglas <s_sourceforge@nedprod.com> wrote:
On 27 Jun 2014 at 13:08, Niall Douglas wrote:
* Shipped the File System TS for ISO DTS (i.e. final) ballot. Since it would be very unusual for at DTS ballot to fail, the File System TS is essentially done. The committee is now actively looking for File System TS 2 proposals. My work now shifts to bringing the Boost implementation into compliance.
Congrats on this Beman. To get a whole library conceptualised from the beginning into the standard is an enormous commitment and achievement, and one which couldn't have happened with you.
"happened with you" => "happened without you"
Apologies for writing English without drinking coffee first and without my glasses on. And thanks to the kind soul who spotted my typo and told me.
Boost is full of kind souls who help protect us from ourselves. Boost.Filesystem benefited tremendously from all the Boosters who contributed their time and knowledge by posting over a decade of suggestions about how to make the library better. Thanks, --Beman
Am 27.06.14 14:08, schrieb Niall Douglas:
[...] Now a subset of it will become the standard networking library, I hope that all other async proposals before the committee can be amended to assume its existence, and that those async proposals - especially those from proprietary vendors based on their proprietary platform technologies - which can't or won't fit ASIO should be discarded from consideration immediately.
What I believe is missing for C++ to get to the next level is a standard serialization proposal. Networking in the standard is great, but for many distributed computing tasks you will need serialization as well. Any chance of bringing Boost.Serialization in for C++17 ? It is established and in production use since years, and Robert did a great job with this library. Best Regards, Beet
On Sun, Jun 29, 2014 at 2:53 PM, beet <r.berlich@gemfony.eu> wrote:
Any chance of bringing Boost.Serialization in for C++17 ? It is established and in production use since years, and Robert did a great job with this library.
There is work going on for adding (compile-time) reflection to C++. I'm not sure if it makes a serialization library obsolete (there is no voted proposal so far if I understand correctly) but my understanding is that the committee first want reflection to base solutions on for fixing networking issues.
On 29 Jun 2014 at 16:40, Klaim - Joël Lamotte wrote:
Any chance of bringing Boost.Serialization in for C++17 ? It is established and in production use since years, and Robert did a great job with this library.
There is work going on for adding (compile-time) reflection to C++. I'm not sure if it makes a serialization library obsolete (there is no voted proposal so far if I understand correctly) but my understanding is that the committee first want reflection to base solutions on for fixing networking issues.
Exactly what I was about to respond! I absolutely agree, until at the very least compile time reflection is done (and probably we have some idea of how runtime reflection will be done) I think the chances of standardising a new serialisation library based on those features will be low. Besides, there are some who have substantial criticisms of the Boost.Serialisation design. I have no experience with the library personally, but I have seen some mention it isn't great at working with partial streams and/or never ending streams and/or truly huge (multi-Gb) files. I am very open to being corrected if I heard wrong. Niall -- ned Productions Limited Consulting http://www.nedproductions.biz/ http://ie.linkedin.com/in/nialldouglas/
On Sun, Jun 29, 2014 at 10:40 AM, Klaim - Joël Lamotte <mjklaim@gmail.com> wrote:
On Sun, Jun 29, 2014 at 2:53 PM, beet <r.berlich@gemfony.eu> wrote:
Any chance of bringing Boost.Serialization in for C++17 ? It is established and in production use since years, and Robert did a great job with this library.
There is work going on for adding (compile-time) reflection to C++. I'm not sure if it makes a serialization library obsolete (there is no voted proposal so far if I understand correctly) but my understanding is that the committee first want reflection to base solutions on for fixing networking issues.
AFAIK, the committee has never held even preliminary straw polls on any relationship between networking and reflection. The Reflection Study Group is still at a very early stage, and has no complete proposal before it, let alone started to build a Reflection TS working paper. The LEWG has held a couple of straw polls on starting a Networking working paper, and if those are any guide the LEWG is not in a mode to wait for anything. There is only one way to find out what the LEWG would think about a Serialization library proposal. Write the proposal and submit it. If a Serialization library proposal would be much stronger with compiler support, then the proposal can include a requirement for compiler support. But until the committee and its sub-groups has a written proposal before it, nothing happens. Think of Type Traits. If the Library had waited until there was full language support for type traits as a language feature, we would still be waiting. When the standard library added type traits, with a requirement for compiler support, it was clearly understood that future events could make the type traits library obsolete. That didn't stop the addition of John Maddock's type-traits library. Same thing could happen with serialization. --Beman
On 06/27/2014 02:08 PM, Niall Douglas wrote:
Now a subset of it will become the standard networking library, I hope that all other async proposals before the committee can be amended to assume its existence, and that those async proposals - especially those from proprietary vendors based on their proprietary platform technologies - which can't or won't fit ASIO should be discarded from consideration immediately.
This appears to be happening. The Rapperswil minutes mention that the "Executors and Schedulers" section has been removed from N3970. No rationale is cited though, but I suspect that it was due to concerns raised in N4032 and N4046.
On 2 Jul 2014 at 17:25, Bjorn Reese wrote:
Now a subset of it will become the standard networking library, I hope that all other async proposals before the committee can be amended to assume its existence, and that those async proposals - especially those from proprietary vendors based on their proprietary platform technologies - which can't or won't fit ASIO should be discarded from consideration immediately.
This appears to be happening. The Rapperswil minutes mention that the "Executors and Schedulers" section has been removed from N3970. No rationale is cited though, but I suspect that it was due to concerns raised in N4032 and N4046.
Heh. I had missed N4032 actually, so thanks for that. I think Anthony really nailed it in N4032, but equally I don't see any of it as insurmountable. Besides, a variant of Executors (not Schedulers) is being implemented into Boost.Thread, and I myself am happy to contribute an Executors adapter for ASIO. I suspect something like Executors based on std::packaged_task<> will turn up. I personally would doubt that the template based approach of N4046 is viable for standardisation, if you need that fine grained I think the newly approved resumable functions are a better fit. I also think that strands are also better done as resumables. Niall -- ned Productions Limited Consulting http://www.nedproductions.biz/ http://ie.linkedin.com/in/nialldouglas/
Hallelujah! Way to go Beman! While having a library in the standard isn't required to use it, it's so nice to have things like the File system standardized as well as building tools like Any and Optional and now ASIO. -Gary- On Tue, Jun 24, 2014 at 7:47 AM, Beman Dawes <bdawes@acm.org> wrote:
The C++ committee met last week in Rapperswil, Switzerland. Actions that might be of interest to Boosters include:
* Shipped the File System TS for ISO DTS (i.e. final) ballot. Since it would be very unusual for at DTS ballot to fail, the File System TS is essentially done. The committee is now actively looking for File System TS 2 proposals. My work now shifts to bringing the Boost implementation into compliance.
* Shipped the Library Fundamentals TS for ISO PDTS (i.e. preliminary) ballot. This TS includes Boost.Any and Boost.Optional.
* Voted to start work Library Fundamentals TS 2. The plan is to issue a continuing stream of Library Fundamentals TSes, with work starting on the next TS as soon as content for the current TS freezes. Hopefully Boost developers will submit proposals for this TS too. It will be a good home for small libraries that don't need a whole TS of their own.
* Shipped the C++ Extensions for Parallelism TS for ISO PDTS (i.e. preliminary) ballot.
* The Library Evolution Working Group (LEWG) voted to base the Networking TS on Boost.ASIO. That was a major surprise, as a Networking Study Group had been trying to build up a Networking TS from many small proposals. But in LEWG polls, support for ASIO was overwhelming, with no one at all voting against. This was a major vote of confidence for Boost.ASIO and Chris Kohlhoff. And several ASIO supporters were not even in the room at the time because Filesystem issues were being worked on elsewhere.
The next C++ committee meeting is November 3rd to 8th in Urbana-Champaign, IL, USA. As always, Boosters welcome.
--Beman
_______________________________________________ Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost
-- ------------------ gwpowell@gmail.com
On 30 Jun 2014 at 10:19, Gary Powell wrote:
While having a library in the standard isn't required to use it, it's so nice to have things like the File system standardized as well as building tools like Any and Optional and now ASIO.
I think the wicked hard problem with ASIO will be deciding what subset of it to standardise. For example, I would wonder if strands or coroutines or any of the Windows support would make it. There is also a strong argument that anything in ASIO which isn't async needs to go. Plus, some might feel that ASIO's current design uses too much malloc for embedded systems/ultra low latency, and I would sympathise :) Finally, as a personal viewpoint I don't care much for ASIO's internal implementation. I find it obtuse and hard to debug or indeed figure out much at all as bits of implementation are scattered all over the place. Some if not much of that is because ASIO implements a sort of generic concept type framework all of which requires checking, well the obvious course of action here is to use proper C++ 17 concepts and do away with the legacy design cruft. I'd also personally split the genericity away from the implementation, and push the implementation into a non-header built stable ABI so we can avoid pulling in so many system header files like all of windows.h. Lastly, as I mentioned before we really need ASIO as a task executor to do continuations easily into threading, futures etc and vice versa so one can easily chain network with disc with thread messaging i/o and so on. I can already see others having vehement disagreements in the above :) Niall -- ned Productions Limited Consulting http://www.nedproductions.biz/ http://ie.linkedin.com/in/nialldouglas/
On 1/07/2014 08:09, Niall Douglas wrote:
There is also a strong argument that anything in ASIO which isn't async needs to go. Plus, some might feel that ASIO's current design uses too much malloc for embedded systems/ultra low latency, and I would sympathise :)
Most of the mallocs can be avoided by using the custom allocator framework. It's the locks that I object to from an embedded-low-latency standpoint, and why I ended up rolling my own. :) That's only really an issue though if you're trying to use it for generic-embedded-async or at least non-network-async purposes. Once you're using it for its original purpose of network I/O, locks make more sense.
Finally, as a personal viewpoint I don't care much for ASIO's internal implementation. I find it obtuse and hard to debug or indeed figure out much at all as bits of implementation are scattered all over the place. Some if not much of that is because ASIO implements a sort of generic concept type framework all of which requires checking, well the obvious course of action here is to use proper C++ 17 concepts and do away with the legacy design cruft. I'd also personally split the genericity away from the implementation, and push the implementation into a non-header built stable ABI so we can avoid pulling in so many system header files like all of windows.h.
Unfortunately one of its most powerful features (concept based callable handlers and the ability to chain special conditions such as custom allocators and strands) also means that templates permeate almost everywhere, making it really hard to do anything other than header-only. (I haven't read up on C++17 concepts and whether they would help with this or not.) When I was rolling my own one I did elect to use private implementation instead but it came at a cost of flexibility and performance (particularly since I'm using boost::function to break the template chain instead of something more lightweight); in particular mine doesn't support custom allocators and while it does have strands, they're a bit more brittle and you need to be more careful how you use them or you can get unexpected concurrency. (It's still a net win in my case due to lower latency and lock avoidance, but this is not a tradeoff everyone would want to make.) I might be wrong about this, but I get the impression that a lot of the internal implementation code is duplicated rather than being factored to common methods in order to avoid conditionals and improve performance without relying on compiler inlining. Which can also contribute to making it hard to read and understand.
On 1 Jul 2014 at 13:41, Gavin Lambert wrote:
There is also a strong argument that anything in ASIO which isn't async needs to go. Plus, some might feel that ASIO's current design uses too much malloc for embedded systems/ultra low latency, and I would sympathise :)
Most of the mallocs can be avoided by using the custom allocator framework. It's the locks that I object to from an embedded-low-latency standpoint, and why I ended up rolling my own. :)
It's hassle though. And could have been avoided altogether, though then you would no longer have the present design so it's probably too late now. BTW, I believe the locks ought to be elidable if you have STM or HTM available, so ASIO could have an entirely wait free implementation in its fast path. AFIO achieved this, I'm sure the same pattern could be used for ASIO.
That's only really an issue though if you're trying to use it for generic-embedded-async or at least non-network-async purposes. Once you're using it for its original purpose of network I/O, locks make more sense.
iTrue. It still annoys me though that you can't concurrently use the same socket for reads and writes, so either you strand or you go all async throughout. I'd prefer all async as the thing to be standardised, it's then trivial to build synchronous functionality on top with whatever concurrency model you prefer. Less is more here I think.
Finally, as a personal viewpoint I don't care much for ASIO's internal implementation. I find it obtuse and hard to debug or indeed figure out much at all as bits of implementation are scattered all over the place. Some if not much of that is because ASIO implements a sort of generic concept type framework all of which requires checking, well the obvious course of action here is to use proper C++ 17 concepts and do away with the legacy design cruft. I'd also personally split the genericity away from the implementation, and push the implementation into a non-header built stable ABI so we can avoid pulling in so many system header files like all of windows.h.
Unfortunately one of its most powerful features (concept based callable handlers and the ability to chain special conditions such as custom allocators and strands) also means that templates permeate almost everywhere, making it really hard to do anything other than header-only. (I haven't read up on C++17 concepts and whether they would help with this or not.)
When I was rolling my own one I did elect to use private implementation instead but it came at a cost of flexibility and performance (particularly since I'm using boost::function to break the template chain instead of something more lightweight); in particular mine doesn't support custom allocators and while it does have strands, they're a bit more brittle and you need to be more careful how you use them or you can get unexpected concurrency. (It's still a net win in my case due to lower latency and lock avoidance, but this is not a tradeoff everyone would want to make.)
Well ... AFIO provides a stable ABI which contains and isolates the platform specific implementation. Said ABI isn't really intended for humans to write against, but rather metaprogramming to assemble for you. I see no reason ASIO couldn't be similarly structured if one can assume C++14 as a minimum. A lot of why ASIO is the way it is is due to C++ 03.
I might be wrong about this, but I get the impression that a lot of the internal implementation code is duplicated rather than being factored to common methods in order to avoid conditionals and improve performance without relying on compiler inlining. Which can also contribute to making it hard to read and understand.
I agree with this claim. Bugs which appear in one function e.g. truncating data sends to 64k can usually be worked around by using another function. There is indeed too much copy and paste implementation in there, and as you said it's mostly due to the template concept stuff all of which was forced by 03's inferior facilities. constexpr alone would transform a ton of that stuff, it could potentially as a guestimate lop 40% of lines of code off the implementation. I think it's going to be very tough to standardise ASIO :( Niall -- ned Productions Limited Consulting http://www.nedproductions.biz/ http://ie.linkedin.com/in/nialldouglas/
On Tue, Jul 1, 2014 at 6:23 AM, Niall Douglas <s_sourceforge@nedprod.com> wrote: ... I think it's going to be very tough to standardise ASIO :(
It is very tough to standardize a lot of libraries. Who would have thought Optional would be as difficult as it is turning out to be? However, (1) the committee has 20 years of experience with difficult to standardize libraries and is getting better at it, (2) the specification is what gets standardized, not the implementation, and that takes a lot of issues off the table, (3) the initial target is a Technical Specification, and the bar there is quite a bit lower, (4) it is usually acceptable for big libraries like ASIO to begin life lightly specified and let the specification mature over time. --Beman
On Mon, Jun 30, 2014 at 5:09 PM, Niall Douglas <s_sourceforge@nedprod.com> wrote:
[snip]
There is also a strong argument that anything in ASIO which isn't async needs to go. Plus, some might feel that ASIO's current design uses too much malloc for embedded systems/ultra low latency, and I would sympathise :)
I would hope Allocators would be added to ASIO in the standard. It is difficult to limit memory usage without Allocators in embedded systems. [snip]
Niall
-- ned Productions Limited Consulting http://www.nedproductions.biz/ http://ie.linkedin.com/in/nialldouglas/
Regards, -- Felipe Magno de Almeida
On 1 July 2014 22:45, Felipe Magno de Almeida wrote:
I would hope Allocators would be added to ASIO in the standard. It is difficult to limit memory usage without Allocators in embedded systems.
I hate this recurring theme of "let's take something that works well today and then fsck it up by insisting it has allocator support"
On 4 Jul 2014, at 10:47, Jonathan Wakely <jwakely.boost@kayari.org> wrote:
On 1 July 2014 22:45, Felipe Magno de Almeida wrote:
I would hope Allocators would be added to ASIO in the standard. It is difficult to limit memory usage without Allocators in embedded systems.
I hate this recurring theme of "let's take something that works well today and then fsck it up by insisting it has allocator support”
Since I am far less familiar with these issues than you are the solution to avoiding detriment to designs by having allocator support is not obvious to me. What should we do instead of adding allocator support? Should we be improving the standard allocator like the implementations in Boost.Container, or are you suggesting that any standardisation of the allocator Concept would lead to a deterioration of the design? My own view is that the standard allocator is clearly suboptimal even for standard containers. This is evident from the performance improvements obtained in Boost.Container. I have noticed though that the improved allocators do not provide superior performance on Linux systems over the standard implementations. I’m very interested in your proposed solutions as I suspect there is much to learn from your experience working on the standard library implementations. Regards, Neil Groves
_______________________________________________ Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost
On Fri, Jul 4, 2014 at 12:46 PM, Neil Groves <neilgroves@gmail.com> wrote:
On 4 Jul 2014, at 10:47, Jonathan Wakely <jwakely.boost@kayari.org> wrote:
On 1 July 2014 22:45, Felipe Magno de Almeida wrote:
I would hope Allocators would be added to ASIO in the standard. It is difficult to limit memory usage without Allocators in embedded systems.
I hate this recurring theme of "let's take something that works well today and then fsck it up by insisting it has allocator support”
Since I am far less familiar with these issues than you are the solution to avoiding detriment to designs by having allocator support is not obvious to me. What should we do instead of adding allocator support? Should we be improving the standard allocator like the implementations in Boost.Container, or are you suggesting that any standardisation of the allocator Concept would lead to a deterioration of the design? My own view is that the standard allocator is clearly suboptimal even for standard containers. This is evident from the performance improvements obtained in Boost.Container. I have noticed though that the improved allocators do not provide superior performance on Linux systems over the standard implementations. I’m very interested in your proposed solutions as I suspect there is much to learn from your experience working on the standard library implementations.
As a quick side-note, there have been recent C++ proposals for improving the situation, like N3525[1]. (I don't know if Boost.Container already have similar features.) [1] http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2013/n3525.pdf
On Fri, Jul 4, 2014 at 12:46 PM, Neil Groves <neilgroves@gmail.com> wrote:
On 4 Jul 2014, at 10:47, Jonathan Wakely <jwakely.boost@kayari.org> wrote:
On 1 July 2014 22:45, Felipe Magno de Almeida wrote:
I would hope Allocators would be added to ASIO in the standard. It is difficult to limit memory usage without Allocators in embedded systems.
I hate this recurring theme of "let's take something that works well today and then fsck it up by insisting it has allocator support”
Since I am far less familiar with these issues than you are the solution to avoiding detriment to designs by having allocator support is not obvious to me. What should we do instead of adding allocator support? Should we be improving the standard allocator like the implementations in Boost.Container, or are you suggesting that any standardisation of the allocator Concept would lead to a deterioration of the design? My own view is that the standard allocator is clearly suboptimal even for standard containers. This is evident from the performance improvements obtained in Boost.Container. I have noticed though that the improved allocators do not provide superior performance on Linux systems over the standard implementations. I’m very interested in your proposed solutions as I suspect there is much to learn from your experience working on the standard library implementations.
As a quick side-note, there have been recent C++ proposals for improving the situation, like N3525[1]. (I don't know if Boost.Container already have similar features.)
[1] http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2013/n3525.pdf
Another side-note: This proposal is part of the Library Fundamentals TS v1, currently in PDTS. see https://isocpp.org/std/status for more details, and the "Work Items by Subgroup"-tab for the working paper Mats Taraldsvik
_______________________________________________ Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost
On 4 Jul 2014 at 11:46, Neil Groves wrote:
I would hope Allocators would be added to ASIO in the standard. It is difficult to limit memory usage without Allocators in embedded systems.
I hate this recurring theme of "let's take something that works well today and then fsck it up by insisting it has allocator support"
Since I am far less familiar with these issues than you are the solution to avoiding detriment to designs by having allocator support is not obvious to me. What should we do instead of adding allocator support? Should we be improving the standard allocator like the implementations in Boost.Container, or are you suggesting that any standardisation of the allocator Concept would lead to a deterioration of the design? My own view is that the standard allocator is clearly suboptimal even for standard containers. This is evident from the performance improvements obtained in Boost.Container. I have noticed though that the improved allocators do not provide superior performance on Linux systems over the standard implementations. ITMm very interested in your proposed solutions as I suspect there is much to learn from your experience working on the standard library implementations.
There are lots of things going on here. STL allocators were always half baked because they were never finished - John Lakos is but one of many who took them to all sorts of interesting new places, indeed Stepanov's original design had enormous potential. Unfortunately, we've ended up stuck with these half finished things, and now they are creating slow path performance with std::vector<std::vector<T>> because STL containers can't have a noexcept move constructor due to allocators! Allocators are getting patched for C++17 right now, but ultimately the problem remains the same one as why they were never finished and left half baked - to take them further in any direction will benefit some uses but penalise others, so we've collectively decided that's as good as it gets for now and left it alone: no one is seriously proposing replacing STL allocators completely with something totally different. Regarding ASIO and allocators, ideally ASIO doesn't allocate nor deallocate any memory at all during its normal course of execution, thus removing much need for allocators and keeping latency low. Achieving that is probably hard - just look at promise/future, in there is an implied unavoidable malloc/free. And allocator support for promise/future really is a mess, indeed Boost.Thread still doesn't support that, and if I have my way it never will because promise/future under C++11 or later is going to become malloc-free, thereby sidestepping the problem. Niall -- ned Productions Limited Consulting http://www.nedproductions.biz/ http://ie.linkedin.com/in/nialldouglas/
On 4 July 2014 12:44, Niall Douglas wrote:
There are lots of things going on here. STL allocators were always half baked because they were never finished - John Lakos is but one of many who took them to all sorts of interesting new places, indeed Stepanov's original design had enormous potential. Unfortunately, we've ended up stuck with these half finished things, and now they are creating slow path performance with std::vector<std::vector<T>> because STL containers can't have a noexcept move constructor due to allocators!
That's not true, the move constructor always propagates the allocator so doesn't need to re-allocate. The reason for it not being noexcept is not because of allocators, and at the last meeting we agreed that vector and string should have noexcept move constructors, always. Move assignment might need to reallocate for non-equal allocators, so that can't always be noexcept (but the implementation can add a conditional noexcept using the propagate_on_container_move_assignment trait, if your vendor doesn't do that complain to them :-)
Scent from my HTC ----- Reply message ----- From: "Jonathan Wakely" <jwakely.boost@kayari.org> To: "boost@lists.boost.org" <boost@lists.boost.org> Subject: [boost] ASIO into the standard (was: Re: C++ committee meeting report) Date: Fri, Jul 4, 2014 15:45 On 4 July 2014 12:44, Niall Douglas wrote:
There are lots of things going on here. STL allocators were always half baked because they were never finished - John Lakos is but one of many who took them to all sorts of interesting new places, indeed Stepanov's original design had enormous potential. Unfortunately, we've ended up stuck with these half finished things, and now they are creating slow path performance with std::vector<std::vector<T>> because STL containers can't have a noexcept move constructor due to allocators!
That's not true, the move constructor always propagates the allocator so doesn't need to re-allocate. The reason for it not being noexcept is not because of allocators, and at the last meeting we agreed that vector and string should have noexcept move constructors, always. Move assignment might need to reallocate for non-equal allocators, so that can't always be noexcept (but the implementation can add a conditional noexcept using the propagate_on_container_move_assignment trait, if your vendor doesn't do that complain to them :-) _______________________________________________ Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost
Sent from my HTC ----- Reply message ----- From: "Jonathan Wakely" <jwakely.boost@kayari.org> To: "boost@lists.boost.org" <boost@lists.boost.org> Subject: [boost] ASIO into the standard (was: Re: C++ committee meeting report) Date: Fri, Jul 4, 2014 15:45 On 4 July 2014 12:44, Niall Douglas wrote:
There are lots of things going on here. STL allocators were always half baked because they were never finished - John Lakos is but one of many who took them to all sorts of interesting new places, indeed Stepanov's original design had enormous potential. Unfortunately, we've ended up stuck with these half finished things, and now they are creating slow path performance with std::vector<std::vector<T>> because STL containers can't have a noexcept move constructor due to allocators!
That's not true, the move constructor always propagates the allocator so doesn't need to re-allocate. The reason for it not being noexcept is not because of allocators, and at the last meeting we agreed that vector and string should have noexcept move constructors, always. Move assignment might need to reallocate for non-equal allocators, so that can't always be noexcept (but the implementation can add a conditional noexcept using the propagate_on_container_move_assignment trait, if your vendor doesn't do that complain to them :-) _______________________________________________ Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost
Sent from my, HTC::: ----- Reply message ----- From: "Jonathan Wakely" <jwakely.boost@kayari.org> To: "boost@lists.boost.org" <boost@lists.boost.org> Subject: [boost] ASIO into the standard (was: Re: C++ committee meeting report) Date: Fri, Jul 4, 2014 15:45 On 4 July 2014 12:44, Niall Douglas wrote:
There are lots of things going on here. STL allocators were always half baked because they were never finished - John Lakos is but one of many who took them to all sorts of interesting new places, indeed Stepanov's original design had enormous potential. Unfortunately, we've ended up stuck with these half finished things, and now they are creating slow path performance with std::vector<std::vector<T>> because STL containers can't have a noexcept move constructor due to allocators!
That's not true, the move constructor always propagates the allocator so doesn't need to re-allocate. The reason for it not being noexcept is not because of allocators, and at the last meeting we agreed that vector and string should have noexcept move constructors, always. Move assignment might need to reallocate for non-equal allocators, so that can't always be noexcept (but the implementation can add a conditional noexcept using the propagate_on_container_move_assignment trait, if your vendor doesn't do that complain to them :-) _______________________________________________ Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost
On 4 Jul 2014 at 15:44, Jonathan Wakely wrote:
On 4 July 2014 12:44, Niall Douglas wrote:
There are lots of things going on here. STL allocators were always half baked because they were never finished - John Lakos is but one of many who took them to all sorts of interesting new places, indeed Stepanov's original design had enormous potential. Unfortunately, we've ended up stuck with these half finished things, and now they are creating slow path performance with std::vector<std::vector<T>> because STL containers can't have a noexcept move constructor due to allocators!
That's not true, the move constructor always propagates the allocator so doesn't need to re-allocate. The reason for it not being noexcept is not because of allocators, and at the last meeting we agreed that vector and string should have noexcept move constructors, always.
Move assignment might need to reallocate for non-equal allocators, so that can't always be noexcept (but the implementation can add a conditional noexcept using the propagate_on_container_move_assignment trait, if your vendor doesn't do that complain to them :-)
I am grateful for the clarification, but I think my original statement was correct yes? STL containers don't have a noexcept move constructor in C++ 11 due to allocators? I am aware it's being fixed. There was a presentation and meeting at C++ Now about it. Niall -- ned Productions Limited Consulting http://www.nedproductions.biz/ http://ie.linkedin.com/in/nialldouglas/
On 5 July 2014 21:00, Niall Douglas wrote:
I am grateful for the clarification, but I think my original statement was correct yes? STL containers don't have a noexcept move constructor in C++ 11 due to allocators?
No, that's not correct. It's because some implementations either need at least one node even in an empty container (which requires the moved-from object to allocate memory after it's move from) or because debugging "safe STL" implementations perform memory allocation during a move. Although those reasons are related to memory allocation, they're not to do with Allocators, the same reasons would apply if everything used malloc().
On 8 Jul 2014 at 17:03, Jonathan Wakely wrote:
On 5 July 2014 21:00, Niall Douglas wrote:
I am grateful for the clarification, but I think my original statement was correct yes? STL containers don't have a noexcept move constructor in C++ 11 due to allocators?
No, that's not correct.
It's because some implementations either need at least one node even in an empty container (which requires the moved-from object to allocate memory after it's move from) or because debugging "safe STL" implementations perform memory allocation during a move.
Although those reasons are related to memory allocation, they're not to do with Allocators, the same reasons would apply if everything used malloc().
I think we might be at cross purposes here due to my sloppy phrasing. I didn't mean that allocators per se are at fault, I did mean that the present design of STL allocators are at fault. If they had a different design the issues you mentioned wouldn't constrain noexcept, though they probably would constrain something else even more important. For example, a better allocator design could guarantee that zero sized allocations always succeed, or that known temporary allocations always use alloca(), or guarantee non-relocating reallocs, or copy on write page table duplicate maps. That sort of thing. Such flexibility would eliminate the constraints the present design imposes upon their users. Niall -- ned Productions Limited Consulting http://www.nedproductions.biz/ http://ie.linkedin.com/in/nialldouglas/
On 8 July 2014 23:44, Niall Douglas wrote:
On 8 Jul 2014 at 17:03, Jonathan Wakely wrote:
On 5 July 2014 21:00, Niall Douglas wrote:
I am grateful for the clarification, but I think my original statement was correct yes? STL containers don't have a noexcept move constructor in C++ 11 due to allocators?
No, that's not correct.
It's because some implementations either need at least one node even in an empty container (which requires the moved-from object to allocate memory after it's move from) or because debugging "safe STL" implementations perform memory allocation during a move.
Although those reasons are related to memory allocation, they're not to do with Allocators, the same reasons would apply if everything used malloc().
I think we might be at cross purposes here due to my sloppy phrasing.
I didn't mean that allocators per se are at fault, I did mean that the present design of STL allocators are at fault.
And I still disagree. Some implementations want to allocate memory in their move constructors. Memory allocation might fail and throw an exception. I don't see how the STL allocator design has any bearing on that.
If they had a different design the issues you mentioned wouldn't constrain noexcept, though they probably would constrain something else even more important.
For example, a better allocator design could guarantee that zero sized allocations always succeed, or that known temporary allocations always use alloca(), or guarantee non-relocating reallocs, or copy on write page table duplicate maps. That sort of thing. Such flexibility would eliminate the constraints the present design imposes upon their users.
But would have no effect on whether move constructors that allocate memory can throw.
Niall, is your non-allocating future promise going to borrow from my C++Now slides, or are you trying a different direction? Tony Sent from my portable Analytical Engine ------------------------------ *From:* "Niall Douglas" <s_sourceforge@nedprod.com> *To:* "boost@lists.boost.org" <boost@lists.boost.org> *Sent:* 4 July, 2014 7:46 AM *Subject:* Re: [boost] ASIO into the standard (was: Re: C++ committee meeting report) On 4 Jul 2014 at 11:46, Neil Groves wrote:
I would hope Allocators would be added to ASIO in the standard. It is difficult to limit memory usage without Allocators in embedded systems.
I hate this recurring theme of "let's take something that works well today and then fsck it up by insisting it has allocator support"
Since I am far less familiar with these issues than you are the solution to avoiding detriment to designs by having allocator support is not obvious to me. What should we do instead of adding allocator support? Should we be improving the standard allocator like the implementations in Boost.Container, or are you suggesting that any standardisation of the allocator Concept would lead to a deterioration of the design? My own view is that the standard allocator is clearly suboptimal even for standard containers. This is evident from the performance improvements obtained in Boost.Container. I have noticed though that the improved allocators do not provide superior performance on Linux systems over the standard implementations. ITMm very interested in your proposed solutions as I suspect there is much to learn from your experience working on the standard library implementations.
There are lots of things going on here. STL allocators were always half baked because they were never finished - John Lakos is but one of many who took them to all sorts of interesting new places, indeed Stepanov's original design had enormous potential. Unfortunately, we've ended up stuck with these half finished things, and now they are creating slow path performance with std::vector> because STL containers can't have a noexcept move constructor due to allocators! Allocators are getting patched for C++17 right now, but ultimately the problem remains the same one as why they were never finished and left half baked - to take them further in any direction will benefit some uses but penalise others, so we've collectively decided that's as good as it gets for now and left it alone: no one is seriously proposing replacing STL allocators completely with something totally different. Regarding ASIO and allocators, ideally ASIO doesn't allocate nor deallocate any memory at all during its normal course of execution, thus removing much need for allocators and keeping latency low. Achieving that is probably hard - just look at promise/future, in there is an implied unavoidable malloc/free. And allocator support for promise/future really is a mess, indeed Boost.Thread still doesn't support that, and if I have my way it never will because promise/future under C++11 or later is going to become malloc-free, thereby sidestepping the problem. Niall -- ned Productions Limited Consultinghttp://www.nedproductions.biz/ http://ie.linkedin.com/in/nialldouglas/
On 6 Jul 2014 at 5:20, Gottlob Frege wrote:
Niall, is your non-allocating future promise going to borrow from my C++Now slides, or are you trying a different direction?
You proposed a non-allocating future promise at C++ Now??? I just rechecked your slides and I see nothing related? Can you supply me some detail? The direction I was going to take was to extend proposed std::experimental::optional<E, T> as the value store which would also allow Vicente's monadic framework to interop seamlessly with future promise. The promise and future would be tied together with two atomically updated pointers. As much as it is very straightforward, what has diverted me is that my attempt used memory transactions (I tried both __transaction_atomic and Intel TSX) and their performance greatly surprised me, as I think it probably did to the other devs on Boost.Thread who I CC-ied privately. I then resolved to learn a feel for memory transactions as I clearly didn't have the experience, so for the past two weeks I have been implementing a concurrent_unordered_map<K, T> based on memory transactions. My first attempt was a simple forward linked list which worked well up until the load factor went past about four, at which point performance collapsed. So the current attempt is based on a table design where if the transaction is unavailable, it uses the bottom bit of the pointer to the value as a fallback spinlock as is required by Intel TSX. Debugging it has been slow going, not helped by this sort of work being hard to mentally gear into after a day of work plus Clara had her vaccinations this past week, which hasn't made for much sleep. I'll get there eventually and probably find the traditional split order list method of implementing concurrent_unordered_map is far faster :( (though, to be honest, I cannot currently see how that would be possible as split order lists are not as cache friendly as what I am doing). Niall -- ned Productions Limited Consulting http://www.nedproductions.biz/ http://ie.linkedin.com/in/nialldouglas/
https://github.com/boostcon/cppnow_presentations_2013/blob/master/mon/future... 2013. The talk is called "Non-Allocating std::future/promise". I think most of it is about a... non-allocating future-promise. Hopefully. That was at least the idea. In brief: - each has a pointer to the other - the result is stored in the future - if one moves or is destroyed, it is... tricky. But basically a CAS and a small spin lock in a rare case when you need to wait for the other side to update the pointer. Yes, replacing the spin and pointer updating with TM would be nice. Pretty much all my code fits on one (crowded) slide near the end of the presentation. Tony On Sun, Jul 6, 2014 at 6:21 AM, Niall Douglas <s_sourceforge@nedprod.com> wrote:
On 6 Jul 2014 at 5:20, Gottlob Frege wrote:
Niall, is your non-allocating future promise going to borrow from my C++Now slides, or are you trying a different direction?
You proposed a non-allocating future promise at C++ Now???
I just rechecked your slides and I see nothing related? Can you supply me some detail?
The direction I was going to take was to extend proposed std::experimental::optional<E, T> as the value store which would also allow Vicente's monadic framework to interop seamlessly with future promise. The promise and future would be tied together with two atomically updated pointers. As much as it is very straightforward, what has diverted me is that my attempt used memory transactions (I tried both __transaction_atomic and Intel TSX) and their performance greatly surprised me, as I think it probably did to the other devs on Boost.Thread who I CC-ied privately.
I then resolved to learn a feel for memory transactions as I clearly didn't have the experience, so for the past two weeks I have been implementing a concurrent_unordered_map<K, T> based on memory transactions. My first attempt was a simple forward linked list which worked well up until the load factor went past about four, at which point performance collapsed. So the current attempt is based on a table design where if the transaction is unavailable, it uses the bottom bit of the pointer to the value as a fallback spinlock as is required by Intel TSX.
Debugging it has been slow going, not helped by this sort of work being hard to mentally gear into after a day of work plus Clara had her vaccinations this past week, which hasn't made for much sleep. I'll get there eventually and probably find the traditional split order list method of implementing concurrent_unordered_map is far faster :( (though, to be honest, I cannot currently see how that would be possible as split order lists are not as cache friendly as what I am doing).
Niall
-- ned Productions Limited Consulting http://www.nedproductions.biz/ http://ie.linkedin.com/in/nialldouglas/
_______________________________________________ Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost
On 7 Jul 2014 at 1:50, Gottlob Frege wrote:
https://github.com/boostcon/cppnow_presentations_2013/blob/master/mon/future...
2013. The talk is called "Non-Allocating std::future/promise". I think most of it is about a... non-allocating future-promise. Hopefully. That was at least the idea.
Dear dear dear ... given that I was working at BlackBerry with you at the time, and went with you to C++ Now, I really don't know how I missed this. I'm going to assume that I didn't miss this and instead choose to forget and then pretend that your idea was my idea. So, my apologies, and I'll credit you in the docs when the time comes.
In brief: - each has a pointer to the other - the result is stored in the future - if one moves or is destroyed, it is... tricky. But basically a CAS and a small spin lock in a rare case when you need to wait for the other side to update the pointer.
The problem with this is it isn't space optimal. Space optimal means storing the spinlock in the low bits of the memory pointer instead of wasting another four or eight bytes on it, which is what I've done. Performance is indeed impacted by a bit, but not that much - after all it's all the same cache line anyway. I also didn't see lock backoff in your example code i.e. where both the promise and future are concurrently moved from different threads. Did I miss something?
Yes, replacing the spin and pointer updating with TM would be nice.
And here is where things become very interesting. I started off assuming, as you would, that TM has two big wins: 1. You can update two or more disparate pointers atomically. Very handy for lock and wait free removal of items from linked lists. 2. You can skip locking where concurrent updating isn't a problem. How this works is you take care to volatile read from all those cache lines which if written to without locks would corrupt state, then you write as little as possible yourself. Where I have made a mistake is in assuming that TM is free. It is not: Intel TSX HLE is significantly slower for the single threaded case, while TSX RTM is not free to configure. For a simple two pointer update, using RTM is slower than a spinlock for example. If you compile with transactional GCC, all your code becomes 40% slower so you don't win in performance there either. Which suggests that TSX really needs a bigger chunk of transaction to make the overhead worth it, and hence why I thought I should write a TSX concurrent_unordered_map which, as I learned last night, runs at about 40% the single threaded performance of a spin locked unordered_map which is unacceptable. A split ordered list implementation using cmpxchg is twice quicker, but has the cost that erase is unsafe. So back to the drawing board again. I'm now thinking of simplifying by requiring that the mapped type is a shared_ptr<T> and I'll see what sort of design that might yield. I am finding that using TM is repeatedly not worth it so far due to the costs on single theaded performance, far more frequently than I expected. Maybe the next Intel chip will improve TSX's overheads substantially. Niall -- ned Productions Limited Consulting http://www.nedproductions.biz/ http://ie.linkedin.com/in/nialldouglas/
On Tue, Jul 8, 2014 at 7:57 AM, Niall Douglas <s_sourceforge@nedprod.com> wrote:
On 7 Jul 2014 at 1:50, Gottlob Frege wrote:
https://github.com/boostcon/cppnow_presentations_2013/blob/master/mon/future...
2013. The talk is called "Non-Allocating std::future/promise". I think most of it is about a... non-allocating future-promise. Hopefully. That was at least the idea.
Dear dear dear ... given that I was working at BlackBerry with you at the time, and went with you to C++ Now, I really don't know how I missed this.
I'm going to assume that I didn't miss this and instead choose to forget and then pretend that your idea was my idea. So, my apologies, and I'll credit you in the docs when the time comes.
In brief: - each has a pointer to the other - the result is stored in the future - if one moves or is destroyed, it is... tricky. But basically a CAS and a small spin lock in a rare case when you need to wait for the other side to update the pointer.
The problem with this is it isn't space optimal. Space optimal means storing the spinlock in the low bits of the memory pointer instead of wasting another four or eight bytes on it, which is what I've done. Performance is indeed impacted by a bit, but not that much - after all it's all the same cache line anyway.
I also didn't see lock backoff in your example code i.e. where both the promise and future are concurrently moved from different threads. Did I miss something?
Yes, replacing the spin and pointer updating with TM would be nice.
And here is where things become very interesting.
I started off assuming, as you would, that TM has two big wins:
1. You can update two or more disparate pointers atomically. Very handy for lock and wait free removal of items from linked lists.
2. You can skip locking where concurrent updating isn't a problem. How this works is you take care to volatile read from all those cache lines which if written to without locks would corrupt state, then you write as little as possible yourself.
Where I have made a mistake is in assuming that TM is free. It is not: Intel TSX HLE is significantly slower for the single threaded case, while TSX RTM is not free to configure. For a simple two pointer update, using RTM is slower than a spinlock for example. If you compile with transactional GCC, all your code becomes 40% slower so you don't win in performance there either.
Which suggests that TSX really needs a bigger chunk of transaction to make the overhead worth it, and hence why I thought I should write a TSX concurrent_unordered_map which, as I learned last night, runs at about 40% the single threaded performance of a spin locked unordered_map which is unacceptable. A split ordered list implementation using cmpxchg is twice quicker, but has the cost that erase is unsafe.
So back to the drawing board again. I'm now thinking of simplifying by requiring that the mapped type is a shared_ptr<T> and I'll see what sort of design that might yield. I am finding that using TM is repeatedly not worth it so far due to the costs on single theaded performance, far more frequently than I expected. Maybe the next Intel chip will improve TSX's overheads substantially.
Niall
I got the impression that writing in a transaction could be the expensive part, especially if it was contested (having to rollback, etc). However, if you entered a critical section for only reading, there would be less of a penalty since it never "dirtied" the cacheline. Have you tested that too (lots of readers few writers)? Intel's tbb::speculative_spin_rw_lock _really_ makes sure that atomic flag is on its own cacheline (padding everywhere), and acquiring the reader doesn't appear to do a write. Although, the single threaded performance has me thinking that I am mistaken; I feel like a novice despite reading so much about hardware memory barriers. Lee
On 8 Jul 2014 at 22:29, Lee Clagett wrote:
So back to the drawing board again. I'm now thinking of simplifying by requiring that the mapped type is a shared_ptr<T> and I'll see what sort of design that might yield. I am finding that using TM is repeatedly not worth it so far due to the costs on single theaded performance, far more frequently than I expected. Maybe the next Intel chip will improve TSX's overheads substantially.
I got the impression that writing in a transaction could be the expensive part, especially if it was contested (having to rollback, etc).
Aborting a RTM transaction is *very* expensive. I think this is why HLE clearly uses a different internal implementation to RTM. You also can only touch about 100 cache lines in a transaction before you have a 50% chance of it aborting irrespective due to exceeding internal buffer capacities (half the L1 cache is available for TM, but it's shared).
However, if you entered a critical section for only reading, there would be less of a penalty since it never "dirtied" the cacheline. Have you tested that too (lots of readers few writers)? Intel's tbb::speculative_spin_rw_lock _really_ makes sure that atomic flag is on its own cacheline (padding everywhere), and acquiring the reader doesn't appear to do a write.
I tried a many reader few writer approach (80/20 split) where readers never write any cache lines. Aborts are even more costly than a many writer approach, I assume because more cache lines must be thrown away as more cache lines are marked as touched by the readers before a writer collides with them. Yeah, I was surprised too. Putting the fallback atomic flag into its own cacheline is okay as a once off for maybe an entire container. Per-bucket it's excessive, and per-future would be crazy. BTW my RTM-enhanced spinlock doesn't acquire the spinlock but instead starts a transaction which will abort if someone does acquire the spinlock. That way all users of the spinlock use the critically sectioned code without actually locking the spinlock. I did this because a HLE enhanced spinlock is so slow in single threaded code, whereas a RTM enhanced spinlock had acceptable single threaded performance costs (~3%).
Although, the single threaded performance has me thinking that I am mistaken; I feel like a novice despite reading so much about hardware memory barriers.
Well, do bear in mind this stuff isn't my forte. I could simply be incompetent. It doesn't help I'm working on this stuff after a full day of work, so my brain is pretty tired. I'm sure when someone like Andrey gets onto this stuff he'll see much better results than I have. Niall -- ned Productions Limited Consulting http://www.nedproductions.biz/ http://ie.linkedin.com/in/nialldouglas/
On Tue, Jul 8, 2014 at 7:57 AM, Niall Douglas <s_sourceforge@nedprod.com> wrote:
On 7 Jul 2014 at 1:50, Gottlob Frege wrote:
https://github.com/boostcon/cppnow_presentations_2013/blob/master/mon/future...
2013. The talk is called "Non-Allocating std::future/promise". I think most of it is about a... non-allocating future-promise. Hopefully. That was at least the idea.
Dear dear dear ... given that I was working at BlackBerry with you at the time, and went with you to C++ Now, I really don't know how I missed this.
I'm going to assume that I didn't miss this and instead choose to forget and then pretend that your idea was my idea. So, my apologies, and I'll credit you in the docs when the time comes.
I _thought_ you were in the audience, but that could have been one of my other talks. At work, I hardly talked about it, so if you weren't at the talk, you could have easily missed it. Chandler said that Google also ended up with similar code, so we are all thinking along the same lines. Chandler had some good ideas for handling the exceptions as well (ie if thrown when setting the value). It is hard to be 100% standards compliant (since the standard basically assumes every implementation uses an allocated storage location, and those assumptions leak into the interface).
In brief: - each has a pointer to the other - the result is stored in the future - if one moves or is destroyed, it is... tricky. But basically a CAS and a small spin lock in a rare case when you need to wait for the other side to update the pointer.
The problem with this is it isn't space optimal. Space optimal means storing the spinlock in the low bits of the memory pointer instead of wasting another four or eight bytes on it, which is what I've done. Performance is indeed impacted by a bit, but not that much - after all it's all the same cache line anyway.
Yeah, it looks like I have about 5 states, but I think that is mostly for exposition. Probably only need 2 bits.
I also didn't see lock backoff in your example code i.e. where both the promise and future are concurrently moved from different threads. Did I miss something?
Yeah, there is definitely potential for spinning there. Each side could mark their own state as MOVING then check the other side, sees it is MOVING, so goes back to the 0 state and retries. So without backoff this could go on forever. In this case an easy "let the future side win" rule could be done (since the two sides are _not_ equal, in particular you expect the promise to be slower anyhow). In my slides I have a pause() function. Not sure if I mentioned that it could/should do backoff. I can never remember what I said during a talk, and the slides often don't make sense on their own. :-(
Yes, replacing the spin and pointer updating with TM would be nice.
And here is where things become very interesting.
<...interesting TM stuff...> Yes, keep us informed. I've been assuming TM won't work well for "big" transactions, but I have no idea yet what is big and what is small. Of course, we could also just ask the TM guys, like Michael Wong et al. But nothing beats experiencing it for yourself. Tony
On 8 Jul 2014 at 23:12, Gottlob Frege wrote:
2013. The talk is called "Non-Allocating std::future/promise". I think most of it is about a... non-allocating future-promise. Hopefully. That was at least the idea.
Dear dear dear ... given that I was working at BlackBerry with you at the time, and went with you to C++ Now, I really don't know how I missed this.
I'm going to assume that I didn't miss this and instead choose to forget and then pretend that your idea was my idea. So, my apologies, and I'll credit you in the docs when the time comes.
I _thought_ you were in the audience, but that could have been one of my other talks.
I was at *one* of your talks. I also almost certainly reviewed your slides as I do for most C++ Now talks I don't make it to. I have no excuse really, just failing memory (though in fairness, it's been an awfully full two years for me, two transatlantic relocations, first baby etc, I can see some memories have got deleted to make space)
At work, I hardly talked about it, so if you weren't at the talk, you could have easily missed it. Chandler said that Google also ended up with similar code, so we are all thinking along the same lines. Chandler had some good ideas for handling the exceptions as well (ie if thrown when setting the value). It is hard to be 100% standards compliant (since the standard basically assumes every implementation uses an allocated storage location, and those assumptions leak into the interface).
Boost.Thread's promise-future doesn't implement allocator support, so a de-malloced implementation shouldn't lose us too much (I agree we'll have to slightly deviate from the standard in some APIs, but TBH it's the standard that needs fixing here, promise-future shouldn't allocate). I need a malloc-free promise-future for AFIO. I see an exact latency resonance peak at one thread sleep duration, and upon investigation it's because the futures are sleeping the thread due to malloc being latency lumpy. AFIO also currently does eight malloc/frees per op executed with four inside a global lock, and I'd very much like to see that down to four malloc/frees per op with none inside a global lock. Also the batch hash engine's tasks are too finely grained to use mallocing promise-future. The promise-future adds about 15-20% to each hash round. That needs to become < 5%.
Yes, replacing the spin and pointer updating with TM would be nice.
And here is where things become very interesting.
<...interesting TM stuff...>
Yes, keep us informed. I've been assuming TM won't work well for "big" transactions, but I have no idea yet what is big and what is small.
The upper limit is probably 100 cache lines touched. My current best guess is the small limit is somewhere around 10 cache lines touched, so you need to exceed 10 lines and keep under 50. A narrow window.
Of course, we could also just ask the TM guys, like Michael Wong et al. But nothing beats experiencing it for yourself.
He'll say go use transactional GCC, and he's right. I put in a code path to use __transaction_relaxed as that's the malloc capable one (malloc doesn't abort transactions in transactional GCC, unlike in TSX). Performance was dismal, especially so on non-TSX hardware where it was another order of magnitude slower again. My lesson learned from that is when writing code targeting both TSX and transactional GCC, don't bother with __transaction_relaxed, just use __transaction_atomic and follow the same granularity rules as with TSX. Regarding transactional GCC, it is neat the way you can write metaprogramming which generates code which the compiler optimiser spots can elide all locking completely, then your output runs completely in parallel. That is very hard to do normally in metaprogramming. Niall -- ned Productions Limited Consulting http://www.nedproductions.biz/ http://ie.linkedin.com/in/nialldouglas/
On 4 July 2014 11:46, Neil Groves wrote:
On 4 Jul 2014, at 10:47, Jonathan Wakely <jwakely.boost@kayari.org> wrote:
On 1 July 2014 22:45, Felipe Magno de Almeida wrote:
I would hope Allocators would be added to ASIO in the standard. It is difficult to limit memory usage without Allocators in embedded systems.
I hate this recurring theme of "let's take something that works well today and then fsck it up by insisting it has allocator support”
Since I am far less familiar with these issues than you are the solution to avoiding detriment to designs by having allocator support is not obvious to me. What should we do instead of adding allocator support? Should we be improving the standard allocator like the implementations in Boost.Container,
I'm not familiar with those implementations, so I can't comment on them. Does Boost.Container do more than the C++11 allocator requirements (i.e. support stateful and scoped allocators, via allocator_traits)?
or are you suggesting that any standardisation of the allocator Concept would lead to a deterioration of the design?
Possibly, yes. Every time a new class gets accepted for standardisation it then gets allocator support slapped on, sometimes despite the fact it's never been implemented and in at least one case is impossible to implement (as happened when boost:any became std::experimental::any). If allocator support is important to asio why isn't it there already? Has it been suggested? Has it been implemented? Has it been shown to be useful? The job of the standards committee should not be to "improve" things that work well already (which is not to say that some things can't be adapted to suit additional use cases, such as better control of memory allocation if that really is needed).
My own view is that the standard allocator is clearly suboptimal even for standard containers. This is evident from the performance improvements obtained in Boost.Container. I have noticed though that the improved allocators do not provide superior performance on Linux systems over the standard implementations. I’m very interested in your proposed solutions as I suspect there is much to learn from your experience working on the standard library implementations.
My opinion is that the refrain "it needs allocator support" wastes vast quantities of WG21 committee time and effort for arguably little benefit.
On 4 Jul 2014 at 15:33, Jonathan Wakely wrote:
If allocator support is important to asio why isn't it there already? Has it been suggested? Has it been implemented? Has it been shown to be useful?
ASIO has had allocator support for a long time. It's useful for helping ASIO to not call the memory allocator so much :) Niall -- ned Productions Limited Consulting http://www.nedproductions.biz/ http://ie.linkedin.com/in/nialldouglas/
El 04/07/2014 16:33, Jonathan Wakely wrote:
I'm not familiar with those implementations, so I can't comment on them. Does Boost.Container do more than the C++11 allocator requirements (i.e. support stateful and scoped allocators, via allocator_traits)?
Sorry for jumping so late in this discussion, in case you read this, I'd say that Boost.Container will (in Boost 1.56, already committed in master) have some experimental support for allocators that can support reallocation or burst allocation (right now, only allocators offered by Boost.Container). You can find a bit of information here: http://lists.boost.org/Archives/boost/2014/02/211629.php The extended interface it's too complex to be standardized but at least I wanted to obtain feedback to see if performance benefits are real or only useful in some particular contexts. Then we can propose a reduced interface if widespread benefit can be obtained. Best, Ion
This is evident from the
El 04/07/2014 12:46, Neil Groves escribió: performance improvements obtained in Boost.Container. I have noticed though that the improved allocators do not provide superior performance on Linux systems over the standard implementations. Hi Neil, I'm interested in your feedback. Which kind of scenarios are you using in Linux? The improved allocators should help when inserting ranges or making copies of complete containers. shrink_to_fit is also much faster (it does not move objects). Are you using boost::container::allocator or a more specialized boost::container::adaptive_pool/node_allocator for node-based container? You have some basic benchmarks in boost/libs/container/bench if you want to have some information about the best scenarios. Linux allocator is based on ptmalloc2 which is based on DLMalloc, so for usual allocations they should perform similarly. On multithreaded scenarios the default allocator should be better. In Windows the performance improvement is noticeable. Best, Ion
On 12/07/2014 07:41, Ion Gaztañaga wrote:
Linux allocator is based on ptmalloc2 which is based on DLMalloc, so for usual allocations they should perform similarly. On multithreaded scenarios the default allocator should be better. In Windows the performance improvement is noticeable.
I haven't tested these allocators, but it seems worth mentioning that for general allocations, while ptmalloc2 is a significant performance improvement vs. the Windows XP allocator, it makes little difference vs. the Windows 7 allocator, at least in my tests.
El 14/07/2014 2:33, Gavin Lambert escribió:
On 12/07/2014 07:41, Ion Gaztañaga wrote:
Linux allocator is based on ptmalloc2 which is based on DLMalloc, so for usual allocations they should perform similarly. On multithreaded scenarios the default allocator should be better. In Windows the performance improvement is noticeable.
I haven't tested these allocators, but it seems worth mentioning that for general allocations, while ptmalloc2 is a significant performance improvement vs. the Windows XP allocator, it makes little difference vs. the Windows 7 allocator, at least in my tests.
For single threaded executions, in Windows 7 and Visual 2010 I've seen in bench_alloc_stable_vector (which tests burst allocation for node containers like stable_vector): ----------------------------------- Iterations/Elements: 400/10000 ----------------------------------- Allocator: StdAllocator stable_vector<MyInt> allocation ns: 70.4541 deallocation ns: 2.12974 Allocator: AllocatorPlusV1 stable_vector<MyInt> allocation ns: 56.1535 deallocation ns: 2.07784 Allocator: AllocatorPlusV2 stable_vector<MyInt> allocation ns: 29.7252 deallocation ns: 2.06671 Allocator: AdPool2PercentV2 stable_vector<MyInt> allocation ns: 28.3165 deallocation ns: 1.93169 It shows that when executing a range insertion in stable_vector: l.insert(l.end(), num_elements, MyInt(r)); with Windows allocator we need 70 ns per element, with DLMalloc we need 56 (-20%) and with burst allocation we only need 29ns (-58%). Note that this is not only the time to allocate memory, it counts also the common tasks of linking and updating internal structures. Maybe more recent Visual Studios have improved the default allocator (I think the allocator depends more on the C runtime than in the OS). In MSVC 10.0 the buffer expansion (realloc-like) test (bench_alloc_expand_fwd.cpp): ----------------------------------- Iterations/Elements: 10000/10000 ----------------------------------- Allocator: StdAllocator //std::vector push_back ns: 4.88223 capacity - alloc calls (new/expand): 12138 - 0(0/0) Allocator: StdAllocator //boost::container::vector push_back ns: 3.98395 capacity - alloc calls (new/expand): 16384 - 15(15/0) Allocator: AllocatorPlusV2 //boost::container::vector push_back ns: 2.12294 capacity - alloc calls (new/expand): 16383 - 13(1/12) In MSVC 7.1 the default allocator is slower: Allocator: StdAllocator //std::vector push_back ns: 7.00777 capacity - alloc calls (new/expand): 12138 - 0(0/0) Boost.Container allocators should help when inserting a range of values, making copies of containers, etc. Best, Ion
On Fri, Jul 4, 2014 at 2:47 AM, Jonathan Wakely <jwakely.boost@kayari.org> wrote:
On 1 July 2014 22:45, Felipe Magno de Almeida wrote:
I would hope Allocators would be added to ASIO in the standard. It is difficult to limit memory usage without Allocators in embedded systems.
I hate this recurring theme of "let's take something that works well today and then fsck it up by insisting it has allocator support"
We did that with std::experimental::any in Bristol, and it's now obvious that was a mistake. (Are we getting an NB comment on that and a wording change to fix it? :) Hopefully that example will help us reject any future "let's just add an allocator to this" changes that aren't driven by a well-thought-out argument and an implementation.
On 6 July 2014 10:00, Jeffrey Yasskin wrote:
On Fri, Jul 4, 2014 at 2:47 AM, Jonathan Wakely <jwakely.boost@kayari.org> wrote:
On 1 July 2014 22:45, Felipe Magno de Almeida wrote:
I would hope Allocators would be added to ASIO in the standard. It is difficult to limit memory usage without Allocators in embedded systems.
I hate this recurring theme of "let's take something that works well today and then fsck it up by insisting it has allocator support"
We did that with std::experimental::any in Bristol, and it's now obvious that was a mistake. (Are we getting an NB comment on that and a wording change to fix it? :)
You bet :-)
On 07/01/2014 11:45 PM, Felipe Magno de Almeida wrote:
I would hope Allocators would be added to ASIO in the standard. It is difficult to limit memory usage without Allocators in embedded systems.
http://www.boost.org/doc/html/boost_asio/overview/core/allocation.html I do not know if it is used consistently inside Asio itself.
Em 04/07/2014 08:25, "Bjorn Reese" <breese@mail1.stofanet.dk> escreveu:
On 07/01/2014 11:45 PM, Felipe Magno de Almeida wrote:
I would hope Allocators would be added to ASIO in the standard. It is difficult to limit memory usage without Allocators in embedded systems.
http://www.boost.org/doc/html/boost_asio/overview/core/allocation.html
I do not know if it is used consistently inside Asio itself.
Last time I checked it didn't used allocators on data structures, for example for buckets in a linked list. This might have changed by then. At that time Christopher didn't want to template the whole library to add allocator support, he had already tried and thought it to be impractical.
_______________________________________________ Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost
On 9 Jul 2014 at 22:53, Felipe Magno de Almeida wrote:
I would hope Allocators would be added to ASIO in the standard. It is difficult to limit memory usage without Allocators in embedded systems.
http://www.boost.org/doc/html/boost_asio/overview/core/allocation.html
I do not know if it is used consistently inside Asio itself.
Last time I checked it didn't used allocators on data structures, for example for buckets in a linked list. This might have changed by then. At that time Christopher didn't want to template the whole library to add allocator support, he had already tried and thought it to be impractical.
Nor are allocators consistently used for internals of STL containers. They have to be used for the type being stored of course, but internal data structures not exposed to external code may use any allocator e.g. malloc(). The next TS from ISO has a type erased allocator. Ought to make allocator support much easier. Niall -- ned Productions Limited Consulting http://www.nedproductions.biz/ http://ie.linkedin.com/in/nialldouglas/
On 10/07/2014 04:11 p.m., Niall Douglas wrote:
On 9 Jul 2014 at 22:53, Felipe Magno de Almeida wrote:
I would hope Allocators would be added to ASIO in the standard. It is difficult to limit memory usage without Allocators in embedded systems.
http://www.boost.org/doc/html/boost_asio/overview/core/allocation.html
I do not know if it is used consistently inside Asio itself.
Last time I checked it didn't used allocators on data structures, for example for buckets in a linked list. This might have changed by then. At that time Christopher didn't want to template the whole library to add allocator support, he had already tried and thought it to be impractical.
Nor are allocators consistently used for internals of STL containers. They have to be used for the type being stored of course, but internal data structures not exposed to external code may use any allocator e.g. malloc().
That is precisely why we have `uses_allocator` and `scoped_allocator_adaptor`. http://en.cppreference.com/w/cpp/memory/scoped_allocator_adaptor Regards, -- Agustín K-ballo Bergé.- http://talesofcpp.fusionfenix.com
On Thu, Jul 10, 2014 at 4:11 PM, Niall Douglas <s_sourceforge@nedprod.com> wrote:
On 9 Jul 2014 at 22:53, Felipe Magno de Almeida wrote:
[snip]
Nor are allocators consistently used for internals of STL containers. They have to be used for the type being stored of course, but internal data structures not exposed to external code may use any allocator e.g. malloc().
I thought this only applied to non-templated-on-allocator types. For example, list must use the allocator passed, rebound of course, to allocate its node. Or am I completely mistaken? If so then allocators are just fairy tale.
The next TS from ISO has a type erased allocator. Ought to make allocator support much easier.
Niall
-- ned Productions Limited Consulting http://www.nedproductions.biz/ http://ie.linkedin.com/in/nialldouglas/
Best regards, -- Felipe Magno de Almeida
On 10 Jul 2014 at 23:48, Felipe Magno de Almeida wrote:
Nor are allocators consistently used for internals of STL containers. They have to be used for the type being stored of course, but internal data structures not exposed to external code may use any allocator e.g. malloc().
I thought this only applied to non-templated-on-allocator types. For example, list must use the allocator passed, rebound of course, to allocate its node. Or am I completely mistaken? If so then allocators are just fairy tale.
Of course it must. But if some implementation of list decided to keep some internal housekeeping data, it could use malloc or any other kind of allocator. For example some debug enhanced implementations keep additional debug info. The concurrent_unordered_map I am currently experimenting with uses the externally supplied allocator to allocate the value_type, but uses the default allocator for the bucket allocation. I believe this is permitted. Niall -- ned Productions Limited Consulting http://www.nedproductions.biz/ http://ie.linkedin.com/in/nialldouglas/
On Fri, Jul 11, 2014 at 6:44 AM, Niall Douglas <s_sourceforge@nedprod.com> wrote:
[snip]
The concurrent_unordered_map I am currently experimenting with uses the externally supplied allocator to allocate the value_type, but uses the default allocator for the bucket allocation. I believe this is permitted.
This looks very arbitrary to me. Is there any wording in the standard that supports that allocators have to be used only for allocation of value_type (or classes that has a value_type in it)?
Niall
-- ned Productions Limited Consulting http://www.nedproductions.biz/ http://ie.linkedin.com/in/nialldouglas/
Regards, -- Felipe Magno de Almeida
On Fri, Jul 11, 2014 at 12:18 PM, Felipe Magno de Almeida <felipe.m.almeida@gmail.com> wrote:
This looks very arbitrary to me. Is there any wording in the standard that supports that allocators have to be used only for allocation of value_type (or classes that has a value_type in it)?
My understanding is that the allocators have to be used for all dynamic allocation. If this is not explicitly worded in the standard, then it should be. Otherwise allocators simply are not as useful with any other interpretation. Glen
On 11 July 2014 20:49, Glen Fernandes <glen.fernandes@gmail.com> wrote:
On Fri, Jul 11, 2014 at 12:18 PM, Felipe Magno de Almeida <felipe.m.almeida@gmail.com> wrote:
This looks very arbitrary to me. Is there any wording in the standard that supports that allocators have to be used only for allocation of value_type (or classes that has a value_type in it)?
My understanding is that the allocators have to be used for all dynamic allocation. If this is not explicitly worded in the standard, then it should be. Otherwise allocators simply are not as useful with any other interpretation.
I looked this up earlier, as I thought the same. 23.2.1.7 says: "Unless otherwise specified, all containers defined in this clause obtain memory using an allocator (see 17.6.3.5)." Which suggests to me that all allocation is done using the allocator. I suppose debug info could be considered something that isn't obtained by the container, as it can be external, but I wouldn't have thought that for things like buckets. 23.2.1.3 says: "For the components affected by this subclause that declare an allocator_type, objects stored in these components shall be constructed using the allocator_traits<allocator_type>::construct function and destroyed using the allocator_traits<allocator_type>::destroy function (20.7.8.2). These functions are called only for the container’s element type, not for internal types used by the container." But that's specifying construction, not allocation (btw. I get this wrong in unordered, I'll fix it soon). Did I miss anything? I don't claim any expertise here.
On 11 Jul 2014 at 21:06, Daniel James wrote:
My understanding is that the allocators have to be used for all dynamic allocation. If this is not explicitly worded in the standard, then it should be. Otherwise allocators simply are not as useful with any other interpretation.
I assume by allocators you mean specifically STL allocators?
I looked this up earlier, as I thought the same. 23.2.1.7 says:
"Unless otherwise specified, all containers defined in this clause obtain memory using an allocator (see 17.6.3.5)."
Which suggests to me that all allocation is done using the allocator. I suppose debug info could be considered something that isn't obtained by the container, as it can be external, but I wouldn't have thought that for things like buckets.
The standard definitely means an STL allocator, not simply "an allocator"? It's not just debug info though. I can think of a number of STL containers which could use non-STL allocation for internal stuff - for example, if you create a threadsafe STL container you could allocate an internal mutex, and it would seem excessive to require using an externally supplied allocator for that too. Furthermore, a std::mutex under the hood will almost certainly do some more allocation. You might argue this isn't really allocation of memory. So to counterexample, what about a STL container of shared memory regions? Or a STL container of memory mapped files? You'd want the allocator - given it has 4Kb granularity - to only be used for value types only. I suppose you could specialise the rebind to avoid this happening, but we're beginning to chase our tail now.
23.2.1.3 says:
"For the components affected by this subclause that declare an allocator_type, objects stored in these components shall be constructed using the allocator_traits<allocator_type>::construct function and destroyed using the allocator_traits<allocator_type>::destroy function (20.7.8.2). These functions are called only for the container´s element type, not for internal types used by the container."
But that's specifying construction, not allocation (btw. I get this wrong in unordered, I'll fix it soon).
I have been lazy as it's test code, and implemented a move assignment like this for an internal item_type due to value_type.first being const: struct item_type { value_type p; size_t hash; item_type() : hash(0) { } item_type(value_type &&_p, size_t _hash) BOOST_NOEXCEPT : p(std::move(_p)), hash(_hash) { } item_type(item_type &&o) BOOST_NOEXCEPT : p(std::move(o.p)), hash(o.hash) { } item_type &operator=(item_type &&o) BOOST_NOEXCEPT { this->~item_type(); new (this) item_type(std::move(o)); return *this; } Which would normally be unsafe, except everything is noexcept, so I actually think this is okay except for the fact that the allocator's construct and destroy aren't being invoked.
Did I miss anything? I don't claim any expertise here.
Me neither. When do C++ programmers normally implement STL containers as part of their normal day job? Probably only Stephan. Anyway, they're a bitch to do correctly and keep performance reasonable. Niall -- ned Productions Limited Consulting http://www.nedproductions.biz/ http://ie.linkedin.com/in/nialldouglas/
On Fri, Jul 11, 2014 at 5:49 PM, Niall Douglas wrote:
I assume by allocators you mean specifically STL allocators?
Yes. Or, to be sure we're talking about the same thing, what I'm saying is: For any C++ standard library container class template 'C that has an 'Allocator' template parameter type and whose constructor accepts an instance of 'Allocator': i.e. template<..., class Allocator> class C { ... public: C(..., const Allocator& alloc = Allocator()); ... }; Then the implementation of that container 'C' must use an instance of 'std::allocator_traits<Allocator>::rebind_alloc<unspecified>' that compares equal to 'alloc' (i.e. a rebound copy of 'alloc'), for all dynamic allocation that it does. For "supports allocators" to mean anything less than this, is just not useful. Glen
On 12 July 2014 01:49, Niall Douglas <s_sourceforge@nedprod.com> wrote:
On 11 Jul 2014 at 21:06, Daniel James wrote:
My understanding is that the allocators have to be used for all dynamic allocation. If this is not explicitly worded in the standard, then it should be. Otherwise allocators simply are not as useful with any other interpretation.
I assume by allocators you mean specifically STL allocators?
I looked this up earlier, as I thought the same. 23.2.1.7 says:
"Unless otherwise specified, all containers defined in this clause obtain memory using an allocator (see 17.6.3.5)."
Which suggests to me that all allocation is done using the allocator. I suppose debug info could be considered something that isn't obtained by the container, as it can be external, but I wouldn't have thought that for things like buckets.
The standard definitely means an STL allocator, not simply "an allocator"?
It refers to 17.6.3.5 which is the standard allocator requirements. I've just been re-reading the standard, and it's subtle. The quote about 'allocate' is for 'containers defined in this clause', the quote for 'construct' is for 'components affected by this subclause', the former could be just about the existing standard containers, the latter about all containers that want to meet the standard requirements. The allocator aware container requirements and general/reversible/etc. container requirements don't mention anything about allocation. So, if I'm reading it correctly, std::unordered_set, std::vector, etc. have to always use 'allocate' to allocate memory, but other containers that want to meet the 'allocator aware' requirements don't have to use 'allocate' at all, just 'construct', which I find surprising. It's possible that I've missed something, or that it's an oversight. It might be worth asking on a standards list. Although, if a container is designed to be standardized, the designer will probably have to justify using allocators and not requiring 'allocate'.
I have been lazy as it's test code, and implemented a move assignment like this for an internal item_type due to value_type.first being const:
I move the nodes over when possible (when the allocators propagate on move or are equal), but copy the key otherwise.
On 12 Jul 2014 at 10:20, Daniel James wrote:
I have been lazy as it's test code, and implemented a move assignment like this for an internal item_type due to value_type.first being const:
I move the nodes over when possible (when the allocators propagate on move or are equal), but copy the key otherwise.
How can you copy the key if it's const? It seemed to me, unless I have missed something, that I need to do an allocator destroy followed by at least an allocator copy construct as the key is const? Niall -- ned Productions Limited Consulting http://www.nedproductions.biz/ http://ie.linkedin.com/in/nialldouglas/
On 12 July 2014 12:40, Niall Douglas <s_sourceforge@nedprod.com> wrote:
On 12 Jul 2014 at 10:20, Daniel James wrote:
I have been lazy as it's test code, and implemented a move assignment like this for an internal item_type due to value_type.first being const:
I move the nodes over when possible (when the allocators propagate on move or are equal), but copy the key otherwise.
How can you copy the key if it's const? It seemed to me, unless I have missed something, that I need to do an allocator destroy followed by at least an allocator copy construct as the key is const?
That's what I do. I've no idea what other implementations do.
On 12 July 2014 10:20, Daniel James <dnljms@gmail.com> wrote:
On 12 July 2014 01:49, Niall Douglas <s_sourceforge@nedprod.com> wrote:
On 11 Jul 2014 at 21:06, Daniel James wrote:
My understanding is that the allocators have to be used for all dynamic allocation. If this is not explicitly worded in the standard, then it should be. Otherwise allocators simply are not as useful with any other interpretation.
I assume by allocators you mean specifically STL allocators?
I looked this up earlier, as I thought the same. 23.2.1.7 says:
"Unless otherwise specified, all containers defined in this clause obtain memory using an allocator (see 17.6.3.5)."
Which suggests to me that all allocation is done using the allocator. I suppose debug info could be considered something that isn't obtained by the container, as it can be external, but I wouldn't have thought that for things like buckets.
The standard definitely means an STL allocator, not simply "an allocator"?
It refers to 17.6.3.5 which is the standard allocator requirements.
Yes, that is pretty clear IMHO. The standard talks about two very distinct things, "allocation functions" and "allocators", the latter being what Niall is referring to as STL allocators.
I've just been re-reading the standard, and it's subtle. The quote about 'allocate' is for 'containers defined in this clause', the quote for 'construct' is for 'components affected by this subclause', the former could be just about the existing standard containers, the latter about all containers that want to meet the standard requirements. The allocator aware container requirements and general/reversible/etc. container requirements don't mention anything about allocation. So, if I'm reading it correctly, std::unordered_set, std::vector, etc. have to always use 'allocate' to allocate memory, but other containers that want to meet the 'allocator aware' requirements don't have to use 'allocate' at all, just 'construct', which I find surprising. It's possible that I've missed something, or that it's an oversight. It might be worth asking on a standards list.
I've tried to clarify a few things with http://cplusplus.github.io/LWG/lwg-active.html#2261 and http://cplusplus.github.io/LWG/lwg-active.html#2218 but I didn't notice that paragraph 8 only applies to the standard containers, not to user-defined ones trying to meet the requirements.
Although, if a container is designed to be standardized, the designer will probably have to justify using allocators and not requiring 'allocate'.
I have been lazy as it's test code, and implemented a move assignment like this for an internal item_type due to value_type.first being const:
I move the nodes over when possible (when the allocators propagate on move or are equal), but copy the key otherwise.
I want to make it possible for maps to store the elements in a union of pair<Key, T> and pair<const Key, T> but there are a few changes needed for that to work, including either forbidding user specializations of std::pair or just requiring that pair<const A, B> must be layout compatible with pair<A, B>, so users who specialize one may need to specialize the other.
On 24 July 2014 18:31, Jonathan Wakely <jwakely.boost@kayari.org> wrote:
I want to make it possible for maps to store the elements in a union of pair<Key, T> and pair<const Key, T> but there are a few changes needed for that to work, including either forbidding user specializations of std::pair
+1. I'd strongly support that change. Good luck! -- Nevin ":-)" Liber <mailto:nevin@eviloverlord.com> (847) 691-1404
Daniel James skrev 2014-07-11 22:06:
On 11 July 2014 20:49, Glen Fernandes <glen.fernandes@gmail.com> wrote:
On Fri, Jul 11, 2014 at 12:18 PM, Felipe Magno de Almeida <felipe.m.almeida@gmail.com> wrote:
This looks very arbitrary to me. Is there any wording in the standard that supports that allocators have to be used only for allocation of value_type (or classes that has a value_type in it)?
My understanding is that the allocators have to be used for all dynamic allocation. If this is not explicitly worded in the standard, then it should be. Otherwise allocators simply are not as useful with any other interpretation.
I looked this up earlier, as I thought the same. 23.2.1.7 says:
"Unless otherwise specified, all containers defined in this clause obtain memory using an allocator (see 17.6.3.5)."
Which suggests to me that all allocation is done using the allocator. I suppose debug info could be considered something that isn't obtained by the container, as it can be external, but I wouldn't have thought that for things like buckets.
Well, it says "obtain memory", not "obtain *all* memory". :-)
23.2.1.3 says:
"For the components affected by this subclause that declare an allocator_type, objects stored in these components shall be constructed using the allocator_traits<allocator_type>::construct function and destroyed using the allocator_traits<allocator_type>::destroy function (20.7.8.2). These functions are called only for the container’s element type, not for internal types used by the container."
But that's specifying construction, not allocation (btw. I get this wrong in unordered, I'll fix it soon).
Right. At least one implementation used construct and destroy on internal pointers, like node pointers in std::list. This section clarifies that they should not do that. Bo Persson
On 30/06/2014 21:09, Niall Douglas wrote: > I think the wicked hard problem with ASIO will be deciding what > subset of it to standardise. For example, I would wonder if strands > or coroutines or any of the Windows support would make it. It seems to me that unless you either: - make it easy to extend the set of 'devices' it can talk to or - standardise a cross-platform 'device' concept that allows straightforward extension (both of which are hard) then it shouldn't be in the standard at all. I certainly wouldn't welcome something that didn't work well on Windows, and last time I tried to integrate a different sort of stream, I gave up in a maze of twisty little templates. It might be possible to enumerate a bigger 'world view' of devices to include files, named and anonymous pipes, serial ports, subprocesses (if not simply done with pipes), shared memory queues, general signalling semaphores and so on - but even that is painful. Not least, you can't integrate all of these onto a single event manager on any platform (eg on *nix, sysv shm queues etc, AIO events and semaphores don't mix well, and IOCP doesn't work with all handle types on Windows either). Its not clear how to handle interaction with devices that have a separate async error channel (whether OOB data, or a pipe to a subprocess stderr, say) despite the subprocess case being rather common in real use. It also seemed to me to be taking template use out of its comfort zone. I'm sure many of us have written C++ IO wrappers many times over the last 20 years or so, and they were probably structured with C++03 facilities and rather limited use of templates. Why that should so so unfashionable beats me if we end up with code as 'maintainable' as ASIO. That's a view that applies to plenty of Boost (and its why I'd generally choose Poco when possible) but in this case 'more so' because arguments about having the compiler remove overheads are somewhat weaker in the presence of IO operations. James
On 2/07/2014 17:20, james wrote: > On 30/06/2014 21:09, Niall Douglas wrote: >> I think the wicked hard problem with ASIO will be deciding what >> subset of it to standardise. For example, I would wonder if strands >> or coroutines or any of the Windows support would make it. > It seems to me that unless you either: > - make it easy to extend the set of 'devices' it can talk to > or > - standardise a cross-platform 'device' concept that allows > straightforward extension > (both of which are hard) then it shouldn't be in the standard at all. It doesn't actually require all that much code (lots of little pieces, mostly) to implement a different type of stream (I've done it a few times), although it's true that several parts are unobvious and it could probably do with an example. (The "Services" example is close but not quite the same thing.) Where it's a little more annoying is that the implementation details do a lot of accessing of *other* implementation details (presumably for performance reasons), which both makes them a bad place to look for examples of how to do it properly from "outside" of ASIO, and when you want something almost-but-not-quite like one of the existing classes, particularly when they have an internal base class that would have actually helped (but isn't safe to rely on from outside, of course). But you get that with many libraries. > Not least, you can't integrate all of these onto a single event > manager on any platform (eg on *nix, sysv shm queues etc, AIO events > and semaphores don't mix well, and IOCP doesn't work with all handle > types on Windows either). It's possible to integrate (almost?) anything, but sometimes it carries a management or performance cost (eg. something that provides a synchronous-only custom API can be integrated into ASIO and made asynchronous, at the cost of adding an internal worker thread to call that API for each in-progress operation on that type of object; note that it's not just the cost of the thread, but also will require a double wakeup to service the completion, which adds latency). Completions can be posted from one service to another in a similar (but more elegant, if not identical) manner to the classic self-pipe trick.
On 2 Jul 2014 at 6:20, james wrote: > > I think the wicked hard problem with ASIO will be deciding what > > subset of it to standardise. For example, I would wonder if strands > > or coroutines or any of the Windows support would make it. > It seems to me that unless you either: > - make it easy to extend the set of 'devices' it can talk to > or > - standardise a cross-platform 'device' concept that allows > straightforward > extension > (both of which are hard) then it shouldn't be in the standard at all. > > I certainly wouldn't welcome something that didn't work well on Windows, and > last time I tried to integrate a different sort of stream, I gave up in > a maze of twisty > little templates. You must remember that from the perspective of ISO, the only engineering standards which exist are other ISO standards. That in this case equals POSIX. This is why I said that Windows support cannot be standardised and would have to be omitted. Similarly, the cross-platform device concept you mentioned is already defined by ISO. It is the device concept implemented by POSIX. It does not include IOCP or any such equivalent (unfortunately [1]). In this nothing would vary from the C++ 11 standard, or any other standard, all of which exclusively think in terms of POSIX. The only i/o concept is that of POSIX alone. [1]: I would REALLY super love if POSIX defined BSD kqueues as the one, true, santified method of doing i/o multiplexing and deprecated select(), poll(), epoll() and all others as being nothing more than wrappers around kqueues. And for the love of god Linux needs to adopt kqueues already :( > It might be possible to enumerate a bigger 'world view' of devices to > include > files, named and anonymous pipes, serial ports, subprocesses (if not simply > done with pipes), shared memory queues, general signalling semaphores and > so on - but even that is painful. Not least, you can't integrate all of > these onto > a single event manager on any platform (eg on *nix, sysv shm queues etc, > AIO events and semaphores don't mix well, and IOCP doesn't work with all > handle types on Windows either). ISO POSIX already specifies files, pipes, block devices, semaphores and all the other stuff you mentioned. Their support in an ASIO standardisation is straightforward. The POSIX way of doing async file i/o is the aio_* POSIX API, and of course POSIX specifies file locking too. Both the aio_* and the file locking specifications do not work at all well with threading and would be useless to ASIO. I, obviously enough, would like AFIO into the C++ 17 standard and with that we get a new standard portable API for async file i/o and locking which is ASIO compatible. But unless there is a sudden and massive uptick in the use of AFIO, it won't be popular enough to become eligible for standardisation. > Its not clear how to handle interaction with devices that have a > separate async error channel (whether OOB data, or a pipe to a subprocess > stderr, say) despite the subprocess case being rather common in real use. I completely agree on this sentiment. Niall -- ned Productions Limited Consulting http://www.nedproductions.biz/ http://ie.linkedin.com/in/nialldouglas/
On 02/07/2014 14:17, Niall Douglas wrote:
this case equals POSIX. This is why I said that Windows support cannot be standardised and would have to be omitted. This is rather like saying the usefulness of the thing would have to be omitted, at least as a cross-platform solution. And if its *NIX only, I'd just use libev anyway. AIO is a hard sell on *NIX given the micro-optimisations that have been applied historically.
Standards should help us do our jobs, and not the other way around. If the standardisation process doesn't address portability to such a major platform then its arguably failed from the get-go.
On Wed, Jul 2, 2014 at 9:17 AM, Niall Douglas <s_sourceforge@nedprod.com> wrote: > On 2 Jul 2014 at 6:20, james wrote: > > > > I think the wicked hard problem with ASIO will be deciding what > > > subset of it to standardise. For example, I would wonder if strands > > > or coroutines or any of the Windows support would make it. > > It seems to me that unless you either: > > - make it easy to extend the set of 'devices' it can talk to > > or > > - standardise a cross-platform 'device' concept that allows > > straightforward > > extension > > (both of which are hard) then it shouldn't be in the standard at all. > > > > I certainly wouldn't welcome something that didn't work well on Windows, > and > > last time I tried to integrate a different sort of stream, I gave up in > > a maze of twisty > > little templates. > > You must remember that from the perspective of ISO, the only > engineering standards which exist are other ISO standards. That in > this case equals POSIX. This is why I said that Windows support > cannot be standardised and would have to be omitted. > pletely agree on this sentiment. > You have been misinformed. ISO rules are somewhat different for dealing with documents that are not ISO standards, but mostly that is just a matter of careful drafting. For example, see the ISO/IEC Directives, Part 2, Rules for the structure and drafting of International Standards, section 6.6.3 Use of trade names and trademarks. The C++ standard and its TRs and TSes support Windows, as well as a lot of other operating systems, and that is one of the criteria for evaluating proposals to the committee. Any proposal that cannot support a common operating system would likely be dead on arrival. Look at the Filesystem TS for an example. It mentions Windows and several other operating systems by name, and has a compliance section that explains how that TS copes with differences between platforms. --Beman
On 2 Jul 2014 at 17:59, Beman Dawes wrote:
You must remember that from the perspective of ISO, the only engineering standards which exist are other ISO standards. That in this case equals POSIX. This is why I said that Windows support cannot be standardised and would have to be omitted. pletely agree on this sentiment.
You have been misinformed.
I believe I used loose language. But I am not misinformed. For your reference, I used to serve as the ISO SC22 mirror convenor for Ireland, though you have many more years of ISO experience than I do.
ISO rules are somewhat different for dealing with documents that are not ISO standards, but mostly that is just a matter of careful drafting. For example, see the ISO/IEC Directives, Part 2, Rules for the structure and drafting of International Standards, section 6.6.3 Use of trade names and trademarks.
The C++ standard and its TRs and TSes support Windows, as well as a lot of other operating systems, and that is one of the criteria for evaluating proposals to the committee. Any proposal that cannot support a common operating system would likely be dead on arrival.
I'll come back to this at the end.
Look at the Filesystem TS for an example. It mentions Windows and several other operating systems by name, and has a compliance section that explains how that TS copes with differences between platforms.
As ISO would call it, it's all about "normative references" which can only be to other ISO documents (source: http://www.iec.ch/standardsdev/resources/draftingpublications/writing_ editing/directives/normative_references.htm). As POSIX is an ISO standard, it can be normatively referenced. Everything else goes into a Bibliography. What this turns into is that you can have an informative section which explains similarities and differences between the standard and implementations and other non-ISO standards, but which forms no part of the standard itself. You can also claim that there is no substantive difference between the standard and some named implementation(s), but again this is informative. While you *could* standardise a non-normatively referenced item aka some proprietary technology, you would receive a lot of heat from ISO and the potential for JTC1 to reject it at a plenary session which would be deeply embarrassing. The reason why is because there is a formal process for proprietary technologies to become standardised which involves patent agreements etc. There are also other ISO working groups to consider, so if a WG21 Networking TS standardised the Windows IOCP model I could rightly see the Austin Working Group going bananas over the potential consequences on POSIX, never mind the ADA WG, or even the Ruby WG. Let's apply this to the Filesystem TS. Windows provides most of a semantic equivalence to POSIX in filesystem, so file handles, reading and writing, file paths referencing a universal file system namespace (apart from a drive letter, differences in allowed characters and the slash being opposite they're very semantically close, much closer than say file paths on DEC VMS), ability to use unicode (albeit in different binary formats), they all map onto the same thing, and these are what Filesystem standardises. This is because this is the common subset of Windows *to* POSIX, not *with* POSIX. Let's briefly cover what Filesystem does not currently standardise. Memory mapping files on Windows is similar to POSIX, though with a nasty gotcha difference [1]. Symbolic links are also similar, though also with a nasty gotcha difference [2]. The Win32 API doesn't expose an atomic stat() call, even though the NT kernel does, so metadata can be racy which is another nasty gotcha. Off the top of my head, the only substantial divergence of Windows from POSIX is security and access permissions [3], and of course file locking where the Windows implementation is not stupidly broken. So, in the Filesystem TS, you can standardise an API which if applications use will produce identical effects on all systems (apart from access perms on Windows [see below]), and you can claim this legitimately as it is true (sans bugs of course) for the subset of POSIX-equivalent Filesystem functionality you have standardised. As your informative, you can point out minor differences about platform specifics and deviations from POSIX, and all is good. But let us be very clear here, the "gold standard" aimed at is POSIX. If Windows provides a feature not provided by POSIX - and in Filesystem there are many e.g. async file i/o - you cannot standardise it in an ISO TS without significant political consequences. If you look at access perms, note the direction of travel: the Filesystem TS can standardise a POSIX feature even though it isn't available on Windows and that's permitted. The opposite is not permitted.
The C++ standard and its TRs and TSes support Windows, as well as a lot of other operating systems, and that is one of the criteria for evaluating proposals to the committee. Any proposal that cannot support a common operating system would likely be dead on arrival.
And here we arrive onto the rub of the matter. Windows originally borrowed the 4.4 BSD network stack, and as a result has a closely similar socket API to POSIX - if used *synchronously*. Unfortunately, all i/o (with a few exceptions) on Windows is *always async* with the synchronous functions acting as wrappers of the underlying async implementation. Windows in fact pretends to be a micro-kernel and provides a 98% [4] async i/o API as if it were micro-kernel. Thus, on Windows, ASIO uses async sockets whilst on POSIX it uses non-blocking sockets. This produces a ton of semantic difference which is why there is such a plethora of POSIX-specific and Windows-specific class types in the ASIO reference page (http://www.boost.org/doc/libs/1_55_0/doc/html/boost_asio/reference.ht ml, bottom of page). Now, a Networking TS standardising ASIO can simply say "implementation defined" whenever these async vs. non-blocking semantic differences turn up, and I suspect that is exactly what will happen because all the alternatives are harder. This implies that Windows-specific support aka the whole true async socket semantic model aka cool stuff like IOCP (i/o completion ports) cannot be standardised. What will result in the Networking TS will still be useful for some C++ network applications, but I suspect that real C++ network applications are going to remain on ASIO because it lets you code around the semantic differences. Does this explain my original claim? I am not claiming that a standardised ASIO in a Networking TS would not function on Windows. I *am* claiming that so much Windows-specific aka async-specific functionality would have to be left unspecified that no serious C++ networking user would bother using the functionality in the TS. So what would I propose instead? What I personally think is needed here is that WG21 provide a canonical Networking TS implementation library rather than writing a specification and leaving it up to STL implementators. That canonical implementation would be a substantially rejigged and slimmed down all C++14 subset of ASIO [5]. This of course is very new territory - I am not aware of WG21 ever providing a canonical library implementation before. But WG21 has already been breaking new ground with letting library writers go direct to std::experimental and skip getting a new STL type into Boost first and later submit for standardisation after some experience is gained. I note the fact that activity in Boost dropped substantially around the same time WG21 allowed this, and that "all the action" in new library features appears to be happening around WG21 processes instead of via Boost. Personally I think this is the best way of standardising ASIO into a Networking TS. But I appreciate that such an approach is fraught with dangers, not least who will pay the engineers who write and test such a canonical library? What if there is substantial disagreement in the direction of refactor? What if real users just don't care, and keep using original ASIO? Still, it's better than every STL having a separate toy implementation of ASIO that is useless. [1]: munmap() will unmap all maps falling into the range supplied. UnmapViewOfFile() only unmaps the precise map given. This can be a nasty source of bugs. [2]: Derereferencing a link takes different semantics to POSIX unfortunately. It works similarly enough most won't notice, until they do. [3]: xattr ACL support on Linux and BSD looks similar enough to Windows ACLs that a common subset of functionality should be possible. Of course, POSIX famously demurred on standardising these in the 1990s, but ACLs are now pretty much standard in implementation across Linux, BSD and Windows. They are ripe for being formally standardised into POSIX. [4]: A glaring counterexample is that CreateFile() which is used to open file handles cannot be executed asynchronously. Neither can CloseHandle() nor FlushFileBuffers(). There are others, but these three are the worst offenders, especially as CreateFile() is incredibly slow (~30k ops/sec). [5]: ASIO could do with explicit support for micro-kernel operating systems, by which I mean you can completely dispense with threads on such systems. Something which annoyed me with the QNX port of ASIO is that it used the POSIX multiplexing API which was woefully inefficient when all syscalls in a micro-kernel OS can be multiplexed at source, and therefore the ASIO io_service could be very tightly integrated into the core QNX message passing system. Such an adaptation I think would not require much change to ASIO. Niall -- ned Productions Limited Consulting http://www.nedproductions.biz/ http://ie.linkedin.com/in/nialldouglas/
On Mon, Jun 30, 2014 at 10:09 PM, Niall Douglas <s_sourceforge@nedprod.com> wrote:
There is also a strong argument that anything in ASIO which isn't async needs to go.
I was trying to discuss the same idea and met heavy resistance. Still an interesting read in that regard might be this bug: https://svn.boost.org/trac/boost/ticket/2832 Consider the age and duration of this still unsolved matter. Cheers, Stephan
On 2 Jul 2014 at 10:48, Stephan Menzel wrote:
There is also a strong argument that anything in ASIO which isn't async needs to go.
I was trying to discuss the same idea and met heavy resistance. Still an interesting read in that regard might be this bug: https://svn.boost.org/trac/boost/ticket/2832
Consider the age and duration of this still unsolved matter.
My only disagreement with Chris's position on that ticket is that in my opinion he is unwise to keep any synchronous APIs in ASIO, because he's going to get never ending hassle about them. If he made ASIO all 100% async, that hassle goes away. In AFIO I have exactly one place where there is a synchronous API which could block. I don't like it, but it's extremely convenient so I've kept it (and it very rarely blocks). I'll probably regret that decision in the future if AFIO ever became popular :( Niall -- ned Productions Limited Consulting http://www.nedproductions.biz/ http://ie.linkedin.com/in/nialldouglas/
There is also a strong argument that anything in ASIO which isn't async needs to go.
Honestly, I think that the sync io could actually pave the way for replacing the iostreams mess. I have actually used file io built on top of asio's sync stream concepts. It actually works very nicely. I was actually able to read directly from files using `boost::asio::read`. The only thing missing was a Seekable concept for streams that can seek and report their position, which is important for implementing buffering. -- View this message in context: http://boost.2283326.n4.nabble.com/C-committee-meeting-report-tp4664412p4664... Sent from the Boost - Dev mailing list archive at Nabble.com.
On 3 Jul 2014 at 7:40, pfultz2 wrote:
There is also a strong argument that anything in ASIO which isn't async needs to go.
Honestly, I think that the sync io could actually pave the way for replacing the iostreams mess. I have actually used file io built on top of asio's sync stream concepts. It actually works very nicely. I was actually able to read directly from files using `boost::asio::read`. The only thing missing was a Seekable concept for streams that can seek and report their position, which is important for implementing buffering.
You might have a look at AFIO too :) You can build non-blocking and synchronous i/o from a decent async i/o platform, as indeed QNX or the NT kernel have done. For standardisation purposes, I'd personally aim exclusively for async only and once that's satisfactory I'd build other abstractions on top. All that said, I agree that Boost.iostreams' fundamental design failure is that it wasn't built atop a 100% async i/o framework like ASIO. Which is a shame, as STL iostreams could do with being replaced. Niall -- ned Productions Limited Consulting http://www.nedproductions.biz/ http://ie.linkedin.com/in/nialldouglas/
On 07/04/2014 01:22 PM, Niall Douglas wrote:
All that said, I agree that Boost.iostreams' fundamental design failure is that it wasn't built atop a 100% async i/o framework like ASIO. Which is a shame, as STL iostreams could do with being replaced.
FYI, Gene Panov has submitted an async extension for Boost.Iostreams: http://lists.boost.org/Archives/boost/2013/10/206826.php I have been helping him getting the patch to follow Boost convensions, but perpetual lack of time has prevented me from finalizing it. The current works is at: https://github.com/breese/iostreams/tree/feature/async_stream/include/boost/...
On 6 Jul 2014 at 12:57, Bjorn Reese wrote:
All that said, I agree that Boost.iostreams' fundamental design failure is that it wasn't built atop a 100% async i/o framework like ASIO. Which is a shame, as STL iostreams could do with being replaced.
FYI, Gene Panov has submitted an async extension for Boost.Iostreams:
I remember this from last year. It seemed to me at the time (and now) a sort of std::async for iostreams implementation that pushed the formatting labour onto worker threads. If I am wrong on this, do say.
I have been helping him getting the patch to follow Boost convensions, but perpetual lack of time has prevented me from finalizing it. The current works is at:
https://github.com/breese/iostreams/tree/feature/async_stream/include/boost/...
As much as this might be worthy, what I had in mind was an iostreams that doesn't mind out of order data readiness, so if I schedule a gather read from offsets A, B, C and D in a file, and they complete in the order D, B, C, A with lumpy periods of time between completions, the iostreams framework gets on with processing instead of waiting around for all to complete. Such an iostreams framework may seem excessively complex, but it's latency optimal. Niall -- ned Productions Limited Consulting http://www.nedproductions.biz/ http://ie.linkedin.com/in/nialldouglas/
You might have a look at AFIO too :)
I might take a look at that, but overall I don't really need async io. However, I did need the stream to be `Seekable`, that is, it would let me read and set the position in the file. I don't think this works really well with async io. I don't see sync stream as a problem really. A default timeout could be a property of the stream, rather than adding it to the read function. Ideally, it would be nice if there were facades that would make an async stream -> sync stream, and a sync stream -> async stream. -- View this message in context: http://boost.2283326.n4.nabble.com/C-committee-meeting-report-tp4664412p4664... Sent from the Boost - Dev mailing list archive at Nabble.com.
On 8 Jul 2014 at 7:34, pfultz2 wrote:
You might have a look at AFIO too :)
I might take a look at that, but overall I don't really need async io. However, I did need the stream to be `Seekable`, that is, it would let me read and set the position in the file. I don't think this works really well with async io.
Async i/o has no concept of file position. You schedule read scatters and write gathers to some file offset. They complete when the conditions you have requested for the op are met. It is of course extremely easy to add a file position using an std::atomic<offset_t> :)
I don't see sync stream as a problem really. A default timeout could be a property of the stream, rather than adding it to the read function. Ideally, it would be nice if there were facades that would make an async stream -> sync stream, and a sync stream -> async stream.
Sure, if it meets your requirements and doesn't eat data, it's a good solution. Unlike network i/o, timeouts have no place in file i/o. A file operation will always either eventually complete, or eventually return an error. I have seen, during testing, a single read take over a minute to complete, so timing that out would be bad as cancelling scheduled i/o is unfortunately very non-portable (and can corrupt memory on Windows). Niall -- ned Productions Limited Consulting http://www.nedproductions.biz/ http://ie.linkedin.com/in/nialldouglas/
On Tue, Jul 8, 2014 at 10:34 AM, pfultz2 <pfultz2@yahoo.com> wrote:
Ideally, it would be nice if there were facades that would make an async stream -> sync stream, and a sync stream -> async stream.
For such flexibility, you're better off to make all fundamental I/O operations async. ASIO already has adapters that can block until any of its async I/O operation completes. Moreover, you can provide different adapters to block at varying levels of granularity: block the whole thread, block the calling fiber, block a coroutine. But how could you adapt a synchronous operation to be async? Your code will not regain control until the synchronous operation has completed. You have no chance to do anything clever in the meantime.
participants (27)
-
a.hagen-zanker@surrey.ac.uk
-
Agustín K-ballo Bergé
-
beet
-
Beman Dawes
-
Bjorn Reese
-
Bo Persson
-
Daniel James
-
Eric Niebler
-
Felipe Magno de Almeida
-
Gary Powell
-
Gavin Lambert
-
Glen Fernandes
-
Gottlob Frege
-
Greg Rubino
-
Ion Gaztañaga
-
james
-
Jeffrey Yasskin
-
Jonathan Wakely
-
Klaim - Joël Lamotte
-
Lee Clagett
-
Mats Taraldsvik
-
Nat Goodspeed
-
Neil Groves
-
Nevin Liber
-
Niall Douglas
-
pfultz2
-
Stephan Menzel