diff mbox

main-loop: For tools, initialize timers as part of qemu_init_main_loop()

Message ID 1327166033-17922-1-git-send-email-mdroth@linux.vnet.ibm.com
State New
Headers show

Commit Message

Michael Roth Jan. 21, 2012, 5:13 p.m. UTC
In some cases initializing the alarm timers can lead to non-negligable
overhead from programs that link against qemu-tool.o. At least,
setting a max-resolution WinMM alarm timer via mm_start_timer() (the
current default for Windows) can increase the "tick rate" on Windows
OSs and affect frequency scaling, and in the case of tools that run
in guest OSs such has qemu-ga, the impact can be fairly dramatic
(+20%/20% user/sys time on a core 2 processor was observed from an idle
Windows XP guest).

This patch doesn't address the issue directly (not sure what a good
solution would be for Windows, or what other situations it might be
noticeable), but it at least limits the scope of the issue to programs
that "opt-in" to using the main-loop.c functions by only enabling alarm
timers when qemu_init_main_loop() is called, which is already required
to make use of those facilities, so existing users shouldn't be
affected.

Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
---
 main-loop.c |    2 +-
 main-loop.h |   12 ++++++++++++
 qemu-tool.c |    3 ++-
 vl.c        |    5 +++++
 4 files changed, 20 insertions(+), 2 deletions(-)

Comments

Jamie Lokier Jan. 21, 2012, 8:39 p.m. UTC | #1
Michael Roth wrote:
> In some cases initializing the alarm timers can lead to non-negligable
> overhead from programs that link against qemu-tool.o. At least,
> setting a max-resolution WinMM alarm timer via mm_start_timer() (the
> current default for Windows) can increase the "tick rate" on Windows
> OSs and affect frequency scaling, and in the case of tools that run
> in guest OSs such has qemu-ga, the impact can be fairly dramatic
> (+20%/20% user/sys time on a core 2 processor was observed from an idle
> Windows XP guest).
> 
> This patch doesn't address the issue directly (not sure what a good
> solution would be for Windows, or what other situations it might be
> noticeable),

Is this a timer that need to fire soon after setting, every time?

I wonder if a different kind of Windows timer, lower-resolution, could
be used if the timeout is longer.  If it has insufficient resolution,
it could be set to trigger a little early, then set a high-resolution
timer at that point.

Maybe that could help for Linux CONFIG_NOHZ guests?

-- Jamie
Paolo Bonzini Jan. 22, 2012, 12:32 p.m. UTC | #2
On 01/21/2012 09:39 PM, Jamie Lokier wrote:
> Is this a timer that need to fire soon after setting, every time?
>
> I wonder if a different kind of Windows timer, lower-resolution, could
> be used if the timeout is longer.  If it has insufficient resolution,
> it could be set to trigger a little early, then set a high-resolution
> timer at that point.
>
> Maybe that could help for Linux CONFIG_NOHZ guests?

No, it's an implementation detail of Windows multimedia timers.  Just 
enabling them apparently burns CPU.

There is another kind of timers for Windows but it didn't work reliably. 
  Finding out why would be the right fix, but anyway

Reviewed-by: Paolo Bonzini <pbonzini@redhat.com>

Paolo
Michael Roth Jan. 23, 2012, 12:12 a.m. UTC | #3
On 01/22/2012 06:32 AM, Paolo Bonzini wrote:
> On 01/21/2012 09:39 PM, Jamie Lokier wrote:
>> Is this a timer that need to fire soon after setting, every time?
>>
>> I wonder if a different kind of Windows timer, lower-resolution, could
>> be used if the timeout is longer. If it has insufficient resolution,
>> it could be set to trigger a little early, then set a high-resolution
>> timer at that point.
>>
>> Maybe that could help for Linux CONFIG_NOHZ guests?
>
> No, it's an implementation detail of Windows multimedia timers. Just
> enabling them apparently burns CPU.
>
> There is another kind of timers for Windows but it didn't work reliably.
> Finding out why would be the right fix, but anyway

I'd looked into timer queues, which the developer docs suggested had 
deprecated the use of mm timers, but I came across this which I figured 
was why mm was preferred (%30 average error for a 50ms periodic/oneshot) 
by the QEMU code:

http://www.virtualdub.org/blog/pivot/entry.php?id=272

Apparently Windows 7 has some fixes for drift that makes them reasonable 
for a clock source, but still fairly low-res for an event timer.

