Commit 93db1991 authored by Ian Lynagh's avatar Ian Lynagh
Browse files

new RTS flag: -V to modify the resolution of the RTS timer

Fixed version of an old patch by Simon Marlow. His description read:
 Also, now an arbitrarily short context switch interval may now be
 specified, as we increase the RTS ticker's resolution to match the
 requested context switch interval.  This also applies to +RTS -i (heap
 profiling) and +RTS -I (the idle GC timer).  +RTS -V is actually only
 required for increasing the resolution of the profile timer.
parent c18587da
...@@ -85,6 +85,34 @@ ...@@ -85,6 +85,34 @@
</sect2> </sect2>
<sect2 id="rts-options-misc">
<title>Miscellaneous RTS options</title>
<variablelist>
<varlistentry>
<term><option>-V<replaceable>secs</replaceable></option></term>
<indexterm><primary><option>-V</option></primary><secondary>RTS
option</secondary></indexterm>
<listitem>
<para>Sets the interval that the RTS clock ticks at. The
runtime uses a single timer signal to count ticks; this timer
signal is used to control the context switch timer (<xref
linkend="sec-using-concurrent" />) and the heap profiling
timer <xref linkend="rts-options-heap-prof" />. Also, the
time profiler uses the RTS timer signal directly to record
time profiling samples.</para>
<para>Normally, setting the <option>-V</option> option
directly is not necessary: the resolution of the RTS timer is
adjusted automatically if a short interval is requested with
the <option>-C</option> or <option>-i</option> options.
However, setting <option>-V</option> is required in order to
increase the resolution of the time profiler.</para>
</listitem>
</varlistentry>
</variablelist>
</sect2>
<sect2 id="rts-options-gc"> <sect2 id="rts-options-gc">
<title>RTS options to control the garbage collector</title> <title>RTS options to control the garbage collector</title>
......
...@@ -1524,10 +1524,7 @@ f "2" = 2 ...@@ -1524,10 +1524,7 @@ f "2" = 2
every 4k of allocation). With <option>-C0</option> or every 4k of allocation). With <option>-C0</option> or
<option>-C</option>, context switches will occur as often as <option>-C</option>, context switches will occur as often as
possible (at every heap block allocation). By default, context possible (at every heap block allocation). By default, context
switches occur every 20ms. Note that GHC's internal timer ticks switches occur every 20ms.</para>
every 20ms, and the context switch timer is always a multiple of
this timer, so 20ms is the maximum granularity available for timed
context switches.</para>
</listitem> </listitem>
</varlistentry> </varlistentry>
</variablelist> </variablelist>
......
...@@ -513,8 +513,6 @@ ...@@ -513,8 +513,6 @@
Misc junk Misc junk
-------------------------------------------------------------------------- */ -------------------------------------------------------------------------- */
#define TICK_MILLISECS (1000/TICK_FREQUENCY) /* ms per tick */
#define NO_TREC stg_NO_TREC_closure #define NO_TREC stg_NO_TREC_closure
#define END_TSO_QUEUE stg_END_TSO_QUEUE_closure #define END_TSO_QUEUE stg_END_TSO_QUEUE_closure
......
...@@ -42,7 +42,7 @@ struct GC_FLAGS { ...@@ -42,7 +42,7 @@ struct GC_FLAGS {
rtsBool ringBell; rtsBool ringBell;
rtsBool frontpanel; rtsBool frontpanel;
int idleGCDelayTicks; /* in milliseconds */ int idleGCDelayTime; /* in milliseconds */
}; };
struct DEBUG_FLAGS { struct DEBUG_FLAGS {
...@@ -112,6 +112,10 @@ struct CONCURRENT_FLAGS { ...@@ -112,6 +112,10 @@ struct CONCURRENT_FLAGS {
int ctxtSwitchTicks; /* derived */ int ctxtSwitchTicks; /* derived */
}; };
struct MISC_FLAGS {
int tickInterval; /* in milliseconds */
};
#ifdef PAR #ifdef PAR
/* currently the same as GRAN_STATS_FLAGS */ /* currently the same as GRAN_STATS_FLAGS */
struct PAR_STATS_FLAGS { struct PAR_STATS_FLAGS {
...@@ -300,6 +304,7 @@ typedef struct _RTS_FLAGS { ...@@ -300,6 +304,7 @@ typedef struct _RTS_FLAGS {
/* The first portion of RTS_FLAGS is invariant. */ /* The first portion of RTS_FLAGS is invariant. */
struct GC_FLAGS GcFlags; struct GC_FLAGS GcFlags;
struct CONCURRENT_FLAGS ConcFlags; struct CONCURRENT_FLAGS ConcFlags;
struct MISC_FLAGS MiscFlags;
struct DEBUG_FLAGS DebugFlags; struct DEBUG_FLAGS DebugFlags;
struct COST_CENTRE_FLAGS CcFlags; struct COST_CENTRE_FLAGS CcFlags;
struct PROFILING_FLAGS ProfFlags; struct PROFILING_FLAGS ProfFlags;
......
...@@ -374,6 +374,8 @@ main(int argc, char *argv[]) ...@@ -374,6 +374,8 @@ main(int argc, char *argv[])
RTS_FLAGS, DebugFlags.weak); RTS_FLAGS, DebugFlags.weak);
struct_field_("RtsFlags_GcFlags_initialStkSize", struct_field_("RtsFlags_GcFlags_initialStkSize",
RTS_FLAGS, GcFlags.initialStkSize); RTS_FLAGS, GcFlags.initialStkSize);
struct_field_("RtsFlags_MiscFlags_tickInterval",
RTS_FLAGS, MiscFlags.tickInterval);
struct_size(StgFunInfoExtraFwd); struct_size(StgFunInfoExtraFwd);
struct_field(StgFunInfoExtraFwd, slow_apply); struct_field(StgFunInfoExtraFwd, slow_apply);
......
...@@ -1975,8 +1975,8 @@ delayzh_fast ...@@ -1975,8 +1975,8 @@ delayzh_fast
#else #else
W_ time; W_ time;
time = foreign "C" getourtimeofday(); time = foreign "C" getourtimeofday() [R1];
target = (R1 / (TICK_MILLISECS*1000)) + time; target = (R1 / (TO_W_(RtsFlags_MiscFlags_tickInterval(RtsFlags))*1000)) + time;
StgTSO_block_info(CurrentTSO) = target; StgTSO_block_info(CurrentTSO) = target;
/* Insert the new thread in the sleeping queue. */ /* Insert the new thread in the sleeping queue. */
......
...@@ -277,7 +277,7 @@ initProfilingLogFile(void) ...@@ -277,7 +277,7 @@ initProfilingLogFile(void)
if (RtsFlags.CcFlags.doCostCentres == COST_CENTRES_XML) { if (RtsFlags.CcFlags.doCostCentres == COST_CENTRES_XML) {
/* dump the time, and the profiling interval */ /* dump the time, and the profiling interval */
fprintf(prof_file, "\"%s\"\n", time_str()); fprintf(prof_file, "\"%s\"\n", time_str());
fprintf(prof_file, "\"%d ms\"\n", TICK_MILLISECS); fprintf(prof_file, "\"%d ms\"\n", RtsFlags.MiscFlags.tickInterval);
/* declare all the cost centres */ /* declare all the cost centres */
{ {
...@@ -732,8 +732,10 @@ reportCCSProfiling( void ) ...@@ -732,8 +732,10 @@ reportCCSProfiling( void )
fprintf(prof_file, "\n\n"); fprintf(prof_file, "\n\n");
fprintf(prof_file, "\ttotal time = %11.2f secs (%lu ticks @ %d ms)\n", fprintf(prof_file, "\ttotal time = %11.2f secs (%lu ticks @ %d ms)\n",
total_prof_ticks / (StgFloat) TICK_FREQUENCY, (double) total_prof_ticks *
total_prof_ticks, TICK_MILLISECS); (double) RtsFlags.MiscFlags.tickInterval / 1000,
(unsigned long) total_prof_ticks,
(int) RtsFlags.MiscFlags.tickInterval);
fprintf(prof_file, "\ttotal alloc = %11s bytes", fprintf(prof_file, "\ttotal alloc = %11s bytes",
ullong_format_string(total_alloc * sizeof(W_), ullong_format_string(total_alloc * sizeof(W_),
......
...@@ -57,9 +57,6 @@ initProfTimer( void ) ...@@ -57,9 +57,6 @@ initProfTimer( void )
{ {
performHeapProfile = rtsFalse; performHeapProfile = rtsFalse;
RtsFlags.ProfFlags.profileIntervalTicks =
RtsFlags.ProfFlags.profileInterval / TICK_MILLISECS;
ticks_to_heap_profile = RtsFlags.ProfFlags.profileIntervalTicks; ticks_to_heap_profile = RtsFlags.ProfFlags.profileIntervalTicks;
startHeapProfTimer(); startHeapProfTimer();
......
...@@ -12,7 +12,6 @@ ...@@ -12,7 +12,6 @@
#include "RtsFlags.h" #include "RtsFlags.h"
#include "RtsUtils.h" #include "RtsUtils.h"
#include "BlockAlloc.h" #include "BlockAlloc.h"
#include "Timer.h" /* CS_MIN_MILLISECS */
#include "Profiling.h" #include "Profiling.h"
#ifdef HAVE_CTYPE_H #ifdef HAVE_CTYPE_H
...@@ -150,7 +149,7 @@ void initRtsFlagsDefaults(void) ...@@ -150,7 +149,7 @@ void initRtsFlagsDefaults(void)
#ifdef RTS_GTK_FRONTPANEL #ifdef RTS_GTK_FRONTPANEL
RtsFlags.GcFlags.frontpanel = rtsFalse; RtsFlags.GcFlags.frontpanel = rtsFalse;
#endif #endif
RtsFlags.GcFlags.idleGCDelayTicks = 300 / TICK_MILLISECS; /* ticks */ RtsFlags.GcFlags.idleGCDelayTime = 300; /* millisecs */
#ifdef DEBUG #ifdef DEBUG
RtsFlags.DebugFlags.scheduler = rtsFalse; RtsFlags.DebugFlags.scheduler = rtsFalse;
...@@ -191,7 +190,8 @@ void initRtsFlagsDefaults(void) ...@@ -191,7 +190,8 @@ void initRtsFlagsDefaults(void)
RtsFlags.ProfFlags.doHeapProfile = rtsFalse; RtsFlags.ProfFlags.doHeapProfile = rtsFalse;
#endif #endif
RtsFlags.ConcFlags.ctxtSwitchTime = CS_MIN_MILLISECS; /* In milliseconds */ RtsFlags.MiscFlags.tickInterval = 50; /* In milliseconds */
RtsFlags.ConcFlags.ctxtSwitchTime = 50; /* In milliseconds */
#ifdef THREADED_RTS #ifdef THREADED_RTS
RtsFlags.ParFlags.nNodes = 1; RtsFlags.ParFlags.nNodes = 1;
...@@ -790,14 +790,9 @@ error = rtsTrue; ...@@ -790,14 +790,9 @@ error = rtsTrue;
} else { } else {
I_ cst; /* tmp */ I_ cst; /* tmp */
/* Convert to ticks */ /* Convert to millisecs */
cst = (I_) ((atof(rts_argv[arg]+2) * 1000)); cst = (I_) ((atof(rts_argv[arg]+2) * 1000));
if (cst > 0 && cst < TICK_MILLISECS) { RtsFlags.GcFlags.idleGCDelayTime = cst;
cst = TICK_MILLISECS;
} else {
cst = cst / TICK_MILLISECS;
}
RtsFlags.GcFlags.idleGCDelayTicks = cst;
} }
break; break;
...@@ -993,10 +988,6 @@ error = rtsTrue; ...@@ -993,10 +988,6 @@ error = rtsTrue;
/* Convert to milliseconds */ /* Convert to milliseconds */
cst = (I_) ((atof(rts_argv[arg]+2) * 1000)); cst = (I_) ((atof(rts_argv[arg]+2) * 1000));
cst = (cst / CS_MIN_MILLISECS) * CS_MIN_MILLISECS;
if (cst != 0 && cst < CS_MIN_MILLISECS)
cst = CS_MIN_MILLISECS;
RtsFlags.ProfFlags.profileInterval = cst; RtsFlags.ProfFlags.profileInterval = cst;
} }
break; break;
...@@ -1011,14 +1002,23 @@ error = rtsTrue; ...@@ -1011,14 +1002,23 @@ error = rtsTrue;
/* Convert to milliseconds */ /* Convert to milliseconds */
cst = (I_) ((atof(rts_argv[arg]+2) * 1000)); cst = (I_) ((atof(rts_argv[arg]+2) * 1000));
cst = (cst / CS_MIN_MILLISECS) * CS_MIN_MILLISECS;
if (cst != 0 && cst < CS_MIN_MILLISECS)
cst = CS_MIN_MILLISECS;
RtsFlags.ConcFlags.ctxtSwitchTime = cst; RtsFlags.ConcFlags.ctxtSwitchTime = cst;
} }
break; break;
case 'V': /* master tick interval */
if (rts_argv[arg][2] == '\0') {
// turns off ticks completely
RtsFlags.MiscFlags.tickInterval = 0;
} else {
I_ cst; /* tmp */
/* Convert to milliseconds */
cst = (I_) ((atof(rts_argv[arg]+2) * 1000));
RtsFlags.MiscFlags.tickInterval = cst;
}
break;
#if defined(THREADED_RTS) && !defined(NOSMP) #if defined(THREADED_RTS) && !defined(NOSMP)
case 'N': case 'N':
THREADED_BUILD_ONLY( THREADED_BUILD_ONLY(
...@@ -1153,6 +1153,47 @@ error = rtsTrue; ...@@ -1153,6 +1153,47 @@ error = rtsTrue;
} }
} }
} }
// Determine what tick interval we should use for the RTS timer
// by taking the shortest of the various intervals that we need to
// monitor.
if (RtsFlags.MiscFlags.tickInterval <= 0) {
RtsFlags.MiscFlags.tickInterval = 50;
}
if (RtsFlags.ConcFlags.ctxtSwitchTime > 0) {
RtsFlags.MiscFlags.tickInterval =
stg_min(RtsFlags.ConcFlags.ctxtSwitchTime,
RtsFlags.MiscFlags.tickInterval);
}
if (RtsFlags.GcFlags.idleGCDelayTime > 0) {
RtsFlags.MiscFlags.tickInterval =
stg_min(RtsFlags.GcFlags.idleGCDelayTime,
RtsFlags.MiscFlags.tickInterval);
}
#ifdef PROFILING
if (RtsFlags.ProfFlags.profileInterval > 0) {
RtsFlags.MiscFlags.tickInterval =
stg_min(RtsFlags.ProfFlags.profileInterval,
RtsFlags.MiscFlags.tickInterval);
}
#endif
if (RtsFlags.ConcFlags.ctxtSwitchTime > 0) {
RtsFlags.ConcFlags.ctxtSwitchTicks =
RtsFlags.ConcFlags.ctxtSwitchTime /
RtsFlags.MiscFlags.tickInterval;
} else {
RtsFlags.ConcFlags.ctxtSwitchTicks = 0;
}
#ifdef PROFILING
RtsFlags.ProfFlags.profileIntervalTicks =
RtsFlags.ProfFlags.profileInterval / RtsFlags.MiscFlags.tickInterval;
#endif
if (error) { if (error) {
const char **p; const char **p;
......
...@@ -210,7 +210,7 @@ hs_init(int *argc, char **argv[]) ...@@ -210,7 +210,7 @@ hs_init(int *argc, char **argv[])
#endif #endif
/* start the virtual timer 'subsystem'. */ /* start the virtual timer 'subsystem'. */
startTimer(TICK_MILLISECS); startTimer();
/* Initialise the stats department */ /* Initialise the stats department */
initStats(); initStats();
......
...@@ -2509,9 +2509,6 @@ initScheduler(void) ...@@ -2509,9 +2509,6 @@ initScheduler(void)
context_switch = 0; context_switch = 0;
sched_state = SCHED_RUNNING; sched_state = SCHED_RUNNING;
RtsFlags.ConcFlags.ctxtSwitchTicks =
RtsFlags.ConcFlags.ctxtSwitchTime / TICK_MILLISECS;
#if defined(THREADED_RTS) #if defined(THREADED_RTS)
/* Initialise the mutex and condition variables used by /* Initialise the mutex and condition variables used by
* the scheduler. */ * the scheduler. */
......
...@@ -54,20 +54,22 @@ handle_tick(int unused STG_UNUSED) ...@@ -54,20 +54,22 @@ handle_tick(int unused STG_UNUSED)
#if defined(THREADED_RTS) #if defined(THREADED_RTS)
/* /*
* If we've been inactive for idleGCDelayTicks (set by +RTS * If we've been inactive for idleGCDelayTime (set by +RTS
* -I), tell the scheduler to wake up and do a GC, to check * -I), tell the scheduler to wake up and do a GC, to check
* for threads that are deadlocked. * for threads that are deadlocked.
*/ */
switch (recent_activity) { switch (recent_activity) {
case ACTIVITY_YES: case ACTIVITY_YES:
recent_activity = ACTIVITY_MAYBE_NO; recent_activity = ACTIVITY_MAYBE_NO;
ticks_to_gc = RtsFlags.GcFlags.idleGCDelayTicks; ticks_to_gc = RtsFlags.GcFlags.idleGCDelayTime /
RtsFlags.MiscFlags.tickInterval;
break; break;
case ACTIVITY_MAYBE_NO: case ACTIVITY_MAYBE_NO:
if (ticks_to_gc == 0) break; /* 0 ==> no idle GC */ if (ticks_to_gc == 0) break; /* 0 ==> no idle GC */
ticks_to_gc--; ticks_to_gc--;
if (ticks_to_gc == 0) { if (ticks_to_gc == 0) {
ticks_to_gc = RtsFlags.GcFlags.idleGCDelayTicks; ticks_to_gc = RtsFlags.GcFlags.idleGCDelayTime /
RtsFlags.MiscFlags.tickInterval;
recent_activity = ACTIVITY_INACTIVE; recent_activity = ACTIVITY_INACTIVE;
blackholes_need_checking = rtsTrue; blackholes_need_checking = rtsTrue;
/* hack: re-use the blackholes_need_checking flag */ /* hack: re-use the blackholes_need_checking flag */
...@@ -81,17 +83,17 @@ handle_tick(int unused STG_UNUSED) ...@@ -81,17 +83,17 @@ handle_tick(int unused STG_UNUSED)
} }
int int
startTimer(nat ms) startTimer(void)
{ {
#ifdef PROFILING #ifdef PROFILING
initProfTimer(); initProfTimer();
#endif #endif
return startTicker(ms, handle_tick); return startTicker(RtsFlags.MiscFlags.tickInterval, handle_tick);
} }
int int
stopTimer() stopTimer(void)
{ {
return stopTicker(); return stopTicker();
} }
/* ----------------------------------------------------------------------------- /* -----------------------------------------------------------------------------
* *
* (c) The GHC Team, 1995-2005 * (c) The GHC Team, 1995-2006
* *
* Interval timer service for profiling and pre-emptive scheduling. * Interval timer service for profiling and pre-emptive scheduling.
* *
...@@ -9,16 +9,9 @@ ...@@ -9,16 +9,9 @@
#ifndef TIMER_H #ifndef TIMER_H
#define TIMER_H #define TIMER_H
# define TICK_MILLISECS (1000/TICK_FREQUENCY) /* ms per tick */
/* Context switch timing constants. Context switches happen after a
* whole number of ticks, the default being every tick.
*/
#define CS_MIN_MILLISECS TICK_MILLISECS /* milliseconds per slice */
typedef void (*TickProc)(int); typedef void (*TickProc)(int);
extern int startTimer(nat ms); extern int startTimer(void);
extern int stopTimer(void); extern int stopTimer(void);
#endif /* TIMER_H */ #endif /* TIMER_H */
...@@ -221,6 +221,6 @@ getourtimeofday(void) ...@@ -221,6 +221,6 @@ getourtimeofday(void)
struct timeval tv; struct timeval tv;
gettimeofday(&tv, (struct timezone *) NULL); gettimeofday(&tv, (struct timezone *) NULL);
// cast to lnat because nat may be 64 bit when int is only 32 bit // cast to lnat because nat may be 64 bit when int is only 32 bit
return ((lnat)tv.tv_sec * TICK_FREQUENCY + return ((lnat)tv.tv_sec * 1000 / RtsFlags.MiscFlags.tickInterval +
(lnat)tv.tv_usec * TICK_FREQUENCY / 1000000); (lnat)tv.tv_usec / (RtsFlags.MiscFlags.tickInterval * 1000));
} }
...@@ -126,7 +126,7 @@ awaitEvent(rtsBool wait) ...@@ -126,7 +126,7 @@ awaitEvent(rtsBool wait)
min = 0; min = 0;
} else if (sleeping_queue != END_TSO_QUEUE) { } else if (sleeping_queue != END_TSO_QUEUE) {
min = (sleeping_queue->block_info.target - ticks) min = (sleeping_queue->block_info.target - ticks)
* TICK_MILLISECS * 1000; * RtsFlags.MiscFlags.tickInterval * 1000;
} else { } else {
min = 0x7ffffff; min = 0x7ffffff;
} }
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment