(2015-09-17, 13:40)takoi Wrote: Is that parallel? Your 6 rule example takes exactly twice as long as the 3 rule. I've barely looked at heimdall and don't have any in-depth knowledge of it but i imagine this is mainly going to be io bound. Looking at the source code, it looks single threaded, with some option for thread pooling. This isn't going to be very fast nor scale very well. It would have more potential if it addressed that, as there is absolutely no reason it should take anywhere near 1 day to import 10000 songs. That is poor design in the current system (or rather ignoring io work altogether)
First I'd like to begin with taking a step back and consider how much cpu do you actually want a background task to eat, even if you have 4 cores do you want all of them on the brink to get your data faster? I had absolutely no problem getting the python process to to 100% (i.e. being python GIL blocked) with the schedule paradigm, and I don't think it will be any problem scaling it beyond that if so desired.
Heimdall can use many mainloops, its written with a classical event loop design. It can run everything one task at a time, waiting for it to finish, or in parallel via thread pool or with minithreads whatever you'd wish. I opted for threadpool as it was easiest and showcases the design well enough.
Its essentially using the same event loop design driving web browsers or gtk apps afaik. Note that python is single threaded when interpreting, much the same as Javascript. Note that this is not necessarily a problem, nodejs is javascript based but massively parallellised even if the scripts are single threaded. The reason it works is because everything heavy takes place in c++, which does not lock the interprenter.
So to showcase, as you say most is io bound, and this is what takes most time to process even if it doesn't take much cpu. So basically you shove all io reading to either multiple threads (in c++) or a select thread. And they callback to python when they are ready, during this time python (and heimdall) can continue with smaller tasks as it wants.
So the important nuance between Heimdall scheduling and our scraper current scraper is that Heimdall can start scraping 100 songs, and have read id3 tags of those 100 songs before it fetches http resources. It can do this with one or more threads. Our current scraper system, afaik, takes one item at a time, so if you have 5 threads (I'm fairly certain our scraper is single threaded) it needs to finish these 5 before it can continue to the next 5. Heimdall can finish part of these 100 before continuing with heavier tasks.
There is basically two things needed for intermediate result and better async support, and none of these are really big ones to add.
- Let Task.run accept a callback or be allowed to return a promise. Then Heimdall could continue while c++ works in a completely different thread or process
- Engine.run needs an intermediate callback, this would let Heimdall callback when id3 are scanned, before starting the next schedule/execution
- Let Tasks return an estimated time so the scheduler lets faster tasks through first
I did trials on this when I worked on it, basically I did a for loop which looped through all my music files with engine.get. If you look at the logs you will notice it has id3 tags before it fetches http resources.
Basically what happens is (lets say we have single thread but you can think how it would work with more threads).
get 1 -> Heimdall schedules id3 task
get 2 -> Heimdall schedules id3 task
..
get N -> Heimdall schedules id3 task
Heimdall executes id3 on 1 -> Heimdall schedules HTTP Request for 1
Heimdall executes id3 on 2 -> Heimdall schedules HTTP Request for 2
...
Heimdall executes id3 on N -> Heimdall schedules HTTP Request for N
== Here we have id3 tags ready for all N to be used and should already have sent out intermediate results ==
Heimdall executes http requst for 1 -> Heimdall executes callback
Heimdall executes http requst for 2 -> Heimdall executes callback
...
Heimdall executes http requst for N -> Heimdall executes callback
== Now all files have all metadata and all files are executed ==
(2015-09-17, 18:11)garbear Wrote: @topfs2 what sort of threading model do you visualize? and should threads be managed by Heimdall or Kodi?
For maximum performance I think the most important part is let io stuff be properly async, which basically means IO does not stop GIL. So move Resource task to C++ essentially.
EDIT: And if this isn't enough we could move parts of the engine, and critical tasks to c++ (say id3) so they can execute without disturbing python. During GSoC I aimed for something portable and main focus was not necessarily maximum performance, but I don't think anything stops the main design from being extremely performant.