diff options
author | neal <neal> | 2008-06-27 15:17:27 +0000 |
---|---|---|
committer | neal <neal> | 2008-06-27 15:17:27 +0000 |
commit | c314b447a633a3aa2d1e4a882675038fd381e873 (patch) | |
tree | c3d2625fc63af15c7e6200203a5347e68f192713 | |
parent | 8305c7cd3d379fda42cee8c58783bf2ae52f4ff0 (diff) |
hurd/
2008-06-27 Neal H. Walfield <neal@gnu.org>
* activity.h (RM_activity_stats): Don't define.
(RM_activity_info): Replace with this.
(struct activity_stats): Add field pressure_local. Make field
pressure an 8 bit integer.
(struct activity_stats_buffer): Remove structure.
(struct activity_info): Replace with this structure.
(activity_info_stats): Define.
(activity_info_pressure): Likewise.
(activity_stats): Remove method.
(activity_info): Replace with this.
viengoos/
2008-06-27 Neal H. Walfield <neal@gnu.org>
* activity.h (struct activity): Add fields free_bad_karma,
free_goal, free_allocations and free_initial_allocation.
* rm.h (rm_method_id_string): Replace RM_activity_stats handling
with RM_activity_info handling.
* thread.h (THREAD_WAIT_STATS): Remove.
(THREAD_WAIT_ACTIVITY_INFO): Replace with this.
(struct thread): Add field wait_reason_arg2.
* ager.c (update_stats): Account for local pressure when
calculating availability. Don't use activity_stats to inform
threads of new statistics but activity_info.
* object.c (object_desc_claim): Adjust the activity's FREE_GOAL,
FREE_ALLOCATIONS, and FRAMES_EXCLUDED fields as appropriate.
* pager.c (pager_collect): When selecting a victim, don't include
an activity's excluded frames in its allocation. Don't even
consider activities for which FREE_ALLOCATIONS is non-zero.
Having selected a victim, don't increase the pressure do
drastically. Update the local pressure. Having selected a victim
from which to revoke pages, send any waiting threads a message to
free memory. If VICTIM->FREE_BAD_KARMA is 0, assume that the
memory will be freed and give the activity 100 claims to do it.
If the activity has bad karma, decrease it by one.
* server.c (server_loop): Replace activity_stats implementation
with implementation appropriate for activity_info.
hieronymus/
2008-06-27 Neal H. Walfield <neal@gnu.org>
* hieronymus.c (do_gather_stats): Change to use activity_info
instead of activity_stats.
(main): Introduce code to delay process creation.
ruth/
2008-06-27 Neal H. Walfield <neal@gnu.org>
* ruth.c (main): Update to use activity_info instead of
activity_stats.
benchmarks/
2008-06-27 Neal H. Walfield <neal@gnu.org>
* shared-memory-distribution.c (main): Update to use activity_info
instead of activity_status.
* activity-distribution.c (main): Likewise.
* GCbench.c: Include <stdint.h> and <stdbool.h>.
(now): New function.
(struct stats): Add fields time, gcs and iter.
(have_a_hog): New variable.
(mem_hog) [__gnu_hurd_viengoos__]: Rename from this...
(helper) [__gnu_hurd_viengoos__]: ... to this. Use activity_info,
not activity_stats. Gather more data.
(helper) [! __gnu_hurd_viengoos__]: New function.
(tid): Rename from this...
(helper_tid): ... to this.
(helper_fork) [__gnu_hurd_viengoos__]: Name activities.
(main): Improve output.
benchmarks/boehm-gc/
2008-06-27 Neal H. Walfield <neal@gnu.org>
* patches/05-viengoos-scheduler.patch: Update to use activity_info
in place of activity_stats. Listen for pressure messages and act
appropriately. Tighten adaptive code. Improve profiling code.
-rw-r--r-- | benchmarks/ChangeLog | 19 | ||||
-rw-r--r-- | benchmarks/GCbench.c | 219 | ||||
-rw-r--r-- | benchmarks/activity-distribution.c | 46 | ||||
-rw-r--r-- | benchmarks/boehm-gc/ChangeLog | 6 | ||||
-rw-r--r-- | benchmarks/boehm-gc/patches/05-viengoos-scheduler.patch | 580 | ||||
-rw-r--r-- | benchmarks/shared-memory-distribution.c | 55 | ||||
-rw-r--r-- | hieronymus/ChangeLog | 7 | ||||
-rw-r--r-- | hieronymus/hieronymus.c | 34 | ||||
-rw-r--r-- | hurd/ChangeLog | 13 | ||||
-rw-r--r-- | hurd/activity.h | 80 | ||||
-rw-r--r-- | ruth/ChangeLog | 5 | ||||
-rw-r--r-- | ruth/ruth.c | 10 | ||||
-rw-r--r-- | viengoos/ChangeLog | 26 | ||||
-rw-r--r-- | viengoos/activity.h | 15 | ||||
-rw-r--r-- | viengoos/ager.c | 75 | ||||
-rw-r--r-- | viengoos/object.c | 57 | ||||
-rw-r--r-- | viengoos/pager.c | 112 | ||||
-rw-r--r-- | viengoos/rm.h | 4 | ||||
-rw-r--r-- | viengoos/server.c | 64 | ||||
-rw-r--r-- | viengoos/thread.h | 9 |
20 files changed, 1040 insertions, 396 deletions
diff --git a/benchmarks/ChangeLog b/benchmarks/ChangeLog index 02bf58b..98f58f4 100644 --- a/benchmarks/ChangeLog +++ b/benchmarks/ChangeLog @@ -1,3 +1,22 @@ +2008-06-27 Neal H. Walfield <neal@gnu.org> + + * shared-memory-distribution.c (main): Update to use activity_info + instead of activity_status. + * activity-distribution.c (main): Likewise. + + * GCbench.c: Include <stdint.h> and <stdbool.h>. + (now): New function. + (struct stats): Add fields time, gcs and iter. + (have_a_hog): New variable. + (mem_hog) [__gnu_hurd_viengoos__]: Rename from this... + (helper) [__gnu_hurd_viengoos__]: ... to this. Use activity_info, + not activity_stats. Gather more data. + (helper) [! __gnu_hurd_viengoos__]: New function. + (tid): Rename from this... + (helper_tid): ... to this. + (helper_fork) [__gnu_hurd_viengoos__]: Name activities. + (main): Improve output. + 2008-06-23 Neal H. Walfield <neal@gnu.org> * GCbench.c (struct stats) [__gnu_hurd_viengoos__]: New structure. diff --git a/benchmarks/GCbench.c b/benchmarks/GCbench.c index 44e3cc2..ab05665 100644 --- a/benchmarks/GCbench.c +++ b/benchmarks/GCbench.c @@ -52,18 +52,41 @@ # include <gc/gc.h> #endif +#include <stdint.h> +#include <stdbool.h> + +static inline uint64_t +now (void) +{ + struct timeval t; + struct timezone tz; + + if (gettimeofday( &t, &tz ) == -1) + return 0; + return (t.tv_sec * 1000000ULL + t.tv_usec); +} +uint64_t epoch; + #include <assert.h> -#ifdef __gnu_hurd_viengoos__ -#define STATS 1000 +#define STATS 5000 + struct stats { int alloced[2]; int available[2]; + uint64_t time; int period; -} stats[1000]; + int gcs; + int iter; +} stats[STATS]; int stat_count; +static int done; + +static int iter; + +#ifdef __gnu_hurd_viengoos__ #include <hurd/storage.h> #include <hurd/cap.h> #include <hurd/activity.h> @@ -71,18 +94,17 @@ int stat_count; #include <hurd/anonymous.h> #include <string.h> -static int done; - addr_t gc_activity; addr_t hog_activity; +bool have_a_hog = false; void * -mem_hog (void *arg) +helper (void *arg) { pthread_setactivity_np (hog_activity); - struct activity_stats_buffer buffer; + struct activity_info info; void wait_read_stats (void) { @@ -90,82 +112,94 @@ mem_hog (void *arg) /* First the main thread. */ error_t err; - err = rm_activity_stats (gc_activity, - stat_count == 0 ? 0 : buffer.stats[0].period + 1, - &buffer, &count); + + err = rm_activity_info (gc_activity, activity_info_stats, + stat_count == 0 + ? 0 : stats[stat_count - 1].period + 1, + &info); assert_perror (err); + assert (info.event == activity_info_stats); + assert (info.stats.count > 0); stats[stat_count].alloced[0] - = buffer.stats[0].clean + buffer.stats[0].dirty - + buffer.stats[0].pending_eviction; - stats[stat_count].available[0] = buffer.stats[0].available; + = info.stats.stats[0].clean + info.stats.stats[0].dirty + + info.stats.stats[0].pending_eviction; + stats[stat_count].available[0] = info.stats.stats[0].available; - stats[stat_count].period = buffer.stats[0].period; + stats[stat_count].time = now () - epoch; + stats[stat_count].period = info.stats.stats[0].period; + stats[stat_count].gcs = GC_gc_no; + stats[stat_count].iter = iter; /* Then, the hog. */ - err = rm_activity_stats (hog_activity, - stat_count == 0 ? 0 : buffer.stats[0].period, - &buffer, &count); + err = rm_activity_info (hog_activity, activity_info_stats, + stat_count == 0 + ? 0 : stats[stat_count - 1].period + 1, + &info); assert_perror (err); + assert (info.event == activity_info_stats); + assert (info.stats.count > 0); stats[stat_count].alloced[1] - = buffer.stats[0].clean + buffer.stats[0].dirty - + buffer.stats[0].pending_eviction; - stats[stat_count].available[1] = buffer.stats[0].available; + = info.stats.stats[0].clean + info.stats.stats[0].dirty + + info.stats.stats[0].pending_eviction; + stats[stat_count].available[1] = info.stats.stats[0].available; stat_count ++; if (stat_count % 10 == 0) debug (0, DEBUG_BOLD ("Period %d"), stat_count); } - /* Wait a minute before starting. */ - int i; - for (i = 0; i < 30; i ++) - wait_read_stats (); + if (have_a_hog) + { + /* Wait a minute before starting. */ + int i; + for (i = 0; i < 20; i ++) + wait_read_stats (); - /* Now, allocate a 10MB chunk of memory every 2 seconds until we've - allocate half the initially available memory. */ + /* Now, allocate a 10MB chunk of memory every 2 seconds until we've + allocate half the initially available memory. */ - int available = stats[1].available[0] * PAGESIZE; + int available = stats[1].available[0] * PAGESIZE; - printf (DEBUG_BOLD ("mem hog starting (avail: %d, period: %d)!") "\n", - available, stats[stat_count - 1].period); + printf (DEBUG_BOLD ("mem hog starting (avail: %d)!") "\n", available); - /* The chunk size. */ - int s = 5 * 1024 * 1024; - int total = available / 2 / s; - struct anonymous_pager *pagers[total]; - void *buffers[total]; + /* The chunk size. */ + int s = 5 * 1024 * 1024; + int total = available / 2 / s; + struct anonymous_pager *pagers[total]; + void *buffers[total]; - int c; - for (c = 0; c < total && ! done; c ++) - { - pagers[c] - = anonymous_pager_alloc (hog_activity, NULL, s, MAP_ACCESS_ALL, - OBJECT_POLICY (false, OBJECT_PRIORITY_LRU), 0, - NULL, &buffers[c]); - assert (pagers[c]); - assert (buffers[c]); + int c; + for (c = 0; c < total && ! done; c ++) + { + pagers[c] + = anonymous_pager_alloc (hog_activity, NULL, s, MAP_ACCESS_ALL, + OBJECT_POLICY (false, OBJECT_PRIORITY_LRU), 0, + NULL, &buffers[c]); + assert (pagers[c]); + assert (buffers[c]); - memset (buffers[c], 0, s); + memset (buffers[c], 0, s); - wait_read_stats (); - } + wait_read_stats (); + } - /* Wait a minute before freeing. */ - for (i = 0; i < 30 && ! done; i ++) - wait_read_stats (); + /* Wait a minute before freeing. */ + for (i = 0; i < 20 && ! done; i ++) + wait_read_stats (); - printf (DEBUG_BOLD ("mem hog releasing memory!") "\n"); + printf (DEBUG_BOLD ("mem hog releasing memory!") "\n"); - /* Release the memory. */ - for (i = 0; i < total && ! done; i ++) - { - wait_read_stats (); + /* Release the memory. */ + for (i = 0; i < total && ! done; i ++) + { + wait_read_stats (); - printf (DEBUG_BOLD ("release: %d!") "\n", i); - anonymous_pager_destroy (pagers[i]); + printf (DEBUG_BOLD ("release: %d!") "\n", i); + anonymous_pager_destroy (pagers[i]); + } } /* Finally, wait until the main thread is done. */ @@ -174,24 +208,55 @@ mem_hog (void *arg) return 0; } +#else +#define have_a_hog false + +void * +helper (void *arg) +{ + while (! done) + { + sleep (2); + stats[stat_count].alloced[0] = GC_get_heap_size (); + stats[stat_count].available[0] = 0; + stats[stat_count].time = now () - epoch; + stats[stat_count].gcs = GC_gc_no; + stats[stat_count].iter = iter; + stat_count ++; + } + + return 0; +} +#endif + -pthread_t tid; +pthread_t helper_tid; void -mem_hog_fork (void) +helper_fork (void) { + int err; + +#ifdef __gnu_hurd_viengoos__ gc_activity = storage_alloc (ADDR_VOID, cap_activity_control, STORAGE_LONG_LIVED, OBJECT_POLICY_DEFAULT, ADDR_VOID).addr; if (ADDR_IS_VOID (gc_activity)) panic ("Failed to allocate main activity"); + struct object_name name; + snprintf (&name.name[0], sizeof (name.name), "gc.%x", l4_myself ()); + rm_object_name (ADDR_VOID, gc_activity, name); + hog_activity = storage_alloc (ADDR_VOID, cap_activity_control, STORAGE_LONG_LIVED, OBJECT_POLICY_DEFAULT, ADDR_VOID).addr; if (ADDR_IS_VOID (hog_activity)) panic ("Failed to allocate hog activity"); + snprintf (&name.name[0], sizeof (name.name), "hog.%x", l4_myself ()); + rm_object_name (ADDR_VOID, hog_activity, name); + /* We give the main thread and the hog the same priority and weight. */ struct activity_policy in, out; @@ -202,7 +267,6 @@ mem_hog_fork (void) in.child_rel.priority = 2; in.child_rel.weight = 20; - error_t err; err = rm_activity_policy (ADDR_VOID, ACTIVITY_POLICY_CHILD_REL_SET, in, &out); assert (err == 0); @@ -217,11 +281,10 @@ mem_hog_fork (void) pthread_setactivity_np (gc_activity); +#endif - err = pthread_create (&tid, NULL, mem_hog, NULL); - assert_perror (err); + pthread_create (&helper_tid, NULL, helper, NULL); } -#endif #if 1 #define DEBUG(fmt, ...) @@ -400,10 +463,11 @@ int main() { extern int GC_print_stats; // GC_print_stats = 1; extern int GC_viengoos_scheduler; - // GC_viengoos_scheduler = 0; -#ifdef __gnu_hurd_viengoos__ - mem_hog_fork (); -#endif + GC_viengoos_scheduler = 1; + + helper_fork (); + epoch = now (); + Node root; Node longLivedTree; @@ -465,10 +529,9 @@ int main() { PrintDiagnostics(); #define ITERATIONS 100 - int j; - for (j = 0; j < ITERATIONS; j ++) + for (iter = 0; iter < ITERATIONS; iter ++) { - printf ("Iteration %d\n", j); + printf ("Iteration %d\n", iter); for (d = kMinTreeDepth; d <= kMaxTreeDepth; d += 2) { TimeConstruction(d); } @@ -495,22 +558,28 @@ int main() { dump_profile(); # endif -#ifdef __gnu_hurd_viengoos__ done = 1; void *status; - pthread_join (tid, &status); + pthread_join (helper_tid, &status); { + printf ("%s scheduler, %smemory hog\n" + "time\tgc alloc'd\tgc avail\thog alloc'd\thog avail\tgcs\titeration\n", + GC_viengoos_scheduler ? "Viengoos" : "Boehm", + have_a_hog ? "" : "no "); + int i; - printf ("period, main alloc'd + hog alloc'd, main avail, hog alloc'd, hog avail\n"); for (i = 0; i < stat_count; i ++) { - printf ("%d %d %d %d %d\n", + printf ("%lld.%lld\t%d\t%d\t%d\t%d\t%d\t%d\t%d\n", + stats[i].time / 1000000, + (stats[i].time / 100000) % 10, stats[i].period, stats[i].alloced[0], stats[i].available[0], - stats[i].alloced[1], stats[i].available[1]); + stats[i].alloced[1], stats[i].available[1], + stats[i].gcs, + stats[i].iter); } } -#endif } diff --git a/benchmarks/activity-distribution.c b/benchmarks/activity-distribution.c index e287d28..cffc288 100644 --- a/benchmarks/activity-distribution.c +++ b/benchmarks/activity-distribution.c @@ -5,6 +5,7 @@ #include <errno.h> #include <string.h> #include <pthread.h> +#include <assert.h> #include <hurd/activity.h> #include <hurd/storage.h> @@ -43,10 +44,11 @@ main (int argc, char *argv[]) ADDR_VOID).addr; struct activity_policy in; + in.sibling_rel.priority = i == 0 ? 2 : 1; in.sibling_rel.weight = i + 1; struct activity_policy out; err = rm_activity_policy (activities[i], - ACTIVITY_POLICY_SIBLING_REL_WEIGHT_SET, in, + ACTIVITY_POLICY_SIBLING_REL_SET, in, &out); assert (err == 0); } @@ -58,14 +60,14 @@ main (int argc, char *argv[]) int available; { - int count; - struct activity_stats_buffer buffer; + struct activity_info info; - err = rm_activity_stats (activity, 1, &buffer, &count); + err = rm_activity_info (activity, activity_info_stats, 1, &info); assert (err == 0); - assert (count > 0); + assert (info.event == activity_info_stats); + assert (info.stats.count >= 1); - available = buffer.stats[0].available * PAGESIZE; + available = info.stats.stats[0].available * PAGESIZE; } printf ("%d kb memory available\n", available / 1024); @@ -153,26 +155,29 @@ main (int argc, char *argv[]) uintptr_t next_period = 0; for (i = 0; i < ITERATIONS; i ++) { - debug (0, DEBUG_BOLD ("starting iteration %d (%x)"), i, l4_myself ()); + printf ("Iteration: %d\n", i); - int count; - struct activity_stats_buffer buffer; + struct activity_info info; - rm_activity_stats (activity, next_period, &buffer, &count); - assert (count > 0); + rm_activity_info (activity, activity_info_stats, + next_period, &info); + assert (info.event == activity_info_stats); + assert (info.stats.count > 0); if (i != 0) - assertx (buffer.stats[0].period != stats[i - 1][0].period, + assertx (info.stats.stats[0].period != stats[i - 1][0].period, "%d == %d", - buffer.stats[0].period, stats[i - 1][0].period); + info.stats.stats[0].period, stats[i - 1][0].period); - stats[i][0] = buffer.stats[0]; + stats[i][0] = info.stats.stats[0]; int j; for (j = 0; j < THREADS; j ++) { - rm_activity_stats (activities[j], next_period, &buffer, &count); - assert (count > 0); - stats[i][1 + j] = buffer.stats[0]; + rm_activity_info (activity, activity_info_stats, + next_period, &info); + assert (info.event == activity_info_stats); + assert (info.stats.count > 0); + stats[i][1 + j] = info.stats.stats[0]; } next_period = stats[i][0].period + 1; @@ -194,10 +199,13 @@ main (int argc, char *argv[]) { int j; - printf ("%d ", (int) stats[i][0].period); + printf ("%d", (int) stats[i][0].period); for (j = 0; j < 1 + THREADS; j ++) - printf ("%d ", (int) stats[i][j].clean + (int) stats[i][j].dirty); + printf ("\t%d\t%d\t%d\t%d", (int) stats[i][j].clean, + (int) stats[i][j].dirty, + (int) stats[i][j].pending_eviction, + (int) stats[i][j].discarded); printf ("\n"); } diff --git a/benchmarks/boehm-gc/ChangeLog b/benchmarks/boehm-gc/ChangeLog index ba04584..ae9ad6d 100644 --- a/benchmarks/boehm-gc/ChangeLog +++ b/benchmarks/boehm-gc/ChangeLog @@ -1,3 +1,9 @@ +2008-06-27 Neal H. Walfield <neal@gnu.org> + + * patches/05-viengoos-scheduler.patch: Update to use activity_info + in place of activity_stats. Listen for pressure messages and act + appropriately. Tighten adaptive code. Improve profiling code. + 2008-06-26 Neal H. Walfield <neal@gnu.org> * Makefile.am (noinst_LIBRARIES): Remove target. diff --git a/benchmarks/boehm-gc/patches/05-viengoos-scheduler.patch b/benchmarks/boehm-gc/patches/05-viengoos-scheduler.patch index 816103d..824eba9 100644 --- a/benchmarks/boehm-gc/patches/05-viengoos-scheduler.patch +++ b/benchmarks/boehm-gc/patches/05-viengoos-scheduler.patch @@ -5,9 +5,11 @@ exit $? Patch to support the Viengoos specific scheduler. To disable it, set GC_viengoos_scheduler to 0. -diff -upr gc-7.0/allchblk.c gc/allchblk.c ---- gc-7.0/allchblk.c 2007-06-07 02:40:07.000000000 +0200 -+++ gc/allchblk.c 2008-06-22 13:13:44.000000000 +0200 + +Only in gc: 05-viengoos-scheduler.patch.applied +diff -upr -x Makefile.in -x configure -x config.guess -x config.sub -x aclocal.m4 gc-old/allchblk.c gc/allchblk.c +--- gc-old/allchblk.c 2007-06-07 02:40:07.000000000 +0200 ++++ gc/allchblk.c 2008-06-27 17:01:49.000000000 +0200 @@ -117,7 +117,9 @@ void GC_print_hblkfreelist() while (h != 0) { hhdr = HDR(h); @@ -19,7 +21,7 @@ diff -upr gc-7.0/allchblk.c gc/allchblk.c total_free += sz; if (GC_is_black_listed(h, HBLKSIZE) != 0) { GC_printf("start black listed\n"); -@@ -381,31 +383,130 @@ void GC_add_to_fl(struct hblk *h, hdr *h +@@ -381,31 +383,135 @@ void GC_add_to_fl(struct hblk *h, hdr *h #ifdef USE_MUNMAP @@ -46,10 +48,15 @@ diff -upr gc-7.0/allchblk.c gc/allchblk.c + start: + if (GC_viengoos_scheduler + && (GC_get_heap_size() - GC_unmapped_bytes -+ < 6 * (GC_available_bytes / 8))) { -+ /* The number of mapped bytes is at most 6/8s the available ++ < 7 * (GC_available_bytes / 8))) { ++ /* The number of mapped bytes is at most 7/8s the available + memory. That's good enough for now. (Recall: the high-water -+ mark is 7/8s the available memory.) */ ++ mark is 15/16s the available memory.) */ ++ if (0) ++ printf ("%x: After unmapping %d used (%d available)\n", ++ l4_myself (), ++ (GC_get_heap_size() - GC_unmapped_bytes) / PAGESIZE, ++ GC_available_bytes / PAGESIZE); + return; + } + @@ -160,7 +167,7 @@ diff -upr gc-7.0/allchblk.c gc/allchblk.c } /* Merge all unmapped blocks that are adjacent to other free */ -@@ -413,6 +514,12 @@ void GC_unmap_old(void) +@@ -413,6 +519,12 @@ void GC_unmap_old(void) /* fully mapped or fully unmapped. */ void GC_merge_unmapped(void) { @@ -173,7 +180,7 @@ diff -upr gc-7.0/allchblk.c gc/allchblk.c struct hblk * h, *next; hdr * hhdr, *nexthdr; word size, nextsize; -@@ -600,12 +707,26 @@ GC_allochblk_nth(size_t sz, int kind, un +@@ -600,12 +712,26 @@ GC_allochblk_nth(size_t sz, int kind, un size_needed = HBLKSIZE * OBJ_SZ_TO_BLOCKS(sz); @@ -200,7 +207,7 @@ diff -upr gc-7.0/allchblk.c gc/allchblk.c if (size_avail != size_needed && !GC_use_entire_heap && !GC_dont_gc -@@ -642,7 +763,7 @@ GC_allochblk_nth(size_t sz, int kind, un +@@ -642,7 +768,7 @@ GC_allochblk_nth(size_t sz, int kind, un next_size = (signed_word)(thishdr -> hb_sz); if (next_size < size_avail && next_size >= size_needed @@ -209,7 +216,7 @@ diff -upr gc-7.0/allchblk.c gc/allchblk.c continue; } } -@@ -748,10 +869,12 @@ GC_allochblk_nth(size_t sz, int kind, un +@@ -748,10 +874,12 @@ GC_allochblk_nth(size_t sz, int kind, un /* hbp may be on the wrong freelist; the parameter n */ /* is important. */ hbp = GC_get_first_part(hbp, hhdr, size_needed, n); @@ -223,9 +230,10 @@ diff -upr gc-7.0/allchblk.c gc/allchblk.c if (0 == hbp) return 0; /* Add it to map of valid blocks */ -diff -upr -x config.guess -x config.sub -x Makefile -x Makefile.in gc-7.0/alloc.c gc/alloc.c ---- gc-7.0/alloc.c 2008-06-24 18:14:01.000000000 +0200 -+++ gc/alloc.c 2008-06-24 17:35:59.000000000 +0200 +Only in gc: allchblk.c~ +diff -upr -x Makefile.in -x configure -x config.guess -x config.sub -x aclocal.m4 gc-old/alloc.c gc/alloc.c +--- gc-old/alloc.c 2008-06-27 17:02:33.000000000 +0200 ++++ gc/alloc.c 2008-06-27 17:01:08.000000000 +0200 @@ -15,6 +15,13 @@ * */ @@ -240,24 +248,140 @@ diff -upr -x config.guess -x config.sub -x Makefile -x Makefile.in gc-7.0/alloc. # include "private/gc_priv.h" -@@ -34,8 +41,7 @@ now (void) +@@ -24,68 +31,7 @@ + # include <sys/types.h> + # endif - if (gettimeofday( &t, &tz ) == -1) - return 0; +-uint64_t gc_time; +- +-static inline uint64_t +-now (void) +-{ +- struct timeval t; +- struct timezone tz; +- +- if (gettimeofday( &t, &tz ) == -1) +- return 0; - return (t.tv_sec * 1000000 + t.tv_usec); - -+ return (t.tv_sec * 1000000ULL + t.tv_usec); - } +-} +- +-static int timing; +- +-static uint64_t +-start_timing (const char *func, int lineno) +-{ +- static const char *timing_func; +- static int timing_lineno; +- +- if (timing) +- { +- printf ("Timing for %s:%d but start_timing called from %s:%d\n", +- timing_func, timing_lineno, func, lineno); +- assert (! timing); +- } +- +- timing_func = func; +- timing_lineno = lineno; +- +- timing = 1; +- +- return now (); +-} +- +-static void +-end_timing (const char *func, int lineno, uint64_t start) +-{ +- uint64_t end = now (); +- +- if (! timing) +- { +- printf ("Timing not started by end_timing called from %s:%d!\n", +- func, lineno); +- assert (! timing); +- } +- timing = 0; +- +- assert (start <= end); +- gc_time += end - start; +-} +- +-#define start_timing() \ +- do { \ +- uint64_t start = start_timing (__FUNCTION__, __LINE__) +- +-#define end_timing() \ +- end_timing (__FUNCTION__, __LINE__, start); \ +- } while (0) +- ++#include "profile.h" - static int timing; -@@ -286,9 +292,106 @@ void GC_clear_a_few_frames() + /* + * Separate free lists are maintained for different sized objects +@@ -286,9 +232,134 @@ void GC_clear_a_few_frames() /* limits used by blacklisting. */ static word GC_collect_at_heapsize = (word)(-1); +int GC_viengoos_scheduler = 1; -+int GC_available_bytes; ++int GC_available_bytes = (256 + 128) * 1024 * 1024; +int GC_could_unmap; + ++#define THRESHOLD (15 * (GC_available_bytes / 16)) ++ ++#ifdef __gnu_hurd_viengoos__ ++static void * ++gather_stats (void *arg) ++{ ++ pthread_detach (pthread_self ()); ++ ++ int period = 0; ++ for (;;) ++ { ++ struct activity_info info; ++ error_t err = rm_activity_info (ACTIVITY, ++ activity_info_stats ++ | activity_info_pressure, ++ period, &info); ++ assert_perror (err); ++ ++ switch (info.event) ++ { ++ case activity_info_stats: ++ GC_available_bytes = info.stats.stats[0].available_local * PAGESIZE; ++ period = info.stats.stats[0].period + 1; ++ break; ++ ++ case activity_info_pressure: ++ if (-info.pressure.amount * PAGESIZE < GC_available_bytes) ++ GC_available_bytes -= -info.pressure.amount * PAGESIZE; ++ else ++ /* Huh? */ ++ GC_available_bytes = 0; ++ break; ++ ++ default: ++ panic ("Unknown event and unrequested event: %d", info.event); ++ } ++ ++ if (0) ++ printf ("%x: %s: %d alloced: %d, heap: %d, " ++ "mapped:%d, unmapped: %d, available: %d, " ++ "low-water: %d\n", ++ l4_myself (), ++ info.event == activity_info_stats ++ ? "Period" : DEBUG_BOLD ("PRESSURE"), ++ (int) (info.event == activity_info_stats ++ ? period : info.pressure.amount), ++ (int) GC_adj_bytes_allocd() / PAGESIZE, ++ (int) GC_get_heap_size () / PAGESIZE, ++ (int) (GC_get_heap_size () - GC_unmapped_bytes) / PAGESIZE, ++ (int) GC_unmapped_bytes / PAGESIZE, ++ (int) GC_available_bytes / PAGESIZE, ++ (int) THRESHOLD / PAGESIZE); ++ } ++} ++#endif ++ /* Have we allocated enough to amortize a collection? */ GC_bool GC_should_collect(void) { @@ -273,51 +397,23 @@ diff -upr -x config.guess -x config.sub -x Makefile -x Makefile.in gc-7.0/alloc. + collection, don't do a collection. */ + return FALSE; + -+ static uint64_t last_check; -+ +#ifdef __gnu_hurd_viengoos__ -+ static struct activity_stats_buffer stats; -+ -+ struct timeval tp; -+ gettimeofday (&tp, NULL); -+ uint64_t t = tp.tv_sec * 1000000ULL + tp.tv_usec; -+ if (! last_check || t - last_check > 1000000) -+ /* Last check was more than a second ago. */ ++ static int init; ++ if (! init) + { -+ last_check = t; -+ -+ int count; -+ error_t err = rm_activity_stats (ACTIVITY, 1, &stats, &count); ++ pthread_t tid; ++ error_t err = pthread_create (&tid, NULL, gather_stats, NULL); + assert_perror (err); + -+ GC_available_bytes = stats.stats[0].available * PAGESIZE; -+ -+ if (0) -+ printf ("Period: %d alloced: %dkb, heap: %dkb, " -+ "mapped:%dkb, unmapped: %dkb, available: %dkb, " -+ "low-water: %dkb\n", -+ (int) stats.stats[0].period, -+ (int) alloced / 1024, -+ (int) GC_get_heap_size () / 1024, -+ (int) (GC_get_heap_size () - GC_unmapped_bytes) / 1024, -+ (int) GC_unmapped_bytes / 1024, -+ (int) GC_available_bytes / 1024, -+ (int) 7 * (GC_available_bytes / 8 / 1024)); ++ init = 1; + } -+ -+ int period = stats.stats[0].period; -+#else -+#define GC_available_bytes ((256 + 128) * 1024 * 1024) -+ int period = 0; +#endif + -+ /* Do some clean up if the mapped memory is 7/8s the available -+ memory. We choose 7/8s as we need to consider meta-data -+ overhead and as we really want to avoid the system pager -+ kicking in. */ -+ GC_bool r; -+ r = ((GC_get_heap_size() - GC_unmapped_bytes) -+ > 7 * (GC_available_bytes / 8)); ++ /* Do some clean up if the mapped memory is more than 15/16s the ++ available memory. We choose 15/16s as we need to consider ++ meta-data overhead and as we really want to avoid the system ++ pager kicking in. */ ++ GC_bool r = GC_get_heap_size() - GC_unmapped_bytes > THRESHOLD; + + if (r && (GC_get_heap_size () - GC_unmapped_bytes - alloced + > GC_available_bytes / 3)){ @@ -325,8 +421,8 @@ diff -upr -x config.guess -x config.sub -x Makefile -x Makefile.in gc-7.0/alloc. + of the total available memory. Before doing a GC, try to + unmap some free mapped pages. */ + GC_unmap_old (); -+ r = ((GC_get_heap_size() - GC_unmapped_bytes) -+ > 7 * (GC_available_bytes / 8)); ++ ++ r = GC_get_heap_size() - GC_unmapped_bytes > THRESHOLD; + } + + if (r) { @@ -337,17 +433,17 @@ diff -upr -x config.guess -x config.sub -x Makefile -x Makefile.in gc-7.0/alloc. + if (warning != GC_gc_no) { + warning = GC_gc_no; + if (0) -+ printf ("(%d) Scheduling GC: (%d) alloced: %dkb, heap: %dkb, " ++ printf ("Scheduling GC: (%u) alloced: %dkb, heap: %dkb, " + "mapped: %dkb, unmapped: %dkb, available: %dkb, " + "low-water: %dkb\n", -+ (int) period, GC_gc_no, ++ GC_gc_no, + (int) alloced / 1024, + (int) GC_get_heap_size () / 1024, + (int) (GC_get_heap_size () - GC_unmapped_bytes) + / 1024, + (int) GC_unmapped_bytes / 1024, + (int) GC_available_bytes / 1024, -+ (int) 7 * (GC_available_bytes / 8 / 1024)); ++ (int) THRESHOLD); + } + } + @@ -357,7 +453,38 @@ diff -upr -x config.guess -x config.sub -x Makefile -x Makefile.in gc-7.0/alloc. return(GC_adj_bytes_allocd() >= min_bytes_allocd() || GC_heapsize >= GC_collect_at_heapsize); } -@@ -396,9 +499,17 @@ GC_bool GC_try_to_collect_inner(GC_stop_ +@@ -319,7 +390,7 @@ void GC_maybe_gc(void) + n_partial_gcs = 0; + return; + } else { +- start_timing (); ++ profile_start (GC_TIMER); + + # ifdef PARALLEL_MARK + GC_wait_for_reclaim(); +@@ -342,10 +413,10 @@ void GC_maybe_gc(void) + n_partial_gcs++; + } + +- end_timing (); ++ profile_end (GC_TIMER); + } + +- start_timing (); ++ profile_start (GC_TIMER); + /* We try to mark with the world stopped. */ + /* If we run out of time, this turns into */ + /* incremental marking. */ +@@ -365,7 +436,7 @@ void GC_maybe_gc(void) + } + } + +- end_timing (); ++ profile_end (GC_TIMER); + } + } + +@@ -396,10 +467,18 @@ GC_bool GC_try_to_collect_inner(GC_stop_ GC_log_printf( "Initiating full world-stop collection %lu after %ld allocd bytes\n", (unsigned long)GC_gc_no+1, (long)GC_bytes_allocd); @@ -372,11 +499,59 @@ diff -upr -x config.guess -x config.sub -x Makefile -x Makefile.in gc-7.0/alloc. } - GC_bool ret = true; +- start_timing (); + GC_bool ret = TRUE; - start_timing (); ++ profile_start (GC_TIMER); GC_promote_black_lists(); -@@ -1110,6 +1221,8 @@ ptr_t GC_allocobj(size_t gran, int kind) + /* Make sure all blocks have been reclaimed, so sweep routines */ +@@ -414,7 +493,7 @@ GC_bool GC_try_to_collect_inner(GC_stop_ + && !GC_reclaim_all(stop_func, FALSE)) { + /* Aborted. So far everything is still consistent. */ + ret = FALSE; +- break; ++ goto out; + } + GC_invalidate_mark_state(); /* Flush mark stack. */ + GC_clear_marks(); +@@ -432,7 +511,7 @@ GC_bool GC_try_to_collect_inner(GC_stop_ + } /* else we claim the world is already still consistent. We'll */ + /* finish incrementally. */ + ret = FALSE; +- break; ++ goto out; + } + GC_finish_collection(); + if (GC_print_stats) { +@@ -441,7 +520,8 @@ GC_bool GC_try_to_collect_inner(GC_stop_ + MS_TIME_DIFF(current_time,start_time)); + } + +- end_timing (); ++ out: ++ profile_end (GC_TIMER); + return ret; + } + +@@ -470,7 +550,7 @@ void GC_collect_a_little_inner(int n) + + if (GC_dont_gc) return; + if (GC_incremental && GC_collection_in_progress()) { +- start_timing (); ++ profile_start (GC_TIMER); + + for (i = GC_deficit; i < GC_RATE*n; i++) { + if (GC_mark_some((ptr_t)0)) { +@@ -498,7 +578,7 @@ void GC_collect_a_little_inner(int n) + if (GC_deficit > 0) GC_deficit -= GC_RATE*n; + if (GC_deficit < 0) GC_deficit = 0; + +- end_timing (); ++ profile_end (GC_TIMER); + } else { + GC_maybe_gc(); + } +@@ -1110,6 +1190,8 @@ ptr_t GC_allocobj(size_t gran, int kind) void GC_dump_stats (void) { @@ -385,39 +560,139 @@ diff -upr -x config.guess -x config.sub -x Makefile -x Makefile.in gc-7.0/alloc. printf ("GC: " #ifdef THREADS "multi-threaded" -@@ -1125,8 +1238,9 @@ GC_dump_stats (void) +@@ -1125,10 +1207,123 @@ GC_dump_stats (void) GC_incremental ? "generational" : "stop the world", GC_dirty_maintained ? "" : "not "); printf("%d collections\n", (int) GC_gc_no); - printf ("%lld.%03lld seconds spent collecting\n", - gc_time / 1000000, (gc_time % 1000000) / 1000); -+ printf ("%lld.%03lld seconds spent collecting, %lld.%03lld mapping\n", -+ gc_time / 1000000, (gc_time % 1000000) / 1000, -+ gc_map_time / 1000000, (gc_map_time % 1000000) / 1000); printf("Heap size: %d (%d kb)\n", GC_get_heap_size(), GC_get_heap_size() / 1024); printf("Total bytes allocated: %d (%d kb)\n", -diff -upr -x config.guess -x config.sub -x Makefile -x Makefile.in gc-7.0/configure gc/configure ---- gc-7.0/configure 2007-06-30 17:40:25.000000000 +0200 -+++ gc/configure 2008-06-23 22:12:43.000000000 +0200 -@@ -21558,14 +21558,6 @@ fi - - if test -n "${with_cross_host}"; then - cat >>confdefs.h <<\_ACEOF --#define NO_CLOCK 1 --_ACEOF -- -- cat >>confdefs.h <<\_ACEOF --#define SMALL_CONFIG 1 --_ACEOF -- -- cat >>confdefs.h <<\_ACEOF - #define NO_DEBUGGING 1 - _ACEOF - -diff -upr -x config.guess -x config.sub -x Makefile -x Makefile.in gc-7.0/include/private/gcconfig.h gc/include/private/gcconfig.h ---- gc-7.0/include/private/gcconfig.h 2008-06-24 18:14:01.000000000 +0200 -+++ gc/include/private/gcconfig.h 2008-06-23 22:12:34.000000000 +0200 + GC_get_total_bytes (), GC_get_total_bytes () / 1024); ++ ++ profile_stats_dump (); ++} ++ ++/* profile.c - Profiling support implementation. ++ Copyright (C) 2008 Free Software Foundation, Inc. ++ Written by Neal H. Walfield <neal@gnu.org>. ++ ++ This file is part of the GNU Hurd. ++ ++ The GNU Hurd is free software; you can redistribute it and/or ++ modify it under the terms of the GNU General Public License as ++ published by the Free Software Foundation; either version 3 of the ++ License, or (at your option) any later version. ++ ++ The GNU Hurd is distributed in the hope that it will be useful, but ++ WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program. If not, see ++ <http://www.gnu.org/licenses/>. */ ++ ++#define SIZE 10 ++struct site ++{ ++ const char *name; ++ uint64_t time; ++ uint64_t start; ++ int calls; ++ int pending; ++} sites[SIZE]; ++ ++static uint64_t epoch; ++static uint64_t calls; ++static uint64_t total_time; ++/* Number of extant profiling calls. We only update total_time if ++ EXTANT is 0. The result is that the time spent profiling is ++ correct, and the percent of the time profile that a function has ++ been is more meaningful. */ ++static int extant; ++ ++static inline uint64_t ++now (void) ++{ ++ struct timeval t; ++ struct timezone tz; ++ ++ if (gettimeofday( &t, &tz ) == -1) ++ return 0; ++ return (t.tv_sec * 1000000ULL + t.tv_usec); ++} ++ ++void ++profile_stats_dump (void) ++{ ++ uint64_t n = now (); ++ ++ int i; ++ for (i = 0; i < SIZE; i ++) ++ if (sites[i].calls) ++ printf ("%s:\t%d calls,\t%lld ms,\t%lld.%d us per call,\t" ++ "%d%% total time,\t%d%% profiled time\n", ++ sites[i].name, ++ sites[i].calls, ++ sites[i].time / 1000, ++ sites[i].time / sites[i].calls, ++ (int) ((10 * sites[i].time) / sites[i].calls) % 10, ++ (int) ((100 * sites[i].time) / (n - epoch)), ++ (int) ((100 * sites[i].time) / total_time)); ++ ++ printf ("profiled time: %lld ms, calls: %lld\n", ++ total_time / 1000, calls); ++ printf ("uptime: %lld ms\n", (n - epoch) / 1000); ++} ++ ++void ++profile_start (uintptr_t id, const char *name) ++{ ++ if (! epoch) ++ epoch = now (); ++ ++ struct site *site = &sites[id]; ++ site->name = name; ++ ++ extant ++; ++ ++ site->pending ++; ++ if (site->pending == 1) ++ site->start = now (); + } ++ ++void ++profile_end (uintptr_t id, const char *name) ++{ ++ struct site *site = &sites[id]; ++ assert (site->pending); ++ ++ extant --; ++ ++ site->pending --; ++ if (site->pending == 0) ++ { ++ uint64_t n = now (); ++ ++ site->time += n - site->start; ++ ++ if (extant == 0) ++ total_time += n - site->start; ++ ++ site->calls ++; ++ calls ++; ++ } ++} ++ +Only in gc: alloc.c~ +Only in gc: atomic_ops.c +Only in gc: atomic_ops_sysdeps.S +Only in gc: autom4te.cache +diff -upr -x Makefile.in -x configure -x config.guess -x config.sub -x aclocal.m4 gc-old/include/private/gcconfig.h gc/include/private/gcconfig.h +--- gc-old/include/private/gcconfig.h 2008-06-27 17:02:33.000000000 +0200 ++++ gc/include/private/gcconfig.h 2008-06-26 10:41:11.000000000 +0200 @@ -1087,6 +1087,8 @@ # ifdef LINUX # define OS_TYPE "LINUX" @@ -440,9 +715,16 @@ diff -upr -x config.guess -x config.sub -x Makefile -x Makefile.in gc-7.0/includ # endif # ifdef DARWIN # define OS_TYPE "DARWIN" -diff -upr -x config.guess -x config.sub -x Makefile -x Makefile.in gc-7.0/malloc.c gc/malloc.c ---- gc-7.0/malloc.c 2007-06-06 22:48:35.000000000 +0200 -+++ gc/malloc.c 2008-06-23 22:12:34.000000000 +0200 +Only in gc: libatomic_ops +Only in gc/libatomic_ops-1.2: .cvsignore +Only in gc/libatomic_ops-1.2/doc: .cvsignore +Only in gc/libatomic_ops-1.2/src/atomic_ops: .cvsignore +Only in gc/libatomic_ops-1.2/src/atomic_ops/sysdeps: .cvsignore +Only in gc/libatomic_ops-1.2/src: .cvsignore +Only in gc/libatomic_ops-1.2/tests: .cvsignore +diff -upr -x Makefile.in -x configure -x config.guess -x config.sub -x aclocal.m4 gc-old/malloc.c gc/malloc.c +--- gc-old/malloc.c 2007-06-06 22:48:35.000000000 +0200 ++++ gc/malloc.c 2008-06-26 10:41:13.000000000 +0200 @@ -269,7 +269,9 @@ void * GC_generic_malloc(size_t lb, int LOCK(); if( EXPECT((op = *opp) == 0, 0) ) { @@ -468,10 +750,10 @@ diff -upr -x config.guess -x config.sub -x Makefile -x Makefile.in gc-7.0/malloc } } -diff -upr -x config.guess -x config.sub -x Makefile -x Makefile.in gc-7.0/os_dep.c gc/os_dep.c ---- gc-7.0/os_dep.c 2007-06-29 21:17:44.000000000 +0200 -+++ gc/os_dep.c 2008-06-24 17:35:30.000000000 +0200 -@@ -2064,6 +2064,75 @@ ptr_t GC_unmap_end(ptr_t start, size_t b +diff -upr -x Makefile.in -x configure -x config.guess -x config.sub -x aclocal.m4 gc-old/os_dep.c gc/os_dep.c +--- gc-old/os_dep.c 2007-06-29 21:17:44.000000000 +0200 ++++ gc/os_dep.c 2008-06-26 10:41:13.000000000 +0200 +@@ -2064,6 +2064,18 @@ ptr_t GC_unmap_end(ptr_t start, size_t b return end_addr; } @@ -485,73 +767,16 @@ diff -upr -x config.guess -x config.sub -x Makefile -x Makefile.in gc-7.0/os_dep + +#endif + -+uint64_t gc_map_time; -+ -+static inline uint64_t -+now (void) -+{ -+ struct timeval t; -+ struct timezone tz; -+ -+ if (gettimeofday( &t, &tz ) == -1) -+ return 0; -+ return (t.tv_sec * 1000000ULL + t.tv_usec); -+ -+} -+ -+static int timing; -+ -+static uint64_t -+start_timing (const char *func, int lineno) -+{ -+ static const char *timing_func; -+ static int timing_lineno; -+ -+ if (timing) -+ panic ("Timing for %s:%d but start_timing called from %s:%d\n", -+ timing_func, timing_lineno, func, lineno); -+ -+ timing_func = func; -+ timing_lineno = lineno; -+ -+ timing = 1; -+ -+ return now (); -+} -+ -+static void -+end_timing (const char *func, int lineno, uint64_t start) -+{ -+ uint64_t end = now (); -+ -+ if (! timing) -+ panic ("Timing not started by end_timing called from %s:%d!\n", -+ func, lineno); -+ -+ timing = 0; -+ -+ if (start > end) -+ panic ("%lld > %lld!", start, end); -+ gc_map_time += end - start; -+} -+ -+#define start_timing() \ -+ do { \ -+ uint64_t __start = start_timing (__FUNCTION__, __LINE__) -+ -+#define end_timing() \ -+ end_timing (__FUNCTION__, __LINE__, __start); \ -+ } while (0) -+ ++#include "profile.h" + /* Under Win32/WinCE we commit (map) and decommit (unmap) */ /* memory using VirtualAlloc and VirtualFree. These functions */ /* work on individual allocations of virtual memory, made */ -@@ -2077,10 +2146,12 @@ ptr_t GC_unmap_end(ptr_t start, size_t b +@@ -2077,10 +2089,12 @@ ptr_t GC_unmap_end(ptr_t start, size_t b /* round the endpoints in both places. */ void GC_unmap(ptr_t start, size_t bytes) { -+ start_timing (); ++ profile_start (MAP_TIMER); + ptr_t start_addr = GC_unmap_start(start, bytes); ptr_t end_addr = GC_unmap_end(start, bytes); @@ -561,7 +786,7 @@ diff -upr -x config.guess -x config.sub -x Makefile -x Makefile.in gc-7.0/os_dep # if defined(MSWIN32) || defined(MSWINCE) while (len != 0) { MEMORY_BASIC_INFORMATION mem_info; -@@ -2095,6 +2166,9 @@ void GC_unmap(ptr_t start, size_t bytes) +@@ -2095,6 +2109,9 @@ void GC_unmap(ptr_t start, size_t bytes) start_addr += free_len; len -= free_len; } @@ -571,19 +796,19 @@ diff -upr -x config.guess -x config.sub -x Makefile -x Makefile.in gc-7.0/os_dep # else /* We immediately remap it to prevent an intervening mmap from */ /* accidentally grabbing the same address space. */ -@@ -2107,19 +2181,25 @@ void GC_unmap(ptr_t start, size_t bytes) +@@ -2107,19 +2124,25 @@ void GC_unmap(ptr_t start, size_t bytes) } GC_unmapped_bytes += len; # endif + + out: -+ end_timing (); ++ profile_end (MAP_TIMER); } void GC_remap(ptr_t start, size_t bytes) { -+ start_timing (); ++ profile_start (MAP_TIMER); + ptr_t start_addr = GC_unmap_start(start, bytes); ptr_t end_addr = GC_unmap_end(start, bytes); @@ -598,7 +823,7 @@ diff -upr -x config.guess -x config.sub -x Makefile -x Makefile.in gc-7.0/os_dep while (len != 0) { MEMORY_BASIC_INFORMATION mem_info; GC_word alloc_len; -@@ -2137,11 +2217,14 @@ void GC_remap(ptr_t start, size_t bytes) +@@ -2137,11 +2160,14 @@ void GC_remap(ptr_t start, size_t bytes) start_addr += alloc_len; len -= alloc_len; } @@ -614,26 +839,26 @@ diff -upr -x config.guess -x config.sub -x Makefile -x Makefile.in gc-7.0/os_dep result = mprotect(start_addr, len, PROT_READ | PROT_WRITE | OPT_PROT_EXEC); if (result != 0) { -@@ -2152,6 +2235,9 @@ void GC_remap(ptr_t start, size_t bytes) +@@ -2152,6 +2178,9 @@ void GC_remap(ptr_t start, size_t bytes) } GC_unmapped_bytes -= len; # endif + + out: -+ end_timing (); ++ profile_end (MAP_TIMER); } /* Two adjacent blocks have already been unmapped and are about to */ -@@ -2160,6 +2246,8 @@ void GC_remap(ptr_t start, size_t bytes) +@@ -2160,6 +2189,8 @@ void GC_remap(ptr_t start, size_t bytes) /* unmapped due to alignment constraints. */ void GC_unmap_gap(ptr_t start1, size_t bytes1, ptr_t start2, size_t bytes2) { -+ start_timing (); ++ profile_start (MAP_TIMER); + ptr_t start1_addr = GC_unmap_start(start1, bytes1); ptr_t end1_addr = GC_unmap_end(start1, bytes1); ptr_t start2_addr = GC_unmap_start(start2, bytes2); -@@ -2170,7 +2258,7 @@ void GC_unmap_gap(ptr_t start1, size_t b +@@ -2170,7 +2201,7 @@ void GC_unmap_gap(ptr_t start1, size_t b GC_ASSERT(start1 + bytes1 == start2); if (0 == start1_addr) start_addr = GC_unmap_start(start1, bytes1 + bytes2); if (0 == start2_addr) end_addr = GC_unmap_end(start1, bytes1 + bytes2); @@ -642,19 +867,22 @@ diff -upr -x config.guess -x config.sub -x Makefile -x Makefile.in gc-7.0/os_dep len = end_addr - start_addr; # if defined(MSWIN32) || defined(MSWINCE) while (len != 0) { -@@ -2190,6 +2278,9 @@ void GC_unmap_gap(ptr_t start1, size_t b +@@ -2190,6 +2221,9 @@ void GC_unmap_gap(ptr_t start1, size_t b if (len != 0 && munmap(start_addr, len) != 0) ABORT("munmap failed"); GC_unmapped_bytes += len; # endif + + out: -+ end_timing (); ++ profile_end (MAP_TIMER); } #endif /* USE_MUNMAP */ -diff -upr -x config.guess -x config.sub -x Makefile -x Makefile.in gc-7.0/tests/test.c gc/tests/test.c ---- gc-7.0/tests/test.c 2007-06-19 00:18:01.000000000 +0200 -+++ gc/tests/test.c 2008-06-23 22:12:34.000000000 +0200 +Only in gc: os_dep.c~ +Only in gc: patch.stamp +Only in gc: profile.h +diff -upr -x Makefile.in -x configure -x config.guess -x config.sub -x aclocal.m4 gc-old/tests/test.c gc/tests/test.c +--- gc-old/tests/test.c 2007-06-19 00:18:01.000000000 +0200 ++++ gc/tests/test.c 2008-06-26 10:41:14.000000000 +0200 @@ -1334,6 +1334,18 @@ void SetMinimumStack(long minSize) # endif # endif @@ -674,4 +902,4 @@ diff -upr -x config.guess -x config.sub -x Makefile -x Makefile.in gc-7.0/tests/ run_one_test(); check_heap_stats(); # ifndef MSWINCE - +Only in gc: unpack.stamp diff --git a/benchmarks/shared-memory-distribution.c b/benchmarks/shared-memory-distribution.c index 4043718..04b37c2 100644 --- a/benchmarks/shared-memory-distribution.c +++ b/benchmarks/shared-memory-distribution.c @@ -62,14 +62,24 @@ main (int argc, char *argv[]) int count = 0; while (! terminate) { - uintptr_t *p = buffer + (rand () % PAGES) * PAGESIZE; - t = *p; - - count ++; - if (count % 100000 == 0) - debug (0, DEBUG_BOLD ("Read %d pages so far"), count); - + int i; + for (i = 0; i < 1; i ++) + { + uintptr_t *p = buffer + (rand () % PAGES) * PAGESIZE; + t += *p; + + count ++; + + if (count % 100000 == 0) + debug (0, DEBUG_BOLD ("Read %d pages so far"), count); + } + +#if 0 + /* ~128Hz. */ + l4_sleep (l4_time_period (1 << 13)); +#elif 1 l4_thread_switch (tids[rand () % THREADS]); +#endif } printf ("Thread %d: %d operations\n", w, count); @@ -98,24 +108,26 @@ main (int argc, char *argv[]) { debug (0, DEBUG_BOLD ("starting iteration %d (%x)"), i, l4_myself ()); - int count; - struct activity_stats_buffer buffer; + struct activity_info info; - rm_activity_stats (activity, next_period, &buffer, &count); - assert (count > 0); + rm_activity_info (activity, activity_info_stats, next_period, &info); + assert (info.event == activity_info_stats); + assert (info.stats.count > 0); if (i != 0) - assertx (buffer.stats[0].period != stats[i - 1][0].period, + assertx (info.stats.stats[0].period != stats[i - 1][0].period, "%x != %x", - buffer.stats[0].period, stats[i - 1][0].period); + info.stats.stats[0].period, stats[i - 1][0].period); - stats[i][0] = buffer.stats[0]; + stats[i][0] = info.stats.stats[0]; int j; for (j = 0; j < THREADS; j ++) { - rm_activity_stats (activities[j], next_period, &buffer, &count); - assert (count > 0); - stats[i][1 + j] = buffer.stats[0]; + rm_activity_info (activity, activity_info_stats, next_period, + &info); + assert (info.event == activity_info_stats); + assert (info.stats.count > 0); + stats[i][1 + j] = info.stats.stats[0]; } next_period = stats[i][0].period + 1; @@ -137,10 +149,15 @@ main (int argc, char *argv[]) { int j; - printf ("%d ", (int) stats[i][0].period); + printf ("%d", (int) stats[i][0].period); for (j = 0; j < 1 + THREADS; j ++) - printf ("%d ", (int) stats[i][j].clean + (int) stats[i][j].dirty); + printf ("\t%d\t%d\t%d\t%d\t%d\t%d", (int) stats[i][j].clean, + (int) stats[i][j].dirty, + (int) stats[i][j].pending_eviction, + (int) stats[i][j].discarded, + (int) stats[i][j].freeloading, + (int) stats[i][j].freeloaded); printf ("\n"); } diff --git a/hieronymus/ChangeLog b/hieronymus/ChangeLog index 32cd062..37b8e33 100644 --- a/hieronymus/ChangeLog +++ b/hieronymus/ChangeLog @@ -1,3 +1,10 @@ +2008-06-27 Neal H. Walfield <neal@gnu.org> + + * hieronymus.c (do_gather_stats): Change to use activity_info + instead of activity_stats. + + (main): Introduce code to delay process creation. + 2008-06-26 Neal H. Walfield <neal@gnu.org> * hieronymus.c: Include <stdlib.h>. diff --git a/hieronymus/hieronymus.c b/hieronymus/hieronymus.c index 4fac2d5..a1ecfc0 100644 --- a/hieronymus/hieronymus.c +++ b/hieronymus/hieronymus.c @@ -111,8 +111,7 @@ do_gather_stats (void *arg) while (! all_done) { - int count; - struct activity_stats_buffer buffer; + struct activity_info info; if (size == stats_count) { @@ -134,8 +133,11 @@ do_gather_stats (void *arg) for (i = 0; i < module_count; i ++, stat ++) { error_t err; - err = rm_activity_stats (activities[0], - period, &buffer, &count); + err = rm_activity_info (activities[i], activity_info_stats, + period, &info); + assert_perror (err); + assert (info.event == activity_info_stats); + assert (info.stats.count > 0); if (err) { stat->alloced = 0; @@ -143,13 +145,14 @@ do_gather_stats (void *arg) } else { - stat->alloced = buffer.stats[0].clean + buffer.stats[0].dirty - + buffer.stats[0].pending_eviction; - stat->available = buffer.stats[0].available; + stat->alloced = info.stats.stats[0].clean + + info.stats.stats[0].dirty + + info.stats.stats[0].pending_eviction; + stat->available = info.stats.stats[0].available; if (n == 0) - n = buffer.stats[0].period + 1; + n = info.stats.stats[0].period + 1; } } @@ -208,21 +211,30 @@ main (int argc, char *argv[]) addr_t thread[module_count]; for (i = 0; i < module_count; i ++) { - int count; - struct activity_stats_buffer buffer; + /* Delay starting each process. */ +#if 0 + error_t err; + struct activity_info info; + err = rm_activity_info (activities[i], activity_info_stats, + i * 50, &info); + assert_perror (err); + assert (info.event == activity_info_stats); + assert (info.stats.count > 0); + const char *argv[] = { modules[i].name, modules[i].commandline, NULL }; const char *env[] = { NULL }; thread[i] = process_spawn (activities[i], modules[i].start, modules[i].end, argv, env, false); +#endif /* Free the memory used by the module's binary. */ /* XXX: This doesn't free folios or note pages that may be partially freed. The latter is important because a page may be used by two modules and after the second module is loaded, it could be freed. */ - count = 0; + int count = 0; int j; for (j = 0; j < __hurd_startup_data->desc_count; j ++) { diff --git a/hurd/ChangeLog b/hurd/ChangeLog index 1df93a2..2e0c3f7 100644 --- a/hurd/ChangeLog +++ b/hurd/ChangeLog @@ -1,5 +1,18 @@ 2008-06-27 Neal H. Walfield <neal@gnu.org> + * activity.h (RM_activity_stats): Don't define. + (RM_activity_info): Replace with this. + (struct activity_stats): Add field pressure_local. Make field + pressure an 8 bit integer. + (struct activity_stats_buffer): Remove structure. + (struct activity_info): Replace with this structure. + (activity_info_stats): Define. + (activity_info_pressure): Likewise. + (activity_stats): Remove method. + (activity_info): Replace with this. + +2008-06-27 Neal H. Walfield <neal@gnu.org> + * stddef.h (do_debug): If DEBUG_ELIDE is defined to an integer, statically exclude those debug statements that have a level greater than or equal to it. diff --git a/hurd/activity.h b/hurd/activity.h index d26a233..5c3a1e5 100644 --- a/hurd/activity.h +++ b/hurd/activity.h @@ -28,7 +28,7 @@ enum { RM_activity_policy = 700, - RM_activity_stats, + RM_activity_info, }; struct activity_memory_policy @@ -79,10 +79,11 @@ struct activity_stats uint32_t available; uint32_t available_local; - /* The maximum amount of memory that the user of this activity ought - to allocate in the next few seconds. If negative, the amount of - memory the activity ought to consider freeing. */ - int32_t damping_factor; + /* Log2 the maximum amount of memory (in pages) that the user of + this activity ought to allocate in the next few seconds. If + negative, the amount of memory the activity ought to consider + freeing. */ + int8_t damping_factor; /* If pressure is non-zero, then this activity is causing PRESSURE. @@ -99,7 +100,8 @@ struct activity_stats system if it does not yield memory. However, if the activity has memory which is yielding a low return, it would be friendly of it to return it. */ - uint32_t pressure; + uint8_t pressure; + uint8_t pressure_local; /* The number of clean and dirty frames that are accounted to this activity. (Does not include frames scheduled for eviction.) The @@ -157,10 +159,6 @@ struct activity_stats evicted, then the process is trashing.) */ uint32_t saved; }; -struct activity_stats_buffer -{ - struct activity_stats stats[ACTIVITY_STATS_PERIODS]; -}; #define ACTIVITY_POLICY(__ap_sibling_rel, __ap_child_rel, __ap_storage) \ (struct activity_policy) { __ap_sibling_rel, __ap_child_rel, __ap_storage } @@ -201,17 +199,59 @@ RPC (activity_policy, 3, 1, addr_t, activity, /* Out: */ struct activity_policy, out); -/* Return statistics about activity ACTIVITY. Waits until the current - period is older than UNTIL_PERIOD. (This can be used to register a - callback that is sent when the statistics are next available. For - example, call with until_period is 0 to get the current statistics - and then examine the period field. Use this as the base for the - next call.) COUNT is the number of returned samples. Samples are - ordered by recency with the youngest towards the start of the - buffer. */ -RPC (activity_stats, 2, 2, addr_t, activity, uintptr_t, until_period, +enum + { + /* Return statistics. */ + activity_info_stats = 1 << 0, + /* Asynchronous change in availability. */ + activity_info_pressure = 1 << 1, + }; + +struct activity_info +{ + /* The returned event. */ + uintptr_t event; + union + { + /* If EVENT is activity_info_stats. */ + struct + { + /* The number of samples. */ + int count; + /* Samples are ordered by recency with the youngest towards the + start of the buffer. */ + struct activity_stats stats[ACTIVITY_STATS_PERIODS]; + } stats; + + /* If EVENT is activity_info_free. */ + struct + { + /* The number of pages the caller should try to free (negative) + or may allocate (positive). */ + int amount; + } pressure; + }; +}; + +/* Return some information about the activity ACTIVITY. FLAGS is a + bit-wise or of events the caller is interested. Only one event + will be returned. + + If FLAGS contains activity_info_stats, may return the next + statistic that comes at or after UNTIL_PERIOD. (This can be used + to register a callback that is sent when the statistics are next + available. For example, call with UNTIL_PERIOD equal to 0 to get + the current statistics and then examine the period field. Use this + as the base for the next call.) + + If FLAGS contains activity_info_free, may return an upcall + indicating that the activity must free some memory or will be such + subject to paging. In this case, the activity should try to free + at least the indicated number of pages as quickly as possible. */ +RPC (activity_info, 3, 1, addr_t, activity, + uintptr_t, flags, uintptr_t, until_period, /* Out: */ - struct activity_stats_buffer, stats, int, count) + struct activity_info, info) #undef RPC_STUB_PREFIX #undef RPC_ID_PREFIX diff --git a/ruth/ChangeLog b/ruth/ChangeLog index e492156..da4ce19 100644 --- a/ruth/ChangeLog +++ b/ruth/ChangeLog @@ -1,3 +1,8 @@ +2008-06-27 Neal H. Walfield <neal@gnu.org> + + * ruth.c (main): Update to use activity_info instead of + activity_stats. + 2008-06-20 Neal H. Walfield <neal@gnu.org> * ruth.c (visit): When encountering a folio, return -1 diff --git a/ruth/ruth.c b/ruth/ruth.c index 027240e..fe40d8c 100644 --- a/ruth/ruth.c +++ b/ruth/ruth.c @@ -961,16 +961,16 @@ main (int argc, char *argv[]) return true; } - struct activity_stats_buffer stats; uint32_t frames; do { - int c; - error_t err = rm_activity_stats (ADDR_VOID, 0, &stats, &c); + struct activity_info info; + error_t err = rm_activity_info (ADDR_VOID, activity_info_stats, 1, + &info); assert_perror (err); - assert (c >= 1); + assert (info.stats.count >= 1); - frames = stats.stats[0].available; + frames = info.stats.stats[0].available; } while (frames == 0); diff --git a/viengoos/ChangeLog b/viengoos/ChangeLog index b12a7e0..b0ef8d2 100644 --- a/viengoos/ChangeLog +++ b/viengoos/ChangeLog @@ -1,3 +1,29 @@ +2008-06-27 Neal H. Walfield <neal@gnu.org> + + * activity.h (struct activity): Add fields free_bad_karma, + free_goal, free_allocations and free_initial_allocation. + * rm.h (rm_method_id_string): Replace RM_activity_stats handling + with RM_activity_info handling. + * thread.h (THREAD_WAIT_STATS): Remove. + (THREAD_WAIT_ACTIVITY_INFO): Replace with this. + (struct thread): Add field wait_reason_arg2. + * ager.c (update_stats): Account for local pressure when + calculating availability. Don't use activity_stats to inform + threads of new statistics but activity_info. + * object.c (object_desc_claim): Adjust the activity's FREE_GOAL, + FREE_ALLOCATIONS, and FRAMES_EXCLUDED fields as appropriate. + * pager.c (pager_collect): When selecting a victim, don't include + an activity's excluded frames in its allocation. Don't even + consider activities for which FREE_ALLOCATIONS is non-zero. + Having selected a victim, don't increase the pressure do + drastically. Update the local pressure. Having selected a victim + from which to revoke pages, send any waiting threads a message to + free memory. If VICTIM->FREE_BAD_KARMA is 0, assume that the + memory will be freed and give the activity 100 claims to do it. + If the activity has bad karma, decrease it by one. + * server.c (server_loop): Replace activity_stats implementation + with implementation appropriate for activity_info. + 2008-06-26 Neal H. Walfield <neal@gnu.org> * viengoos.c (main): Assign viengoos_tid after we test that that diff --git a/viengoos/activity.h b/viengoos/activity.h index 38d9997..8ffcc94 100644 --- a/viengoos/activity.h +++ b/viengoos/activity.h @@ -115,6 +115,21 @@ struct activity unsigned char current_period; struct activity_stats stats[ACTIVITY_STATS_PERIODS + 1]; + /* When an activity is choosen to yield pages, we first send it a + signal asking it to free memory. We exclude it from eviction for + the next FREE_ALLOCATIONS allocations. If, after that many + allocations, the activity has not dropped below free_goal, we + simply start evicting. We also set its free bad karma. Each + time it is choosen for deallocation, we decrement it. Only when + FREE_BAD_KARMA is again zero do we give it this chance again. */ + uintptr_t free_bad_karma; + uintptr_t free_goal; + uintptr_t free_allocations; + uintptr_t free_initial_allocation; + /* The number of pages that are to be excluded from FRAMES_TOTAL + when looking for a process to page. */ + uintptr_t frames_excluded; + struct object_name name; }; diff --git a/viengoos/ager.c b/viengoos/ager.c index a7f8808..b6dd8b7 100644 --- a/viengoos/ager.c +++ b/viengoos/ager.c @@ -86,6 +86,8 @@ update_stats (void) ACTIVITY_STATS (activity)->pressure += ACTIVITY_STATS_LAST (activity)->pressure >> 1; + ACTIVITY_STATS (activity)->pressure_local += + ACTIVITY_STATS_LAST (activity)->pressure_local >> 1; if (activity->frames_total > frames || ACTIVITY_STATS (activity)->pressure) @@ -104,18 +106,7 @@ update_stats (void) frames, frames - dec); frames -= dec; - - ACTIVITY_STATS (activity)->damping_factor - = -1024 * 1024 / PAGESIZE; } - else if (activity->frames_total > 7 * (frames / 8)) - /* The allocated amount is close to the available frames. - Encourage it not to allocate. */ - ACTIVITY_STATS (activity)->damping_factor = 0; - else - /* It can allocate. */ - ACTIVITY_STATS (activity)->damping_factor - = 1024 * 1024 / PAGESIZE; ACTIVITY_STATS (activity)->available = frames; @@ -318,11 +309,42 @@ update_stats (void) } if (activity->policy.child_rel.priority == priority) - ACTIVITY_STATS (activity)->available_local - = comp_avail (activity, - activity->policy.child_rel.weight, - activity->frames_local, - 0, 0); + { + int avail = comp_avail (activity, + activity->policy.child_rel.weight, + activity->frames_local, + 0, 0); + + if (activity->frames_local > avail + || ACTIVITY_STATS (activity)->pressure_local) + { + /* The amount by which we decrease is proportional to + pressure. */ + int dec = avail / 8; + dec /= 5 - MIN (ACTIVITY_STATS (activity)->pressure_local, 4); + + debug (1, "Due to pressure (%d), decreasing frames locally " + "available to " OBJECT_NAME_FMT " from %d to %d", + ACTIVITY_STATS (activity)->pressure_local, + OBJECT_NAME_PRINTF ((struct object *) activity), + avail, avail - dec); + + avail -= dec; + + ACTIVITY_STATS (activity)->damping_factor + = -l4_msb (1024 * 1024 / PAGESIZE); + } + else if (activity->frames_total > 7 * (frames / 8)) + /* The allocated amount is close to the available frames. + Encourage it not to allocate. */ + ACTIVITY_STATS (activity)->damping_factor = 0; + else + /* It can allocate. */ + ACTIVITY_STATS (activity)->damping_factor + = l4_msb (1024 * 1024 / PAGESIZE); + + ACTIVITY_STATS (activity)->available_local = avail; + } debug (5, "%d frames for %d activities with priority %d, " "total weight: %lld, alloced: %d, excess: %d", @@ -357,7 +379,8 @@ update_stats (void) } debug (5, OBJECT_NAME_FMT " (s: %d/%d; c: %d/%d): " - "%d/%d frames, %d/%d avail (" OBJECT_NAME_FMT ")", + "%d/%d frames, %d/%d avail, %d free goal, %d bad_karma " + "(" OBJECT_NAME_FMT ")", OBJECT_NAME_PRINTF ((struct object *) activity), activity->policy.sibling_rel.priority, activity->policy.sibling_rel.weight, @@ -367,6 +390,7 @@ update_stats (void) activity->frames_total, ACTIVITY_STATS (activity)->available_local, ACTIVITY_STATS (activity)->available, + activity->free_goal, activity->free_bad_karma, OBJECT_NAME_PRINTF ((struct object *) (activity->parent ?: root_activity))); @@ -381,13 +405,16 @@ update_stats (void) struct thread *thread; object_wait_queue_for_each (activity, (struct object *) activity, thread) - if (thread->wait_reason == THREAD_WAIT_STATS - && thread->wait_reason_arg <= period / FREQ) + if (thread->wait_reason == THREAD_WAIT_ACTIVITY_INFO + && (thread->wait_reason_arg & activity_info_stats) + && thread->wait_reason_arg2 <= period / FREQ) { object_wait_queue_dequeue (activity, thread); /* XXX: Only return valid stat buffers. */ - struct activity_stats_buffer buffer; + struct activity_info info; + info.event = activity_info_stats; + int i; for (i = 0; i < ACTIVITY_STATS_PERIODS; i ++) { @@ -395,13 +422,13 @@ update_stats (void) if (period < 0) period = (ACTIVITY_STATS_PERIODS + 1) + period; - buffer.stats[i] = activity->stats[period]; + info.stats.stats[i] = activity->stats[period]; } + info.stats.count = ACTIVITY_STATS_PERIODS; + l4_msg_t msg; - rm_activity_stats_reply_marshal (&msg, - buffer, - ACTIVITY_STATS_PERIODS); + rm_activity_info_reply_marshal (&msg, info); l4_msg_tag_t msg_tag = l4_msg_msg_tag (msg); l4_set_propagation (&msg_tag); l4_msg_set_msg_tag (msg, msg_tag); diff --git a/viengoos/object.c b/viengoos/object.c index f5708f7..b1433e3 100644 --- a/viengoos/object.c +++ b/viengoos/object.c @@ -818,6 +818,31 @@ object_desc_claim (struct activity *activity, struct object_desc *desc, if (activity != desc->activity && update_accounting) activity_charge (desc->activity, -1); } + + if ((activity != desc->activity + || (desc->eviction_candidate + && ! (desc->dirty && ! desc->policy.discardable))) + && update_accounting + && desc->activity->free_goal) + { + desc->activity->free_goal --; + if (desc->activity->free_goal == 0) + /* The activity met the free goal! */ + { + debug (0, DEBUG_BOLD (OBJECT_NAME_FMT " met goal."), + OBJECT_NAME_PRINTF ((struct object *) desc->activity)); + + struct activity *ancestor = desc->activity; + activity_for_each_ancestor + (ancestor, + ({ + ancestor->frames_excluded + -= desc->activity->free_initial_allocation; + })); + + desc->activity->free_allocations = 0; + } + } } if (! activity) @@ -844,7 +869,37 @@ object_desc_claim (struct activity *activity, struct object_desc *desc, || (desc->eviction_candidate && ! (desc->dirty && ! desc->policy.discardable))) && update_accounting) - activity_charge (activity, 1); + { + activity_charge (activity, 1); + + if (activity->free_allocations) + { + activity->free_allocations --; + if (activity->free_allocations == 0) + { + if (activity->free_goal) + /* Bad boy! */ + { + activity->free_bad_karma = 8; + + debug (0, DEBUG_BOLD (OBJECT_NAME_FMT + " failed to free %d pages."), + OBJECT_NAME_PRINTF ((struct object *) activity), + activity->free_goal); + } + + struct activity *ancestor = activity; + activity_for_each_ancestor + (ancestor, + ({ + ancestor->frames_excluded + -= activity->free_initial_allocation; + })); + + activity->free_goal = 0; + } + } + } desc->eviction_candidate = false; desc->activity = activity; diff --git a/viengoos/pager.c b/viengoos/pager.c index db3f6cf..0dc7ad4 100644 --- a/viengoos/pager.c +++ b/viengoos/pager.c @@ -286,13 +286,14 @@ reclaim_from (struct activity *victim, int goal) ancestor->frames_pending_eviction += laundry_count; })); - debug (5, "Reclaimed from " OID_FMT ": goal: %d; %d frames; " + debug (5, "Reclaimed from " OBJECT_NAME_FMT ": goal: %d; %d frames; " DEBUG_BOLD ("%d in laundry, %d made available (%d discarded)") " " - "(now: avail: %d, laundry: %d)", - OID_PRINTF (object_oid ((struct object *) victim)), goal, + "(now: free: %d, avail: %d, laundry: %d)", + OBJECT_NAME_PRINTF ((struct object *) victim), goal, victim->frames_local, laundry_count, count - laundry_count, discarded, - available_list_count (&available), laundry_list_count (&laundry)); + zalloc_memory, available_list_count (&available), + laundry_list_count (&laundry)); assertx (victim->priorities_count + activity_lru_list_count (&victim->active) @@ -324,7 +325,7 @@ pager_collect (int goal) int available_memory = zalloc_memory + available_list_count (&available); - debug (5, "Frames: %d, available: %d (%d%%), pending page out: %d, " + debug (0, "Frames: %d, available: %d (%d%%), pending page out: %d, " "low water: %d, goal: %d", memory_total, available_memory, (available_memory * 100) / memory_total, @@ -466,18 +467,34 @@ pager_collect (int goal) child->policy.sibling_rel.priority)) { have_self = true; - int frames = parent->frames_local - - eviction_list_count (&parent->eviction_dirty) - - (ACTIVITY_STATS_LAST (parent)->active_local - >> factor); - if (frames > 0) - done = process (parent, parent->policy.child_rel, - frames); - if (done) - break; + + /* If PARENT->FREE_ALLOCATIONS is non-zero, we + have already selected this activity for + eviction and have sent it a message asking it + to free memory. It may make + PARENT->FREE_ALLOCATIONS before it is again + eligible. */ + if (! parent->free_allocations) + { + int frames = parent->frames_local + - eviction_list_count (&parent->eviction_dirty) + - (ACTIVITY_STATS_LAST (parent)->active_local + >> factor); + if (frames > 0) + done = process (parent, parent->policy.child_rel, + frames); + if (done) + break; + } + else + debug (0, "Excluding " OBJECT_NAME_FMT + ": %d free frames, %d excluded", + parent->free_allocations, + parent->frames_excluded); } int frames = child->frames_total + - child->frames_excluded - child->frames_pending_eviction - (ACTIVITY_STATS_LAST (child)->active >> factor); if (frames > 0) @@ -489,6 +506,7 @@ pager_collect (int goal) if (! done && ! have_self) { int frames = parent->frames_local + - child->frames_excluded - eviction_list_count (&parent->eviction_dirty) - (ACTIVITY_STATS_LAST (parent)->active_local >> factor); if (frames > 0) @@ -504,7 +522,7 @@ pager_collect (int goal) if (! victim) break; - ACTIVITY_STATS (victim)->pressure += 4; + ACTIVITY_STATS (victim)->pressure ++; assert (victim); } @@ -515,6 +533,8 @@ pager_collect (int goal) /* We steal from VICTIM. */ + ACTIVITY_STATS (victim)->pressure_local += 4; + /* Calculate VICTIM's share of the frames allocated to all the activity's at this priority level. */ int share = 0; @@ -531,8 +551,10 @@ pager_collect (int goal) assertx (victim_frames >= share, "%d < %d", victim_frames, share); - debug (5, DEBUG_BOLD ("Revoking from activity " OBJECT_NAME_FMT ", ") + debug (5, "%d of %d pages available; " + DEBUG_BOLD ("Revoking from activity " OBJECT_NAME_FMT ", ") "%d/%d frames (pending eviction: %d/%d), share: %d, goal: %d", + zalloc_memory + available_list_count (&available), memory_total, OBJECT_NAME_PRINTF ((struct object *) victim), victim->frames_local, victim->frames_total, eviction_list_count (&victim->eviction_dirty), @@ -543,7 +565,63 @@ pager_collect (int goal) if (reclaim > goal) reclaim = goal; - total_freed += reclaim_from (victim, reclaim); + bool need_reclaim = true; + + struct thread *thread; + object_wait_queue_for_each (victim, (struct object *) victim, + thread) + if (thread->wait_reason == THREAD_WAIT_ACTIVITY_INFO + && (thread->wait_reason_arg & activity_info_pressure)) + break; + + if (thread) + { + debug (0, DEBUG_BOLD ("Requesting that " OBJECT_NAME_FMT " free " + "%d pages.") " Karma: %d", + OBJECT_NAME_PRINTF ((struct object *) victim), + goal, victim->free_bad_karma); + + if (! victim->free_goal + && victim->free_bad_karma == 0) + /* If the activity manages half the goal, we'll be + happy. */ + { + need_reclaim = false; + + victim->free_goal = goal / 2; + victim->free_allocations = 100; + victim->free_initial_allocation = victim->frames_local; + + struct activity *ancestor = victim; + activity_for_each_ancestor + (ancestor, + ({ + ancestor->frames_excluded += victim->frames_local; + })); + + total_freed += goal; + } + + struct activity_info info; + info.event = activity_info_pressure; + info.pressure.amount = - goal; + + object_wait_queue_for_each (victim, + (struct object *) victim, + thread) + if (thread->wait_reason == THREAD_WAIT_ACTIVITY_INFO + && (thread->wait_reason_arg & activity_info_pressure)) + { + object_wait_queue_dequeue (victim, thread); + rm_activity_info_reply (thread->tid, info); + } + } + + if (victim->free_bad_karma) + victim->free_bad_karma --; + + if (need_reclaim) + total_freed += reclaim_from (victim, reclaim); } if (zalloc_memory + available_list_count (&available) diff --git a/viengoos/rm.h b/viengoos/rm.h index 3954ca3..04e2a06 100644 --- a/viengoos/rm.h +++ b/viengoos/rm.h @@ -77,8 +77,8 @@ rm_method_id_string (int id) return "thread_wait_object_destroyed"; case RM_activity_policy: return "activity_policy"; - case RM_activity_stats: - return "activity_stats"; + case RM_activity_info: + return "activity_info"; case RM_futex: return "futex"; default: diff --git a/viengoos/server.c b/viengoos/server.c index 832fe5d..153477a 100644 --- a/viengoos/server.c +++ b/viengoos/server.c @@ -1362,37 +1362,40 @@ server_loop (void) break; } - case RM_activity_stats: + case RM_activity_info: { + uintptr_t flags; uintptr_t until_period; - err = rm_activity_stats_send_unmarshal (&msg, &principal_addr, - &until_period); + + err = rm_activity_info_send_unmarshal (&msg, &principal_addr, + &flags, + &until_period); if (err) REPLY (err); - DEBUG (4, "%d", until_period); - int period = principal->current_period - 1; if (period < 0) period = (ACTIVITY_STATS_PERIODS + 1) + period; - if (principal->stats[period].period < until_period) - /* Queue thread on the activity. */ - { - thread->wait_reason = THREAD_WAIT_STATS; - thread->wait_reason_arg = until_period; - - object_wait_queue_enqueue (principal, - (struct object *) principal, - thread); - - do_reply = 0; - } - else + DEBUG (4, OBJECT_NAME_FMT ": %s%s%s(%d), " + "period: %d (current: %d)\n", + OBJECT_NAME_PRINTF ((struct object *) principal), + flags & activity_info_stats ? "stats" : "", + (flags == (activity_info_free|activity_info_stats)) + ? ", " : "", + flags & activity_info_free ? "free" : "", + flags, + until_period, principal->stats[period].period); + + if ((flags & activity_info_stats) + && principal->stats[period].period > 0 + && principal->stats[period].period >= until_period) /* Return the available statistics. */ { /* XXX: Only return valid stat buffers. */ - struct activity_stats_buffer buffer; + struct activity_info info; + info.event = activity_info_stats; + int i; for (i = 0; i < ACTIVITY_STATS_PERIODS; i ++) { @@ -1400,13 +1403,28 @@ server_loop (void) if (period < 0) period = (ACTIVITY_STATS_PERIODS + 1) + period; - buffer.stats[i] = principal->stats[period]; + info.stats.stats[i] = principal->stats[period]; } - rm_activity_stats_reply_marshal (&msg, - buffer, - ACTIVITY_STATS_PERIODS); + info.stats.count = ACTIVITY_STATS_PERIODS; + + rm_activity_info_reply_marshal (&msg, info); } + else if (flags) + /* Queue thread on the activity. */ + { + thread->wait_reason = THREAD_WAIT_ACTIVITY_INFO; + thread->wait_reason_arg = flags; + thread->wait_reason_arg2 = until_period; + + object_wait_queue_enqueue (principal, + (struct object *) principal, + thread); + + do_reply = 0; + } + else + REPLY (EINVAL); break; } diff --git a/viengoos/thread.h b/viengoos/thread.h index 27321f6..728bf7b 100644 --- a/viengoos/thread.h +++ b/viengoos/thread.h @@ -46,10 +46,10 @@ enum /* THREAD is blocked on an object waiting for the object to be destroyed. */ THREAD_WAIT_DESTROY, - /* THREAD is blocked on an activity waiting for the stats for a - particular period, which is stored in - thread->wait_reason_arg. */ - THREAD_WAIT_STATS, + /* THREAD is blocked on an activity waiting for information. The + type of information is stored in wait_reason_arg. The period + in wait_reason_arg2. */ + THREAD_WAIT_ACTIVITY_INFO, }; struct thread @@ -99,6 +99,7 @@ struct thread uint32_t wait_reason : 28; /* More information about the reason. */ uint32_t wait_reason_arg; + uint32_t wait_reason_arg2; /* The object the thread is waiting on. Only meaningful if WAIT_QUEUE_P is true. */ |