[poky] What's taking so long??
Richard Purdie
richard.purdie at linuxfoundation.org
Fri Jan 28 02:34:45 PST 2011
On Fri, 2011-01-28 at 15:42 +0800, Xu, Dongxiao wrote:
> Kevin and I spent some time investigating this logging issue, and
> finally we found its cause.
>
> The phenomenon is, if we write a task like:
>
> testlog.bb:
>
> python do_crazylog () {
> for i in range(500):
> bb.note("xdx: hello world.")
> }
> addtask crazylog
>
> And run the command: "time bitbake testlog -c crazylog -f", it needs
> about 68 seconds to print the 500 sentences, which is problematic.
>
> I used "bitbake -P" to do some profiling for this task, and found most
> time is costed by time.sleep(), and each time it will sleep 0.25
> seconds.
>
> Thu Jan 27 16:11:59 2011 profile.log
>
> 922167 function calls (892080 primitive calls) in 69.882 CPU
> seconds
>
> Ordered by: cumulative time
>
> ncalls tottime percall cumtime percall
> filename:lineno(function)
> 1 0.012 0.012 69.887
> 69.887 /home/dongxiao/poky/bitbake/lib/bb/ui/knotty.py:67(main)
> 1391 0.005 0.000 69.829
> 0.050 /home/dongxiao/poky/bitbake/lib/bb/server/none.py:95(waitEvent)
> 1044 0.005 0.000 69.818
> 0.067 /home/dongxiao/poky/bitbake/lib/bb/server/none.py:121(idle_commands)
> 265 66.350 0.250 66.350 0.250 {time.sleep}
> 780 0.001 0.000 2.143
> 0.003 /home/dongxiao/poky/bitbake/lib/bb/cooker.py:156(runCommands)
> 780 0.003 0.000 2.142
> 0.003 /home/dongxiao/poky/bitbake/lib/bb/command.py:91(runAsyncCommand)
> 780 0.002 0.000 1.935
> 0.002 /home/dongxiao/poky/bitbake/lib/bb/cooker.py:738(updateCache)
>
>
> I bisected the git log and found that the commit introduced this issue
> is a merge from openembedded.
>
> After this commit, the whole LogRecord object into eventQueue, which
> contains more information than only level and messages.
>
> Commit: d3a45c7d41a88d79389fc40eb68816e4939fb6f9.
>
> Use logging in the knotty ui, and pass the log record across
> directly
>
> This kills firing of Msg* events in favor of just passing along
> LogRecord
> objects. These objects hold more than just level and message, but
> can also
> have exception information, so the UI can decide what to do with
> that.
>
> As an aside, when using the 'none' server, this results in the log
> messages in
> the server being displayed directly via the logging module and the
> UI's
> handler, rather than going through the server's event queue. As a
> result of
> doing it this way, we have to override the event handlers of the
> base logger
> when spawning a worker process, to ensure they log via events
> rather than
> directly.
>
>
> Based on our analysis of the current code, the main steps for logging
> is: (knotty ui and none server)
>
> 1) Each time when bitbake server fork off a task in execute(), it will
> create pipe between server and forked task.
>
> 2) Server will read the pipe to get "events", and then append it to
> the server eventQueue. Each time it will read 1024 bytes.
>
> 3) The UI module (knotty in our case) will wait on getting the event
> from eventQueue of "none" server, if there are events exist, it will
> handle the event. Otherwise, it will sleep 0.25 seconds and see there
> is any event.
>
>
> Previsouly before the patch, we only put message and level information
> into eventQueue, thus each time in read(), we can read a bunch of
> messages in above case (about 10 "xdx: hello world.").
>
> However, after passing the whole object, 1024 bytes could only cover
> 1, 2 or even zero LogRecord objects. Therefore the server is always
> "starve" to get event from the pipe, and thus the eventQueue is empty
> in most cases, which makes knotty ui sleep.
>
> One of the straightforward idea is to change 1024 to be a bigger
> number, for example, 10240. See following patch.
>
> diff --git a/bitbake/lib/bb/runqueue.py b/bitbake/lib/bb/runqueue.py
> index b9d89ec..196b957 100644
> --- a/bitbake/lib/bb/runqueue.py
> +++ b/bitbake/lib/bb/runqueue.py
> @@ -1637,7 +1637,7 @@ class runQueuePipe():
> def read(self):
> start = len(self.queue)
> try:
> - self.queue = self.queue + self.input.read(1024)
> + self.queue = self.queue + self.input.read(10240)
> except (OSError, IOError):
> pass
> end = len(self.queue)
>
> I retested the above case, and it can get a much better results:
>
> After patch:
> real 0m9.994s
> user 0m3.410s
> sys 0m0.390s
>
> Before patch:
> real 1m8.614s
> user 0m3.460s
> sys 0m0.320s
>
> This is a bottle neck for those cases which have a loop of "bb.note",
> like Gary found in the package bbclass for kernel recipes.
>
> But for normal "bb.note", it may not benefit a lot. So this logging
> issue may be not a key point to the current long building time.
>
> But anyhow, I am scheduling a test for this patch to see whether it
> helps for our common build result, like poky-image-minimal, etc.
>
> Comments and thoughts on it?
Great work in tracking that down. I've merged a version into master but
I did increase the read value by another factor of 10 as my local tests
showed it to be beneficial. I was nearly tempted to remove the size all
together but didn't for the reasons I've outlined in the commit (task
scheduling fairness and the risk of a crazy task blocking the server
main loop).
I think this could well have a significant speed effect on things in
general, we'll soon find out I guess! :)
Cheers,
Richard
More information about the poky
mailing list