I suppose we could use some historical timer data to adaptively 
determine a reasonable minimum resolution to set...basically if the last 
timer fired X ns ago, we'd configure the resolution to be 1/10th of that 
periodic or whatever, and start them off low-res as James suggested to 
avoid unnecessary CPU burn, but it's a pretty loose heuristic.

>
> Reviewed-by: Paolo Bonzini <pbonzini@redhat.com>
>
> Paolo
>
>
Paolo Bonzini Jan. 23, 2012, 7:49 a.m. UTC | #4
On 01/23/2012 01:12 AM, Michael Roth wrote:
> I'd looked into timer queues, which the developer docs suggested had
> deprecated the use of mm timers, but I came across this which I figured
> was why mm was preferred (%30 average error for a 50ms periodic/oneshot)
> by the QEMU code:
>
> http://www.virtualdub.org/blog/pivot/entry.php?id=272
>
> Apparently Windows 7 has some fixes for drift that makes them reasonable
> for a clock source, but still fairly low-res for an event timer.

On the other hand, timeBeginPeriod affect basically all time sources, 
not just MM timers.  Using timer queues together with timeBeginPeriod 
might work, after all.  However, it's not too interesting since 
timeBeginPeriod is what causes the additional CPU usage.

What is interesting is to move timeBeginPeriod/timeEndPeriod from 
qemu-timer.c elsewhere so that only QEMU uses it.  The tools do not need 
precise timekeeping anyway.

But while that would be good to have later when more tools use the 
shared main loop, your patch is still good for now.

Paolo
Stefan Hajnoczi Jan. 27, 2012, 5:46 a.m. UTC | #5
On Sat, Jan 21, 2012 at 11:13:53AM -0600, Michael Roth wrote:
> In some cases initializing the alarm timers can lead to non-negligable
> overhead from programs that link against qemu-tool.o. At least,
> setting a max-resolution WinMM alarm timer via mm_start_timer() (the
> current default for Windows) can increase the "tick rate" on Windows
> OSs and affect frequency scaling, and in the case of tools that run
> in guest OSs such has qemu-ga, the impact can be fairly dramatic
> (+20%/20% user/sys time on a core 2 processor was observed from an idle
> Windows XP guest).
> 
> This patch doesn't address the issue directly (not sure what a good
> solution would be for Windows, or what other situations it might be
> noticeable), but it at least limits the scope of the issue to programs
> that "opt-in" to using the main-loop.c functions by only enabling alarm
> timers when qemu_init_main_loop() is called, which is already required
> to make use of those facilities, so existing users shouldn't be
> affected.
> 
> Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
> ---
>  main-loop.c |    2 +-
>  main-loop.h |   12 ++++++++++++
>  qemu-tool.c |    3 ++-
>  vl.c        |    5 +++++
>  4 files changed, 20 insertions(+), 2 deletions(-)

static struct qemu_alarm_timer alarm_timers[] = {
#ifndef _WIN32
#ifdef __linux__
    {"dynticks", dynticks_start_timer,
     dynticks_stop_timer, dynticks_rearm_timer},
#endif
    {"unix", unix_start_timer, unix_stop_timer, unix_rearm_timer},
#else
    {"mmtimer", mm_start_timer, mm_stop_timer, mm_rearm_timer},
    {"dynticks", win32_start_timer, win32_stop_timer, win32_rearm_timer},
#endif

It seems Windows host already has a "dynticks" implementation.  Have you
tried using this instead of "mmtimer"?

mm_start_timer() is using 1 ms intervals!

Stefan
Stefan Hajnoczi Jan. 27, 2012, 5:46 a.m. UTC | #6
On Sat, Jan 21, 2012 at 11:13:53AM -0600, Michael Roth wrote:
> In some cases initializing the alarm timers can lead to non-negligable
> overhead from programs that link against qemu-tool.o. At least,
> setting a max-resolution WinMM alarm timer via mm_start_timer() (the
> current default for Windows) can increase the "tick rate" on Windows
> OSs and affect frequency scaling, and in the case of tools that run
> in guest OSs such has qemu-ga, the impact can be fairly dramatic
> (+20%/20% user/sys time on a core 2 processor was observed from an idle
> Windows XP guest).
> 
> This patch doesn't address the issue directly (not sure what a good
> solution would be for Windows, or what other situations it might be
> noticeable), but it at least limits the scope of the issue to programs
> that "opt-in" to using the main-loop.c functions by only enabling alarm
> timers when qemu_init_main_loop() is called, which is already required
> to make use of those facilities, so existing users shouldn't be
> affected.
> 
> Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
> ---
>  main-loop.c |    2 +-
>  main-loop.h |   12 ++++++++++++
>  qemu-tool.c |    3 ++-
>  vl.c        |    5 +++++
>  4 files changed, 20 insertions(+), 2 deletions(-)

