24 ms·
A Universal I/O Abstraction for C++ (2020)
- jcelerier 5y agoIn the end the overall design looks like a semi-reified asynchronous dataflow programming API based on coroutines. I think that it would be useful to consider the existing (very very very large) body of research on dataflow PL and APIs; e.g. check this paper from 1982 which talks about dataflow, coroutines, for handling file operations: - https://scholarship.claremont.edu/cgi/viewcontent.cgi?article=1285&context=hmc_fac_pub https://scholarship.claremont.edu/cgi/viewcontent.cgi?articl... More recent papers in the field: - https://hal.archives-ouvertes.fr/hal-02865894/document https://hal.archives-ouvertes.fr/hal-02865894/document - https://library.oapen.org/bitstream/handle/20.500.12657/37721/2020_Book_ProgrammingLanguagesAndSystems.pdf?sequence=1#page=408 https://library.oapen.org/bitstream/handle/20.500.12657/3772... and older general overviews of the field: - http://www.cs.ucf.edu/~dcm/Teaching/COT4810-Spring2011/Literature/DataFlowProgrammingLanguages.pdf http://www.cs.ucf.edu/~dcm/Teaching/COT4810-Spring2011/Liter... - http://210.47.10.86:8032/200009-3/65492.pdf#page=141 http://210.47.10.86:8032/200009-3/65492.pdf#page=141 My main concern with this design, even if the code looks quite neat, is that the actual dataflow graph is not reified, but exists "invisibly" as part of the program source through the way co_await calls are sequenced. This means that it is not possible to do as an user, a static analysis and scheduling of the tasks : it relies on the compiler to perform this. But, this problem is in my experience quite often very use-case-specific and ridden with informations compilers do not possess. Not having the way tasks are interdependent reified in an actual user-manipulable "graph" object means that users won't have the possibility, to, say, perform a topological sort or flow propagation on their async graph in order to extract information on it that could be necessary to perform a domain-specific optimization of said graph (for instance if we know that a set of tasks are dependent on each other in ways that aren't visible on the source code, but are known to the programmers, it would make sense to schedule them on a queue in their own thread).
- motakraxxer 5y agoCould you please show an example for it?
- jcelerier 5y agoIn c++ some examples would be RaftLib or cpp-taskflow
- gpderetta 5y agoHum, in principle you can do arbitrary compile time transformations to the datagraph before submitting it via metaprogramming. If you really want you could even convert the graph (fully or partially) to a runtime representation. The core of the proposals are not about specific classes or functions other than some default algorithm implementations. Instead they are mostly about defining concepts and protocols so that independent async domains can coexist and iteroperate in the same application. This is similar to the STL where the most important part are the various iterator concepts and the algorithm interfaces, not the actual algo implementations and the containers.
- jcelerier 5y ago> Hum, in principle you can do arbitrary compile time transformations to the datagraph before submitting it via metaprogramming. I would find that to be great, but I don't understand how. In e.g. while(i) { co_await sch.schedule(std::chrono::milliseconds(100)); co_await w.write(--i); } how can you get a meaningful "graph" object out of that, e.g. [wait 100] -> [write 9] -> [wait 100] -> [write 8] -> ... without actually "running" the code first to go through the co_await, which are the only way to acquire the information of what is to run ? for instance, let's say that I want to compute "statically" (not in the "at compile time" sense but in the "after knowing the inputs to the program and before actually running the scheduler") how many [write] calls there are to schedule (assuming no cycles / infinite loops).
- gpderetta 5y agoThe proposed async "framework" happens to work well with coroutines, but they are not required (and infact most of the papers on the topic deal with them only in passing). You can build the async graph as an expression template and if you want, each operation (including the while loop) can be expressed as a different type. I don't think coroutines provide (yet) enough introspection to break them down in the same way.
- jcelerier 5y agoI see, that solves it then. Great !
- Const-me 5y ago> if we know that a set of tasks are dependent on each other in ways that aren't visible on the source code, but are known to the programmers, it would make sense to schedule them on a queue in their own thread Why it would make sense? In my experience, dedicated threads only makes sense in very specific use cases. Like dealing with APIs which have thread affinity, or when you want OS to prioritize stuff. Generally, throwing tasks into a shared thread pool is the way to go.
- jcelerier 5y ago> In my experience, dedicated threads only makes sense in very specific use cases. Like dealing with APIs which have thread affinity, or when you want OS to prioritize stuff. well, yes, those are things that happen fairly often. e.g. there's a lot of work on optimizing code for various arm big.LITTLE things, where you have some algorithm that you want to make sure runs on the "big" cores.
- Const-me 5y agoCan’t you use two shared thread pools, with threads affinity set to the big and little parts of the CPU, respectively? I think for most use cases runtime scheduling is better than compile-time one. Because other processes sharing the hardware, and because tasks take unpredictable time, depends on almost everything including ambient temperature. Not to mention compilers don’t generally know count of cores, outside of a few niches like embedded, iOS and consoles games.
- ur-whale 5y ago> semi-reified asynchronous dataflow programming API based on coroutines Gesundheit!
- jbluepolarbear 5y agoThere’s only one C++ library I’ll use for I/O and networking and that’s POCO C++. I’ve used it in a lot of project over the last 10 years and I haven’t seen a better solution.
- synergy20 5y agoits open source version has Boost license which is fine, not sure what's the difference between this and its POCO Pro product, it's better than QT license model for sure, but worse than than those "here is _all_ the code, pay me if you need professional service but not the code"
- jbluepolarbear 5y agoI’ve never used the pro version, which I think is just more specialized features on top of the core framework.
- secondcoming 5y agoC++'s networking support is one of its greatest shortcomings. If I had the choice, I'd never use ASIO again and just use libuv. Coroutines look interesting, I just need to get my head around them. All code examples I've seen, just as with ASIO, deal with the happy path only. For example, there's hardly a mention of timeouts and the faff ASIO makes you do to get them to work. If there's no 'read(..., timeout)' function then the API is broken as far as I'm concerned. Then you'll (probably) need to write your own HTTP parsing code which, when you're half-way through, will make you realise that HTTP is actually not trivial at all to implement correctly. Best of luck writing your own HTTP2/3 engine. Boost.Beast seems like a great attempt at doing it right, but I've never used it. (HTTP needs to die, but that's a rant for another day) IMO, it's too late for C++ and networking. If you need to read from something simple like a serial device then fine, it's probably ok. If you actually need to talk across the Internet then there are other languages that will do it better than you can. Having said all this, personally, I know my limits and could never come up with anything better than ASIO.
- synergy20 5y agowhat about POCO libs mentioned above for network and IO applications?
- v8dev123 5y agoYou might love evpp instead ASIO. It's just pain to install it but once you do it's a heaven. As for HTTP parsing, llhttp does great job. It however lack multipart parsing.
- nly 5y agoBoost.Beast has its own HTTP parser[0], during the development of which Vinnie Falco (the principle author of Beast) found many bugs/inconsistencies in Node.js's own parser[1]. Personally I'd rather use this than something based on Node.js http-parser. [0] https://www.boost.org/doc/libs/develop/libs/beast/doc/html/beast/ref/boost__beast__http__parser.html https://www.boost.org/doc/libs/develop/libs/beast/doc/html/b... [1] https://github.com/nodejs/http-parser/issues?q=is%3Aissue+author%3Avinniefalco+ https://github.com/nodejs/http-parser/issues?q=is%3Aissue+au...
- jfrd 5y agoToo bad C++ is the most terrible thing since forever. https://twitter.com/Cor3ntin/status/1383875999033565194?s=20 https://twitter.com/Cor3ntin/status/1383875999033565194?s=20
- pjmlp 5y agoNah, that place I leave for C, the number one reason we keep having memory corruption issues during the last 50 years of computer history, and having taited every language that has some level of compatibility with it.
- linkdd 5y agoThis kind of comments are forgetting all the good that C did bring in the last 50 years of computer history. <sarcasm>Too bad those shitty engineers in the 70s didn't think of Rust...</sarcasm>
- miohtama 5y agoThey thought many ideas of it (see Lisp, Smalltalk) but computers where not powerful enough for complex compilers. Instead, the burden of ensuring correctness had to be left as the cognitive burden for the developer for decades to come.
- MaxBarraclough 5y ago> They thought many ideas of it (see Lisp, Smalltalk) but computers where not powerful enough for complex compilers. That's probably true for borrower-checking specifically, but I don't think it accounts for all language-design breakthroughs. There were decades between the development of functional programming languages, and widespread hybrid languages which incorporated those ideas (e.g. modern Python and C#).
- pjmlp 5y agoSee Burroughs B5500, created in 1961 with ESPOL/NEWP, 10 years before C was even an idea.
- bregma 5y agoI would hesitate to embrace a "universal" I/O abstraction that is architected around a monolithic kernel. Linux and Windows are not the only game in town. This design make make for a nice third-party library for some applications, but it has no place being cemented into the standard library.
- gpderetta 5y agoThe abstraction doesn't even require the existence of a kernel.
- flukus 5y agoBack when the "Law of Leaky Abstractions" was first coined such things were given as examples: https://www.joelonsoftware.com/2002/11/11/the-law-of-leaky-abstractions/ https://www.joelonsoftware.com/2002/11/11/the-law-of-leaky-a... > Even though network libraries like NFS and SMB let you treat files on remote machines “as if” they were local, sometimes the connection becomes very slow or goes down, and the file stops acting like it was local, and as a programmer you have to write code to deal with this. The abstraction of “remote file is the same as local file” leaks.
- KingOfCoders 5y agoAlways suspicious of IO abstractions. Worst contender is Java with Appendable append(CharSequence csq) throws IOException where Appendable is also used for a StringBuffer.