Hi,
Peterbjornx wrote:While I was pondering about an implementation of asynchronous I/O I came up with a ?new? way of dividing computing time, not bounded by large monolithic threads running for long periods of time but instead sequential tasks that take and yield information to other tasks on starting and completion, much like UNIX pipe programs but much smaller. A sequence of tasks would be specified via a dependency calculation, an interface program such as a shell would receive a request from the user to process information and determine what input is needed to generate the information, then invoke the task class for that input to generate the dependencies of that input (if any) which in turn generate their dependencies and when this tree has been built it will be run with each node running after its dependencies have become available.
That dependency calculation is likely to be impossible for non-trivial scenarios. For example, the user types "gcc" with some command line arguments in the shell. You can't the know the dependencies until after GCC has examined its command line arguments, and maybe GCC loads a file and (depending on the file's contents - e.g. "#include" lines) it may or may not load more files and examine their contents too.
Instead of trying to determine which other tasks will be needed in advance (which is impossible); why not decide which tasks are needed when they are actually sent data (which is trivial)? Of course this ends up being the same as "remote procedure call" - all tasks are waiting (either on disk waiting to be executed until they've been called, or in memory waiting to be called); and when they are called they stop waiting for a while and do something and then stop again (either terminating and going back to waiting to be executed, or staying in memory and going back to waiting to be called).
Peterbjornx wrote:This system would still require some long running threads for user interaction but would greatly simplify data flows and would (if implemented efficiently) greatly speed up data processing by only loading/starting tasks when they can actually run, eliminating wait states.
For a normal system; those wait states are actually flow control. One task might do "fetch data from disk; process it; send processed data to next task" in a loop to process a 20 TiB file; where it's constantly stopping due to flow control (because it can't get data from disk fast enough and has to wait). The second task has to wait for data to arrive from the first task (more flow control) but as soon as its next piece of data does arrive it can do something with it. Note that the first task and second task might be running in parallel at the same time (e.g. on different CPUs); and you don't need 20 TiB of RAM to process the 20 TiB file. This is how "command line pipes" work - e.g. if you pipe data from a file to "gunzip" to "wc" to a second file; then gunzip and wc can be running in parallel, and you don't wait until the entire original file is loaded before doing any decompressing, you don't wait until all data is decompressed before doing any word counting, etc). Even better is when you're able to use asychronous file IO; where the computer can be loading one piece of data from disk while decompressing the previous piece of data while counting words in the piece of data before that; all at the same time.
If you get rid of that and (e.g.) load the entire 20 TiB file into memory, then process the 20 TiB of data (e.g. decompress it so that now you've got 40 TiB of data in RAM), then send all of that data to the next task ("wc") and start processing it; then it will be extremely slow (massive amount of swapping to/from disk, nothing happening in parallel).
Basically; to greatly speed up data processing you need wait states; and by eliminating wait states you cripple performance.
Cheers,
Brendan