BTW the reason I suggest making Windows timers work efficiently
("dynticks") is because qemu-tool might legitimately want to make use of
QEMU timers.

Stefan
Paolo Bonzini Jan. 27, 2012, 8:09 a.m. UTC | #7
On 01/27/2012 06:46 AM, Stefan Hajnoczi wrote:
> #ifndef _WIN32
> #ifdef __linux__
>      {"dynticks", dynticks_start_timer,
>       dynticks_stop_timer, dynticks_rearm_timer},
> #endif
>      {"unix", unix_start_timer, unix_stop_timer, unix_rearm_timer},
> #else
>      {"mmtimer", mm_start_timer, mm_stop_timer, mm_rearm_timer},
>      {"dynticks", win32_start_timer, win32_stop_timer, win32_rearm_timer},
> #endif
>
> It seems Windows host already has a "dynticks" implementation.  Have you
> tried using this instead of "mmtimer"?

The dynticks Win32 timer doesn't boot Linux successfully, even though 
under Wine it works and it is actually more reliable than mmtimer (which 
is why I haven't thrown it away yet).

> mm_start_timer() is using 1 ms intervals!

No, it's setting a 1 ms system quantum via timeBeginPeriod.  The actual 
implementation is using dynamic rather than periodic ticks (it has a 
rearm callback).  We threw away periodic timers a few months ago.

The problem is that Windows doesn't have something like Linux NOHZ and 
limits the timer resolution to the system quanta.  That's 1 ms for 
mmtimer and 10 ms (the default) for dynticks right now.  However, I 
suspect that the solution is to move timeBeginPeriod and timeEndPeriod 
from timer code to generic QEMU code, dynticks would start working on 
native Windows too.  Besides possibly fixing QEMU, it would definitely 
fix Michael's problem, too.  Tools do not need such a fine-grained 
timer, and shorter quanta cause the increased CPU usage that Michael 
observed.

However, Michael's patch makes sense as a cleanup anyway.  Since we have 
an initialization function, there's no need to have that _and_ a 
constructor.

Paolo
Anthony Liguori Feb. 1, 2012, 10:10 p.m. UTC | #8
On 01/21/2012 11:13 AM, Michael Roth wrote:
> In some cases initializing the alarm timers can lead to non-negligable
> overhead from programs that link against qemu-tool.o. At least,
> setting a max-resolution WinMM alarm timer via mm_start_timer() (the
> current default for Windows) can increase the "tick rate" on Windows
> OSs and affect frequency scaling, and in the case of tools that run
> in guest OSs such has qemu-ga, the impact can be fairly dramatic
> (+20%/20% user/sys time on a core 2 processor was observed from an idle
> Windows XP guest).
>
> This patch doesn't address the issue directly (not sure what a good
> solution would be for Windows, or what other situations it might be
> noticeable), but it at least limits the scope of the issue to programs
> that "opt-in" to using the main-loop.c functions by only enabling alarm
> timers when qemu_init_main_loop() is called, which is already required
> to make use of those facilities, so existing users shouldn't be
> affected.
>
> Signed-off-by: Michael Roth<mdroth@linux.vnet.ibm.com>

Applied.  Thanks.

Regards,

Anthony Liguori
> ---
>   main-loop.c |    2 +-
>   main-loop.h |   12 ++++++++++++
>   qemu-tool.c |    3 ++-
>   vl.c        |    5 +++++
>   4 files changed, 20 insertions(+), 2 deletions(-)
>
> diff --git a/main-loop.c b/main-loop.c
> index 62d95b9..db23de0 100644
> --- a/main-loop.c
> +++ b/main-loop.c
> @@ -199,7 +199,7 @@ static int qemu_signal_init(void)
>   }
>   #endif
>
> -int qemu_init_main_loop(void)
> +int main_loop_init(void)
>   {
>       int ret;
>
> diff --git a/main-loop.h b/main-loop.h
> index f971013..4987041 100644
> --- a/main-loop.h
> +++ b/main-loop.h
> @@ -41,10 +41,22 @@
>    * SIGUSR2, thread signals (SIGFPE, SIGILL, SIGSEGV, SIGBUS) and real-time
>    * signals if available.  Remember that Windows in practice does not have
>    * signals, though.
> + *
> + * In the case of QEMU tools, this will also start/initialize timers.
>    */
>   int qemu_init_main_loop(void);
>
>   /**
> + * main_loop_init: Initializes main loop
> + *
> + * Internal (but shared for compatibility reasons) initialization routine
> + * for the main loop. This should not be used by applications directly,
> + * use qemu_init_main_loop() instead.
> + *
> + */
> +int main_loop_init(void);
> +
> +/**
>    * main_loop_wait: Run one iteration of the main loop.
>    *
>    * If @nonblocking is true, poll for events, otherwise suspend until
> diff --git a/qemu-tool.c b/qemu-tool.c
> index 6b69668..183a583 100644
> --- a/qemu-tool.c
> +++ b/qemu-tool.c
> @@ -83,11 +83,12 @@ void qemu_clock_warp(QEMUClock *clock)
>   {
>   }
>
> -static void __attribute__((constructor)) init_main_loop(void)
> +int qemu_init_main_loop(void)
>   {
>       init_clocks();
>       init_timer_alarm();
>       qemu_clock_enable(vm_clock, false);
> +    return main_loop_init();
>   }
>
>   void slirp_select_fill(int *pnfds, fd_set *readfds,
> diff --git a/vl.c b/vl.c
> index ba55b35..74a47e6 100644
> --- a/vl.c
> +++ b/vl.c
> @@ -2136,6 +2136,11 @@ static void free_and_trace(gpointer mem)
>       free(mem);
>   }
>
> +int qemu_init_main_loop(void)
> +{
> +    return main_loop_init();
> +}
> +
>   int main(int argc, char **argv, char **envp)
>   {
>       const char *gdbstub_dev = NULL;
diff mbox

Patch

diff --git a/main-loop.c b/main-loop.c
index 62d95b9..db23de0 100644
--- a/main-loop.c
+++ b/main-loop.c
@@ -199,7 +199,7 @@  static int qemu_signal_init(void)
 }
 #endif
 
-int qemu_init_main_loop(void)
+int main_loop_init(void)
 {
     int ret;
 
diff --git a/main-loop.h b/main-loop.h
index f971013..4987041 100644
--- a/main-loop.h
+++ b/main-loop.h
@@ -41,10 +41,22 @@ 
  * SIGUSR2, thread signals (SIGFPE, SIGILL, SIGSEGV, SIGBUS) and real-time
  * signals if available.  Remember that Windows in practice does not have
  * signals, though.
+ *
+ * In the case of QEMU tools, this will also start/initialize timers.
  */
 int qemu_init_main_loop(void);
 
 /**
+ * main_loop_init: Initializes main loop
+ *
+ * Internal (but shared for compatibility reasons) initialization routine
+ * for the main loop. This should not be used by applications directly,
+ * use qemu_init_main_loop() instead.
+ *
+ */
+int main_loop_init(void);
+
+/**
  * main_loop_wait: Run one iteration of the main loop.
  *
  * If @nonblocking is true, poll for events, otherwise suspend until
diff --git a/qemu-tool.c b/qemu-tool.c
index 6b69668..183a583 100644
--- a/qemu-tool.c
+++ b/qemu-tool.c
@@ -83,11 +83,12 @@  void qemu_clock_warp(QEMUClock *clock)
 {
 }
 
-static void __attribute__((constructor)) init_main_loop(void)
+int qemu_init_main_loop(void)
 {
     init_clocks();
     init_timer_alarm();
     qemu_clock_enable(vm_clock, false);
+    return main_loop_init();
 }
 
 void slirp_select_fill(int *pnfds, fd_set *readfds,
diff --git a/vl.c b/vl.c
index ba55b35..74a47e6 100644
--- a/vl.c
+++ b/vl.c
@@ -2136,6 +2136,11 @@  static void free_and_trace(gpointer mem)
     free(mem);
 }
 
+int qemu_init_main_loop(void)
+{
+    return main_loop_init();
+}
+
 int main(int argc, char **argv, char **envp)
 {
     const char *gdbstub_dev = NULL;