railsbench 0.9.2 → 0.9.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/CHANGELOG +1808 -451
- data/GCPATCH +73 -0
- data/INSTALL +5 -0
- data/Manifest.txt +23 -13
- data/PROBLEMS +0 -0
- data/README +23 -7
- data/Rakefile +1 -2
- data/bin/railsbench +7 -1
- data/config/benchmarking.rb +0 -0
- data/config/benchmarks.rb +3 -2
- data/config/benchmarks.yml +0 -0
- data/images/empty.png +0 -0
- data/images/minus.png +0 -0
- data/images/plus.png +0 -0
- data/install.rb +1 -1
- data/latest_changes.txt +18 -0
- data/lib/benchmark.rb +0 -0
- data/lib/railsbench/benchmark.rb +576 -0
- data/lib/railsbench/benchmark_specs.rb +63 -63
- data/lib/railsbench/gc_info.rb +38 -3
- data/lib/railsbench/perf_info.rb +1 -1
- data/lib/railsbench/perf_utils.rb +202 -179
- data/lib/railsbench/railsbenchmark.rb +213 -55
- data/lib/railsbench/version.rb +9 -9
- data/lib/railsbench/write_headers_only.rb +15 -15
- data/postinstall.rb +0 -0
- data/ruby185gc.patch +56 -29
- data/ruby186gc.patch +564 -0
- data/ruby19gc.patch +2425 -0
- data/script/convert_raw_data_files +49 -49
- data/script/generate_benchmarks +14 -4
- data/script/perf_bench +12 -8
- data/script/perf_comp +1 -1
- data/script/perf_comp_gc +9 -1
- data/script/perf_diff +2 -2
- data/script/perf_diff_gc +2 -2
- data/script/perf_html +1 -1
- data/script/perf_plot +192 -75
- data/script/perf_plot_gc +213 -74
- data/script/perf_prof +29 -10
- data/script/perf_run +2 -2
- data/script/perf_run_gc +2 -2
- data/script/perf_table +2 -2
- data/script/perf_tex +1 -1
- data/script/perf_times +6 -6
- data/script/perf_times_gc +14 -2
- data/script/run_urls +16 -10
- data/setup.rb +0 -0
- data/test/railsbench_test.rb +0 -0
- data/test/test_helper.rb +2 -0
- metadata +77 -55
@@ -1,15 +1,15 @@
|
|
1
|
-
class ActionController::CgiResponse
|
2
|
-
# on windows it is sometimes necessary to turn off writing output
|
3
|
-
# to avoid out of memory errors running under the console
|
4
|
-
def out(output = $stdout)
|
5
|
-
convert_content_type!(@headers)
|
6
|
-
output.binmode if output.respond_to?(:binmode)
|
7
|
-
output.sync = false if output.respond_to?(:sync=)
|
8
|
-
begin
|
9
|
-
output.write(@cgi.header(@headers))
|
10
|
-
output.flush if output.respond_to?(:flush)
|
11
|
-
rescue Errno::EPIPE => e
|
12
|
-
# lost connection to the FCGI process -- ignore the output, then
|
13
|
-
end
|
14
|
-
end
|
15
|
-
end
|
1
|
+
class ActionController::CgiResponse
|
2
|
+
# on windows it is sometimes necessary to turn off writing output
|
3
|
+
# to avoid out of memory errors running under the console
|
4
|
+
def out(output = $stdout)
|
5
|
+
convert_content_type!(@headers)
|
6
|
+
output.binmode if output.respond_to?(:binmode)
|
7
|
+
output.sync = false if output.respond_to?(:sync=)
|
8
|
+
begin
|
9
|
+
output.write(@cgi.header(@headers))
|
10
|
+
output.flush if output.respond_to?(:flush)
|
11
|
+
rescue Errno::EPIPE => e
|
12
|
+
# lost connection to the FCGI process -- ignore the output, then
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
data/postinstall.rb
CHANGED
File without changes
|
data/ruby185gc.patch
CHANGED
@@ -1,5 +1,5 @@
|
|
1
|
-
--- gc.c.orig 2006-
|
2
|
-
+++ gc.c
|
1
|
+
--- gc.c.orig 2006-08-25 10:12:46.000000000 +0200
|
2
|
+
+++ gc.c 2007-05-06 10:55:19.000000000 +0200
|
3
3
|
@@ -22,8 +22,16 @@
|
4
4
|
#include <setjmp.h>
|
5
5
|
#include <sys/types.h>
|
@@ -17,7 +17,7 @@
|
|
17
17
|
#endif
|
18
18
|
|
19
19
|
#ifdef HAVE_SYS_RESOURCE_H
|
20
|
-
@@ -
|
20
|
+
@@ -54,7 +62,6 @@
|
21
21
|
#if !defined(setjmp) && defined(HAVE__SETJMP)
|
22
22
|
#define setjmp(env) _setjmp(env)
|
23
23
|
#endif
|
@@ -25,7 +25,7 @@
|
|
25
25
|
/* Make alloca work the best possible way. */
|
26
26
|
#ifdef __GNUC__
|
27
27
|
# ifndef atarist
|
28
|
-
@@ -
|
28
|
+
@@ -173,8 +180,17 @@
|
29
29
|
RUBY_CRITICAL(free(x));
|
30
30
|
}
|
31
31
|
|
@@ -43,7 +43,7 @@
|
|
43
43
|
static int during_gc;
|
44
44
|
static int need_call_final = 0;
|
45
45
|
static st_table *finalizer_table = 0;
|
46
|
-
@@ -
|
46
|
+
@@ -209,7 +225,7 @@
|
47
47
|
* Disables garbage collection, returning <code>true</code> if garbage
|
48
48
|
* collection was already disabled.
|
49
49
|
*
|
@@ -52,7 +52,7 @@
|
|
52
52
|
* GC.disable #=> true
|
53
53
|
*
|
54
54
|
*/
|
55
|
-
@@ -
|
55
|
+
@@ -223,6 +239,104 @@
|
56
56
|
return old;
|
57
57
|
}
|
58
58
|
|
@@ -157,7 +157,7 @@
|
|
157
157
|
VALUE rb_mGC;
|
158
158
|
|
159
159
|
static struct gc_list {
|
160
|
-
@@ -
|
160
|
+
@@ -314,7 +428,7 @@
|
161
161
|
static RVALUE *freelist = 0;
|
162
162
|
static RVALUE *deferred_final_list = 0;
|
163
163
|
|
@@ -166,7 +166,7 @@
|
|
166
166
|
static struct heaps_slot {
|
167
167
|
void *membase;
|
168
168
|
RVALUE *slot;
|
169
|
-
@@ -
|
169
|
+
@@ -323,13 +437,165 @@
|
170
170
|
static int heaps_length = 0;
|
171
171
|
static int heaps_used = 0;
|
172
172
|
|
@@ -176,6 +176,8 @@
|
|
176
176
|
+static int heap_slots = 10000;
|
177
177
|
+
|
178
178
|
+static int heap_free_min = 4096;
|
179
|
+
+static int heap_slots_increment = 10000;
|
180
|
+
+static double heap_slots_growth_factor = 1.8;
|
179
181
|
+
|
180
182
|
+static long initial_malloc_limit = GC_MALLOC_LIMIT;
|
181
183
|
|
@@ -188,11 +190,11 @@
|
|
188
190
|
|
189
191
|
+static void set_gc_parameters()
|
190
192
|
+{
|
191
|
-
+ char *gc_stats_ptr, *min_slots_ptr, *free_min_ptr,
|
192
|
-
+
|
193
|
+
+ char *gc_stats_ptr, *min_slots_ptr, *free_min_ptr, *heap_slots_incr_ptr,
|
194
|
+
+ *heap_incr_ptr, *malloc_limit_ptr, *gc_heap_file_ptr, *heap_slots_growth_factor_ptr;
|
193
195
|
+
|
194
196
|
+ gc_data_file = stderr;
|
195
|
-
+
|
197
|
+
+
|
196
198
|
+ gc_stats_ptr = getenv("RUBY_GC_STATS");
|
197
199
|
+ if (gc_stats_ptr != NULL) {
|
198
200
|
+ int gc_stats_i = atoi(gc_stats_ptr);
|
@@ -200,10 +202,10 @@
|
|
200
202
|
+ verbose_gc_stats = Qtrue;
|
201
203
|
+ }
|
202
204
|
+ }
|
203
|
-
+
|
205
|
+
+
|
204
206
|
+ gc_heap_file_ptr = getenv("RUBY_GC_DATA_FILE");
|
205
207
|
+ if (gc_heap_file_ptr != NULL) {
|
206
|
-
+ FILE* data_file = fopen(gc_heap_file_ptr, "w");
|
208
|
+
+ FILE* data_file = fopen(gc_heap_file_ptr, "w");
|
207
209
|
+ if (data_file != NULL) {
|
208
210
|
+ gc_data_file = data_file;
|
209
211
|
+ }
|
@@ -247,6 +249,28 @@
|
|
247
249
|
+ }
|
248
250
|
+ }
|
249
251
|
+
|
252
|
+
+ heap_slots_incr_ptr = getenv("RUBY_HEAP_SLOTS_INCREMENT");
|
253
|
+
+ if (heap_slots_incr_ptr != NULL) {
|
254
|
+
+ int heap_slots_incr_i = atoi(heap_slots_incr_ptr);
|
255
|
+
+ if (verbose_gc_stats) {
|
256
|
+
+ fprintf(gc_data_file, "RUBY_HEAP_SLOTS_INCREMENT=%s\n", heap_slots_incr_ptr);
|
257
|
+
+ }
|
258
|
+
+ if (heap_slots_incr_i > 0) {
|
259
|
+
+ heap_slots_increment = heap_slots_incr_i;
|
260
|
+
+ }
|
261
|
+
+ }
|
262
|
+
+
|
263
|
+
+ heap_slots_growth_factor_ptr = getenv("RUBY_HEAP_SLOTS_GROWTH_FACTOR");
|
264
|
+
+ if (heap_slots_growth_factor_ptr != NULL) {
|
265
|
+
+ double heap_slots_growth_factor_d = atoi(heap_slots_growth_factor_ptr);
|
266
|
+
+ if (verbose_gc_stats) {
|
267
|
+
+ fprintf(gc_data_file, "RUBY_HEAP_SLOTS_GROWTH_FACTOR=%s\n", heap_slots_growth_factor_ptr);
|
268
|
+
+ }
|
269
|
+
+ if (heap_slots_growth_factor_d > 0) {
|
270
|
+
+ heap_slots_growth_factor = heap_slots_growth_factor_d;
|
271
|
+
+ }
|
272
|
+
+ }
|
273
|
+
+
|
250
274
|
+ malloc_limit_ptr = getenv("RUBY_GC_MALLOC_LIMIT");
|
251
275
|
+ if (malloc_limit_ptr != NULL) {
|
252
276
|
+ int malloc_limit_i = atol(malloc_limit_ptr);
|
@@ -311,7 +335,7 @@
|
|
311
335
|
static void
|
312
336
|
add_heap()
|
313
337
|
{
|
314
|
-
@@ -
|
338
|
+
@@ -340,7 +606,7 @@
|
315
339
|
struct heaps_slot *p;
|
316
340
|
int length;
|
317
341
|
|
@@ -320,7 +344,7 @@
|
|
320
344
|
length = heaps_length*sizeof(struct heaps_slot);
|
321
345
|
RUBY_CRITICAL(
|
322
346
|
if (heaps_used > 0) {
|
323
|
-
@@ -
|
347
|
+
@@ -356,10 +622,10 @@
|
324
348
|
for (;;) {
|
325
349
|
RUBY_CRITICAL(p = (RVALUE*)malloc(sizeof(RVALUE)*(heap_slots+1)));
|
326
350
|
if (p == 0) {
|
@@ -333,16 +357,19 @@
|
|
333
357
|
continue;
|
334
358
|
}
|
335
359
|
heaps[heaps_used].membase = p;
|
336
|
-
@@ -
|
360
|
+
@@ -375,8 +641,9 @@
|
361
|
+
if (lomem == 0 || lomem > p) lomem = p;
|
337
362
|
if (himem < pend) himem = pend;
|
338
363
|
heaps_used++;
|
339
|
-
|
364
|
+
- heap_slots *= 1.8;
|
340
365
|
- if (heap_slots <= 0) heap_slots = HEAP_MIN_SLOTS;
|
366
|
+
+ heap_slots += heap_slots_increment;
|
367
|
+
+ heap_slots_increment *= heap_slots_growth_factor;
|
341
368
|
+ if (heap_slots <= 0) heap_slots = heap_min_slots;
|
342
369
|
|
343
370
|
while (p < pend) {
|
344
371
|
p->as.free.flags = 0;
|
345
|
-
@@ -
|
372
|
+
@@ -1026,6 +1293,39 @@
|
346
373
|
}
|
347
374
|
}
|
348
375
|
|
@@ -382,7 +409,7 @@
|
|
382
409
|
static void
|
383
410
|
free_unused_heaps()
|
384
411
|
{
|
385
|
-
@@ -
|
412
|
+
@@ -1056,12 +1356,21 @@
|
386
413
|
unsigned long live = 0;
|
387
414
|
unsigned long free_min = 0;
|
388
415
|
|
@@ -406,7 +433,7 @@
|
|
406
433
|
|
407
434
|
if (ruby_in_compile && ruby_parser_stack_on_heap()) {
|
408
435
|
/* should not reclaim nodes during compilation
|
409
|
-
@@ -
|
436
|
+
@@ -1094,6 +1403,9 @@
|
410
437
|
if (!(p->as.basic.flags & FL_MARK)) {
|
411
438
|
if (p->as.basic.flags) {
|
412
439
|
obj_free((VALUE)p);
|
@@ -416,7 +443,7 @@
|
|
416
443
|
}
|
417
444
|
if (need_call_final && FL_TEST(p, FL_FINALIZE)) {
|
418
445
|
p->as.free.flags = FL_MARK; /* remain marked */
|
419
|
-
@@ -
|
446
|
+
@@ -1101,6 +1413,12 @@
|
420
447
|
final_list = p;
|
421
448
|
}
|
422
449
|
else {
|
@@ -429,7 +456,7 @@
|
|
429
456
|
p->as.free.flags = 0;
|
430
457
|
p->as.free.next = freelist;
|
431
458
|
freelist = p;
|
432
|
-
@@ -
|
459
|
+
@@ -1114,6 +1432,9 @@
|
433
460
|
else {
|
434
461
|
RBASIC(p)->flags &= ~FL_MARK;
|
435
462
|
live++;
|
@@ -439,7 +466,7 @@
|
|
439
466
|
}
|
440
467
|
p++;
|
441
468
|
}
|
442
|
-
@@ -
|
469
|
+
@@ -1132,7 +1453,7 @@
|
443
470
|
}
|
444
471
|
if (malloc_increase > malloc_limit) {
|
445
472
|
malloc_limit += (malloc_increase - malloc_limit) * (double)live / (live + freed);
|
@@ -448,7 +475,7 @@
|
|
448
475
|
}
|
449
476
|
malloc_increase = 0;
|
450
477
|
if (freed < free_min) {
|
451
|
-
@@ -
|
478
|
+
@@ -1140,6 +1461,20 @@
|
452
479
|
}
|
453
480
|
during_gc = 0;
|
454
481
|
|
@@ -469,7 +496,7 @@
|
|
469
496
|
/* clear finalization list */
|
470
497
|
if (final_list) {
|
471
498
|
deferred_final_list = final_list;
|
472
|
-
@@ -
|
499
|
+
@@ -1334,6 +1669,7 @@
|
473
500
|
struct gc_list *list;
|
474
501
|
struct FRAME * volatile frame; /* gcc 2.7.2.3 -O2 bug?? */
|
475
502
|
jmp_buf save_regs_gc_mark;
|
@@ -477,7 +504,7 @@
|
|
477
504
|
SET_STACK_END;
|
478
505
|
|
479
506
|
#ifdef HAVE_NATIVETHREAD
|
480
|
-
@@ -
|
507
|
+
@@ -1350,6 +1686,14 @@
|
481
508
|
if (during_gc) return;
|
482
509
|
during_gc++;
|
483
510
|
|
@@ -492,7 +519,7 @@
|
|
492
519
|
init_mark_stack();
|
493
520
|
|
494
521
|
gc_mark((VALUE)ruby_current_node, 0);
|
495
|
-
@@ -
|
522
|
+
@@ -1438,6 +1782,17 @@
|
496
523
|
} while (!MARK_STACK_EMPTY);
|
497
524
|
|
498
525
|
gc_sweep();
|
@@ -510,7 +537,7 @@
|
|
510
537
|
}
|
511
538
|
|
512
539
|
void
|
513
|
-
@@ -
|
540
|
+
@@ -1551,6 +1906,7 @@
|
514
541
|
if (!rb_gc_stack_start) {
|
515
542
|
Init_stack(0);
|
516
543
|
}
|
@@ -518,7 +545,7 @@
|
|
518
545
|
add_heap();
|
519
546
|
}
|
520
547
|
|
521
|
-
@@ -
|
548
|
+
@@ -2020,6 +2376,14 @@
|
522
549
|
rb_define_singleton_method(rb_mGC, "disable", rb_gc_disable, 0);
|
523
550
|
rb_define_method(rb_mGC, "garbage_collect", rb_gc_start, 0);
|
524
551
|
|
data/ruby186gc.patch
ADDED
@@ -0,0 +1,564 @@
|
|
1
|
+
Index: gc.c
|
2
|
+
===================================================================
|
3
|
+
--- gc.c (revision 12920)
|
4
|
+
+++ gc.c (working copy)
|
5
|
+
@@ -22,8 +22,16 @@
|
6
|
+
#include <setjmp.h>
|
7
|
+
#include <sys/types.h>
|
8
|
+
|
9
|
+
+#ifdef _WIN32
|
10
|
+
+#include <string.h>
|
11
|
+
+#else
|
12
|
+
+#include <strings.h>
|
13
|
+
+#endif
|
14
|
+
+
|
15
|
+
#ifdef HAVE_SYS_TIME_H
|
16
|
+
#include <sys/time.h>
|
17
|
+
+#elif defined(_WIN32)
|
18
|
+
+#include <time.h>
|
19
|
+
#endif
|
20
|
+
|
21
|
+
#ifdef HAVE_SYS_RESOURCE_H
|
22
|
+
@@ -40,7 +48,6 @@
|
23
|
+
#if !defined(setjmp) && defined(HAVE__SETJMP)
|
24
|
+
#define setjmp(env) _setjmp(env)
|
25
|
+
#endif
|
26
|
+
-
|
27
|
+
/* Make alloca work the best possible way. */
|
28
|
+
#ifdef __GNUC__
|
29
|
+
# ifndef atarist
|
30
|
+
@@ -159,8 +166,17 @@
|
31
|
+
RUBY_CRITICAL(free(x));
|
32
|
+
}
|
33
|
+
|
34
|
+
+#if HAVE_LONG_LONG
|
35
|
+
+#define GC_TIME_TYPE LONG_LONG
|
36
|
+
+#else
|
37
|
+
+#define GC_TIME_TYPE long
|
38
|
+
+#endif
|
39
|
+
+
|
40
|
+
extern int ruby_in_compile;
|
41
|
+
static int dont_gc;
|
42
|
+
+static int gc_statistics = 0;
|
43
|
+
+static GC_TIME_TYPE gc_time = 0;
|
44
|
+
+static int gc_collections = 0;
|
45
|
+
static int during_gc;
|
46
|
+
static int need_call_final = 0;
|
47
|
+
static st_table *finalizer_table = 0;
|
48
|
+
@@ -195,7 +211,7 @@
|
49
|
+
* Disables garbage collection, returning <code>true</code> if garbage
|
50
|
+
* collection was already disabled.
|
51
|
+
*
|
52
|
+
- * GC.disable #=> false
|
53
|
+
+ * GC.disable #=> false or true
|
54
|
+
* GC.disable #=> true
|
55
|
+
*
|
56
|
+
*/
|
57
|
+
@@ -209,6 +225,104 @@
|
58
|
+
return old;
|
59
|
+
}
|
60
|
+
|
61
|
+
+/*
|
62
|
+
+ * call-seq:
|
63
|
+
+ * GC.enable_stats => true or false
|
64
|
+
+ *
|
65
|
+
+ * Enables garbage collection statistics, returning <code>true</code> if garbage
|
66
|
+
+ * collection statistics was already enabled.
|
67
|
+
+ *
|
68
|
+
+ * GC.enable_stats #=> false or true
|
69
|
+
+ * GC.enable_stats #=> true
|
70
|
+
+ *
|
71
|
+
+ */
|
72
|
+
+
|
73
|
+
+VALUE
|
74
|
+
+rb_gc_enable_stats()
|
75
|
+
+{
|
76
|
+
+ int old = gc_statistics;
|
77
|
+
+ gc_statistics = Qtrue;
|
78
|
+
+ return old;
|
79
|
+
+}
|
80
|
+
+
|
81
|
+
+/*
|
82
|
+
+ * call-seq:
|
83
|
+
+ * GC.disable_stats => true or false
|
84
|
+
+ *
|
85
|
+
+ * Disables garbage collection statistics, returning <code>true</code> if garbage
|
86
|
+
+ * collection statistics was already disabled.
|
87
|
+
+ *
|
88
|
+
+ * GC.disable_stats #=> false or true
|
89
|
+
+ * GC.disable_stats #=> true
|
90
|
+
+ *
|
91
|
+
+ */
|
92
|
+
+
|
93
|
+
+VALUE
|
94
|
+
+rb_gc_disable_stats()
|
95
|
+
+{
|
96
|
+
+ int old = gc_statistics;
|
97
|
+
+ gc_statistics = Qfalse;
|
98
|
+
+ return old;
|
99
|
+
+}
|
100
|
+
+
|
101
|
+
+/*
|
102
|
+
+ * call-seq:
|
103
|
+
+ * GC.clear_stats => nil
|
104
|
+
+ *
|
105
|
+
+ * Clears garbage collection statistics, returning nil. This resets the number
|
106
|
+
+ * of collections (GC.collections) and the time used (GC.time) to 0.
|
107
|
+
+ *
|
108
|
+
+ * GC.clear_stats #=> nil
|
109
|
+
+ *
|
110
|
+
+ */
|
111
|
+
+
|
112
|
+
+VALUE
|
113
|
+
+rb_gc_clear_stats()
|
114
|
+
+{
|
115
|
+
+ gc_collections = 0;
|
116
|
+
+ gc_time = 0;
|
117
|
+
+ return Qnil;
|
118
|
+
+}
|
119
|
+
+
|
120
|
+
+/*
|
121
|
+
+ * call-seq:
|
122
|
+
+ * GC.collections => Integer
|
123
|
+
+ *
|
124
|
+
+ * Returns the number of garbage collections performed while GC statistics collection
|
125
|
+
+ * was enabled.
|
126
|
+
+ *
|
127
|
+
+ * GC.collections #=> 35
|
128
|
+
+ *
|
129
|
+
+ */
|
130
|
+
+
|
131
|
+
+VALUE
|
132
|
+
+rb_gc_collections()
|
133
|
+
+{
|
134
|
+
+ return INT2NUM(gc_collections);
|
135
|
+
+}
|
136
|
+
+
|
137
|
+
+/*
|
138
|
+
+ * call-seq:
|
139
|
+
+ * GC.time => Integer
|
140
|
+
+ *
|
141
|
+
+ * Returns the time spent during garbage collection while GC statistics collection
|
142
|
+
+ * was enabled (in micro seconds).
|
143
|
+
+ *
|
144
|
+
+ * GC.time #=> 20000
|
145
|
+
+ *
|
146
|
+
+ */
|
147
|
+
+
|
148
|
+
+VALUE
|
149
|
+
+rb_gc_time()
|
150
|
+
+{
|
151
|
+
+#if HAVE_LONG_LONG
|
152
|
+
+ return LL2NUM(gc_time);
|
153
|
+
+#else
|
154
|
+
+ return LONG2NUM(gc_time);
|
155
|
+
+#endif
|
156
|
+
+}
|
157
|
+
+
|
158
|
+
+
|
159
|
+
VALUE rb_mGC;
|
160
|
+
|
161
|
+
static struct gc_list {
|
162
|
+
@@ -300,7 +414,7 @@
|
163
|
+
static RVALUE *freelist = 0;
|
164
|
+
static RVALUE *deferred_final_list = 0;
|
165
|
+
|
166
|
+
-#define HEAPS_INCREMENT 10
|
167
|
+
+static int heaps_increment = 10;
|
168
|
+
static struct heaps_slot {
|
169
|
+
void *membase;
|
170
|
+
RVALUE *slot;
|
171
|
+
@@ -309,13 +423,165 @@
|
172
|
+
static int heaps_length = 0;
|
173
|
+
static int heaps_used = 0;
|
174
|
+
|
175
|
+
-#define HEAP_MIN_SLOTS 10000
|
176
|
+
-static int heap_slots = HEAP_MIN_SLOTS;
|
177
|
+
+static int heap_min_slots = 10000;
|
178
|
+
+static int heap_slots = 10000;
|
179
|
+
+
|
180
|
+
+static int heap_free_min = 4096;
|
181
|
+
+static int heap_slots_increment = 10000;
|
182
|
+
+static double heap_slots_growth_factor = 1.8;
|
183
|
+
+
|
184
|
+
+static long initial_malloc_limit = GC_MALLOC_LIMIT;
|
185
|
+
+
|
186
|
+
+static int verbose_gc_stats = Qfalse;
|
187
|
+
|
188
|
+
-#define FREE_MIN 4096
|
189
|
+
+static FILE* gc_data_file = NULL;
|
190
|
+
|
191
|
+
static RVALUE *himem, *lomem;
|
192
|
+
|
193
|
+
+static void set_gc_parameters()
|
194
|
+
+{
|
195
|
+
+ char *gc_stats_ptr, *min_slots_ptr, *free_min_ptr, *heap_slots_incr_ptr,
|
196
|
+
+ *heap_incr_ptr, *malloc_limit_ptr, *gc_heap_file_ptr, *heap_slots_growth_factor_ptr;
|
197
|
+
+
|
198
|
+
+ gc_data_file = stderr;
|
199
|
+
+
|
200
|
+
+ gc_stats_ptr = getenv("RUBY_GC_STATS");
|
201
|
+
+ if (gc_stats_ptr != NULL) {
|
202
|
+
+ int gc_stats_i = atoi(gc_stats_ptr);
|
203
|
+
+ if (gc_stats_i > 0) {
|
204
|
+
+ verbose_gc_stats = Qtrue;
|
205
|
+
+ }
|
206
|
+
+ }
|
207
|
+
+
|
208
|
+
+ gc_heap_file_ptr = getenv("RUBY_GC_DATA_FILE");
|
209
|
+
+ if (gc_heap_file_ptr != NULL) {
|
210
|
+
+ FILE* data_file = fopen(gc_heap_file_ptr, "w");
|
211
|
+
+ if (data_file != NULL) {
|
212
|
+
+ gc_data_file = data_file;
|
213
|
+
+ }
|
214
|
+
+ else {
|
215
|
+
+ fprintf(stderr,
|
216
|
+
+ "can't open gc log file %s for writing, using default\n", gc_heap_file_ptr);
|
217
|
+
+ }
|
218
|
+
+ }
|
219
|
+
+
|
220
|
+
+ min_slots_ptr = getenv("RUBY_HEAP_MIN_SLOTS");
|
221
|
+
+ if (min_slots_ptr != NULL) {
|
222
|
+
+ int min_slots_i = atoi(min_slots_ptr);
|
223
|
+
+ if (verbose_gc_stats) {
|
224
|
+
+ fprintf(gc_data_file, "RUBY_HEAP_MIN_SLOTS=%s\n", min_slots_ptr);
|
225
|
+
+ }
|
226
|
+
+ if (min_slots_i > 0) {
|
227
|
+
+ heap_slots = min_slots_i;
|
228
|
+
+ heap_min_slots = min_slots_i;
|
229
|
+
+ }
|
230
|
+
+ }
|
231
|
+
+
|
232
|
+
+ free_min_ptr = getenv("RUBY_HEAP_FREE_MIN");
|
233
|
+
+ if (free_min_ptr != NULL) {
|
234
|
+
+ int free_min_i = atoi(free_min_ptr);
|
235
|
+
+ if (verbose_gc_stats) {
|
236
|
+
+ fprintf(gc_data_file, "RUBY_HEAP_FREE_MIN=%s\n", free_min_ptr);
|
237
|
+
+ }
|
238
|
+
+ if (free_min_i > 0) {
|
239
|
+
+ heap_free_min = free_min_i;
|
240
|
+
+ }
|
241
|
+
+ }
|
242
|
+
+
|
243
|
+
+ heap_incr_ptr = getenv("RUBY_HEAP_INCREMENT");
|
244
|
+
+ if (heap_incr_ptr != NULL) {
|
245
|
+
+ int heap_incr_i = atoi(heap_incr_ptr);
|
246
|
+
+ if (verbose_gc_stats) {
|
247
|
+
+ fprintf(gc_data_file, "RUBY_HEAP_INCREMENT=%s\n", heap_incr_ptr);
|
248
|
+
+ }
|
249
|
+
+ if (heap_incr_i > 0) {
|
250
|
+
+ heaps_increment = heap_incr_i;
|
251
|
+
+ }
|
252
|
+
+ }
|
253
|
+
+
|
254
|
+
+ heap_slots_incr_ptr = getenv("RUBY_HEAP_SLOTS_INCREMENT");
|
255
|
+
+ if (heap_slots_incr_ptr != NULL) {
|
256
|
+
+ int heap_slots_incr_i = atoi(heap_slots_incr_ptr);
|
257
|
+
+ if (verbose_gc_stats) {
|
258
|
+
+ fprintf(gc_data_file, "RUBY_HEAP_SLOTS_INCREMENT=%s\n", heap_slots_incr_ptr);
|
259
|
+
+ }
|
260
|
+
+ if (heap_slots_incr_i > 0) {
|
261
|
+
+ heap_slots_increment = heap_slots_incr_i;
|
262
|
+
+ }
|
263
|
+
+ }
|
264
|
+
+
|
265
|
+
+ heap_slots_growth_factor_ptr = getenv("RUBY_HEAP_SLOTS_GROWTH_FACTOR");
|
266
|
+
+ if (heap_slots_growth_factor_ptr != NULL) {
|
267
|
+
+ double heap_slots_growth_factor_d = atoi(heap_slots_growth_factor_ptr);
|
268
|
+
+ if (verbose_gc_stats) {
|
269
|
+
+ fprintf(gc_data_file, "RUBY_HEAP_SLOTS_GROWTH_FACTOR=%s\n", heap_slots_growth_factor_ptr);
|
270
|
+
+ }
|
271
|
+
+ if (heap_slots_growth_factor_d > 0) {
|
272
|
+
+ heap_slots_growth_factor = heap_slots_growth_factor_d;
|
273
|
+
+ }
|
274
|
+
+ }
|
275
|
+
+
|
276
|
+
+ malloc_limit_ptr = getenv("RUBY_GC_MALLOC_LIMIT");
|
277
|
+
+ if (malloc_limit_ptr != NULL) {
|
278
|
+
+ int malloc_limit_i = atol(malloc_limit_ptr);
|
279
|
+
+ if (verbose_gc_stats) {
|
280
|
+
+ fprintf(gc_data_file, "RUBY_GC_MALLOC_LIMIT=%s\n", malloc_limit_ptr);
|
281
|
+
+ }
|
282
|
+
+ if (malloc_limit_i > 0) {
|
283
|
+
+ initial_malloc_limit = malloc_limit_i;
|
284
|
+
+ }
|
285
|
+
+ }
|
286
|
+
+}
|
287
|
+
+
|
288
|
+
+/*
|
289
|
+
+ * call-seq:
|
290
|
+
+ * GC.dump => nil
|
291
|
+
+ *
|
292
|
+
+ * dumps information about the current GC data structures to the GC log file
|
293
|
+
+ *
|
294
|
+
+ * GC.dump #=> nil
|
295
|
+
+ *
|
296
|
+
+ */
|
297
|
+
+
|
298
|
+
+VALUE
|
299
|
+
+rb_gc_dump()
|
300
|
+
+{
|
301
|
+
+ int i;
|
302
|
+
+
|
303
|
+
+ for (i = 0; i < heaps_used; i++) {
|
304
|
+
+ int heap_size = heaps[i].limit;
|
305
|
+
+ fprintf(gc_data_file, "HEAP[%2d]: size=%7d\n", i, heap_size);
|
306
|
+
+ }
|
307
|
+
+
|
308
|
+
+ return Qnil;
|
309
|
+
+}
|
310
|
+
+
|
311
|
+
+/*
|
312
|
+
+ * call-seq:
|
313
|
+
+ * GC.log String => String
|
314
|
+
+ *
|
315
|
+
+ * Logs string to the GC data file and returns it.
|
316
|
+
+ *
|
317
|
+
+ * GC.log "manual GC call" #=> "manual GC call"
|
318
|
+
+ *
|
319
|
+
+ */
|
320
|
+
+
|
321
|
+
+VALUE
|
322
|
+
+rb_gc_log(self, original_str)
|
323
|
+
+ VALUE self, original_str;
|
324
|
+
+{
|
325
|
+
+ if (original_str == Qnil) {
|
326
|
+
+ fprintf(gc_data_file, "\n");
|
327
|
+
+ }
|
328
|
+
+ else {
|
329
|
+
+ VALUE str = StringValue(original_str);
|
330
|
+
+ char *p = RSTRING(str)->ptr;
|
331
|
+
+ fprintf(gc_data_file, "%s\n", p);
|
332
|
+
+ }
|
333
|
+
+ return original_str;
|
334
|
+
+}
|
335
|
+
+
|
336
|
+
+
|
337
|
+
static void
|
338
|
+
add_heap()
|
339
|
+
{
|
340
|
+
@@ -326,7 +592,7 @@
|
341
|
+
struct heaps_slot *p;
|
342
|
+
int length;
|
343
|
+
|
344
|
+
- heaps_length += HEAPS_INCREMENT;
|
345
|
+
+ heaps_length += heaps_increment;
|
346
|
+
length = heaps_length*sizeof(struct heaps_slot);
|
347
|
+
RUBY_CRITICAL(
|
348
|
+
if (heaps_used > 0) {
|
349
|
+
@@ -342,10 +608,10 @@
|
350
|
+
for (;;) {
|
351
|
+
RUBY_CRITICAL(p = (RVALUE*)malloc(sizeof(RVALUE)*(heap_slots+1)));
|
352
|
+
if (p == 0) {
|
353
|
+
- if (heap_slots == HEAP_MIN_SLOTS) {
|
354
|
+
+ if (heap_slots == heap_min_slots) {
|
355
|
+
rb_memerror();
|
356
|
+
}
|
357
|
+
- heap_slots = HEAP_MIN_SLOTS;
|
358
|
+
+ heap_slots = heap_min_slots;
|
359
|
+
continue;
|
360
|
+
}
|
361
|
+
heaps[heaps_used].membase = p;
|
362
|
+
@@ -361,8 +627,9 @@
|
363
|
+
if (lomem == 0 || lomem > p) lomem = p;
|
364
|
+
if (himem < pend) himem = pend;
|
365
|
+
heaps_used++;
|
366
|
+
- heap_slots *= 1.8;
|
367
|
+
- if (heap_slots <= 0) heap_slots = HEAP_MIN_SLOTS;
|
368
|
+
+ heap_slots += heap_slots_increment;
|
369
|
+
+ heap_slots_increment *= heap_slots_growth_factor;
|
370
|
+
+ if (heap_slots <= 0) heap_slots = heap_min_slots;
|
371
|
+
|
372
|
+
while (p < pend) {
|
373
|
+
p->as.free.flags = 0;
|
374
|
+
@@ -1015,6 +1282,39 @@
|
375
|
+
}
|
376
|
+
}
|
377
|
+
|
378
|
+
+static char* obj_type(int tp)
|
379
|
+
+{
|
380
|
+
+ switch (tp) {
|
381
|
+
+ case T_NIL : return "NIL";
|
382
|
+
+ case T_OBJECT : return "OBJECT";
|
383
|
+
+ case T_CLASS : return "CLASS";
|
384
|
+
+ case T_ICLASS : return "ICLASS";
|
385
|
+
+ case T_MODULE : return "MODULE";
|
386
|
+
+ case T_FLOAT : return "FLOAT";
|
387
|
+
+ case T_STRING : return "STRING";
|
388
|
+
+ case T_REGEXP : return "REGEXP";
|
389
|
+
+ case T_ARRAY : return "ARRAY";
|
390
|
+
+ case T_FIXNUM : return "FIXNUM";
|
391
|
+
+ case T_HASH : return "HASH";
|
392
|
+
+ case T_STRUCT : return "STRUCT";
|
393
|
+
+ case T_BIGNUM : return "BIGNUM";
|
394
|
+
+ case T_FILE : return "FILE";
|
395
|
+
+
|
396
|
+
+ case T_TRUE : return "TRUE";
|
397
|
+
+ case T_FALSE : return "FALSE";
|
398
|
+
+ case T_DATA : return "DATA";
|
399
|
+
+ case T_MATCH : return "MATCH";
|
400
|
+
+ case T_SYMBOL : return "SYMBOL";
|
401
|
+
+
|
402
|
+
+ case T_BLKTAG : return "BLKTAG";
|
403
|
+
+ case T_UNDEF : return "UNDEF";
|
404
|
+
+ case T_VARMAP : return "VARMAP";
|
405
|
+
+ case T_SCOPE : return "SCOPE";
|
406
|
+
+ case T_NODE : return "NODE";
|
407
|
+
+ default: return "____";
|
408
|
+
+ }
|
409
|
+
+}
|
410
|
+
+
|
411
|
+
static void
|
412
|
+
free_unused_heaps()
|
413
|
+
{
|
414
|
+
@@ -1045,12 +1345,21 @@
|
415
|
+
unsigned long live = 0;
|
416
|
+
unsigned long free_min = 0;
|
417
|
+
|
418
|
+
+ unsigned long really_freed = 0;
|
419
|
+
+ int free_counts[256];
|
420
|
+
+ int live_counts[256];
|
421
|
+
+ int do_gc_stats = gc_statistics & verbose_gc_stats;
|
422
|
+
+
|
423
|
+
for (i = 0; i < heaps_used; i++) {
|
424
|
+
free_min += heaps[i].limit;
|
425
|
+
}
|
426
|
+
free_min = free_min * 0.2;
|
427
|
+
- if (free_min < FREE_MIN)
|
428
|
+
- free_min = FREE_MIN;
|
429
|
+
+ if (free_min < heap_free_min)
|
430
|
+
+ free_min = heap_free_min;
|
431
|
+
+
|
432
|
+
+ if (do_gc_stats) {
|
433
|
+
+ for (i = 0 ; i< 256; i++) { free_counts[i] = live_counts[i] = 0; }
|
434
|
+
+ }
|
435
|
+
|
436
|
+
if (ruby_in_compile && ruby_parser_stack_on_heap()) {
|
437
|
+
/* should not reclaim nodes during compilation
|
438
|
+
@@ -1083,6 +1392,9 @@
|
439
|
+
if (!(p->as.basic.flags & FL_MARK)) {
|
440
|
+
if (p->as.basic.flags) {
|
441
|
+
obj_free((VALUE)p);
|
442
|
+
+ if (do_gc_stats) {
|
443
|
+
+ really_freed++;
|
444
|
+
+ }
|
445
|
+
}
|
446
|
+
if (need_call_final && FL_TEST(p, FL_FINALIZE)) {
|
447
|
+
p->as.free.flags = FL_MARK; /* remain marked */
|
448
|
+
@@ -1090,6 +1402,12 @@
|
449
|
+
final_list = p;
|
450
|
+
}
|
451
|
+
else {
|
452
|
+
+ if (do_gc_stats) {
|
453
|
+
+ int obt = p->as.basic.flags & T_MASK;
|
454
|
+
+ if (obt) {
|
455
|
+
+ free_counts[obt]++;
|
456
|
+
+ }
|
457
|
+
+ }
|
458
|
+
p->as.free.flags = 0;
|
459
|
+
p->as.free.next = freelist;
|
460
|
+
freelist = p;
|
461
|
+
@@ -1103,6 +1421,9 @@
|
462
|
+
else {
|
463
|
+
RBASIC(p)->flags &= ~FL_MARK;
|
464
|
+
live++;
|
465
|
+
+ if (do_gc_stats) {
|
466
|
+
+ live_counts[RANY((VALUE)p)->as.basic.flags & T_MASK]++;
|
467
|
+
+ }
|
468
|
+
}
|
469
|
+
p++;
|
470
|
+
}
|
471
|
+
@@ -1121,7 +1442,7 @@
|
472
|
+
}
|
473
|
+
if (malloc_increase > malloc_limit) {
|
474
|
+
malloc_limit += (malloc_increase - malloc_limit) * (double)live / (live + freed);
|
475
|
+
- if (malloc_limit < GC_MALLOC_LIMIT) malloc_limit = GC_MALLOC_LIMIT;
|
476
|
+
+ if (malloc_limit < initial_malloc_limit) malloc_limit = initial_malloc_limit;
|
477
|
+
}
|
478
|
+
malloc_increase = 0;
|
479
|
+
if (freed < free_min) {
|
480
|
+
@@ -1129,6 +1450,20 @@
|
481
|
+
}
|
482
|
+
during_gc = 0;
|
483
|
+
|
484
|
+
+ if (do_gc_stats) {
|
485
|
+
+ fprintf(gc_data_file, "objects processed: %.7d\n", live+freed);
|
486
|
+
+ fprintf(gc_data_file, "live objects : %.7d\n", live);
|
487
|
+
+ fprintf(gc_data_file, "freelist objects : %.7d\n", freed - really_freed);
|
488
|
+
+ fprintf(gc_data_file, "freed objects : %.7d\n", really_freed);
|
489
|
+
+ for(i=0; i<256; i++) {
|
490
|
+
+ if (free_counts[i]>0) {
|
491
|
+
+ fprintf(gc_data_file,
|
492
|
+
+ "kept %.7d / freed %.7d objects of type %s\n",
|
493
|
+
+ live_counts[i], free_counts[i], obj_type(i));
|
494
|
+
+ }
|
495
|
+
+ }
|
496
|
+
+ }
|
497
|
+
+
|
498
|
+
/* clear finalization list */
|
499
|
+
if (final_list) {
|
500
|
+
deferred_final_list = final_list;
|
501
|
+
@@ -1323,6 +1658,7 @@
|
502
|
+
struct gc_list *list;
|
503
|
+
struct FRAME * volatile frame; /* gcc 2.7.2.3 -O2 bug?? */
|
504
|
+
jmp_buf save_regs_gc_mark;
|
505
|
+
+ struct timeval gctv1, gctv2;
|
506
|
+
SET_STACK_END;
|
507
|
+
|
508
|
+
#ifdef HAVE_NATIVETHREAD
|
509
|
+
@@ -1339,6 +1675,14 @@
|
510
|
+
if (during_gc) return;
|
511
|
+
during_gc++;
|
512
|
+
|
513
|
+
+ if (gc_statistics) {
|
514
|
+
+ gc_collections++;
|
515
|
+
+ gettimeofday(&gctv1, NULL);
|
516
|
+
+ if (verbose_gc_stats) {
|
517
|
+
+ fprintf(gc_data_file, "Garbage collection started\n");
|
518
|
+
+ }
|
519
|
+
+ }
|
520
|
+
+
|
521
|
+
init_mark_stack();
|
522
|
+
|
523
|
+
gc_mark((VALUE)ruby_current_node, 0);
|
524
|
+
@@ -1414,6 +1758,17 @@
|
525
|
+
} while (!MARK_STACK_EMPTY);
|
526
|
+
|
527
|
+
gc_sweep();
|
528
|
+
+
|
529
|
+
+ if (gc_statistics) {
|
530
|
+
+ GC_TIME_TYPE musecs_used;
|
531
|
+
+ gettimeofday(&gctv2, NULL);
|
532
|
+
+ musecs_used = ((GC_TIME_TYPE)(gctv2.tv_sec - gctv1.tv_sec) * 1000000) + (gctv2.tv_usec - gctv1.tv_usec);
|
533
|
+
+ gc_time += musecs_used;
|
534
|
+
+
|
535
|
+
+ if (verbose_gc_stats) {
|
536
|
+
+ fprintf(gc_data_file, "GC time: %d msec\n", musecs_used / 1000);
|
537
|
+
+ }
|
538
|
+
+ }
|
539
|
+
}
|
540
|
+
|
541
|
+
void
|
542
|
+
@@ -1595,6 +1950,7 @@
|
543
|
+
if (!rb_gc_stack_start) {
|
544
|
+
Init_stack(0);
|
545
|
+
}
|
546
|
+
+ set_gc_parameters();
|
547
|
+
add_heap();
|
548
|
+
}
|
549
|
+
|
550
|
+
@@ -2064,6 +2420,14 @@
|
551
|
+
rb_define_singleton_method(rb_mGC, "disable", rb_gc_disable, 0);
|
552
|
+
rb_define_method(rb_mGC, "garbage_collect", rb_gc_start, 0);
|
553
|
+
|
554
|
+
+ rb_define_singleton_method(rb_mGC, "enable_stats", rb_gc_enable_stats, 0);
|
555
|
+
+ rb_define_singleton_method(rb_mGC, "disable_stats", rb_gc_disable_stats, 0);
|
556
|
+
+ rb_define_singleton_method(rb_mGC, "clear_stats", rb_gc_clear_stats, 0);
|
557
|
+
+ rb_define_singleton_method(rb_mGC, "collections", rb_gc_collections, 0);
|
558
|
+
+ rb_define_singleton_method(rb_mGC, "time", rb_gc_time, 0);
|
559
|
+
+ rb_define_singleton_method(rb_mGC, "dump", rb_gc_dump, 0);
|
560
|
+
+ rb_define_singleton_method(rb_mGC, "log", rb_gc_log, 1);
|
561
|
+
+
|
562
|
+
rb_mObSpace = rb_define_module("ObjectSpace");
|
563
|
+
rb_define_module_function(rb_mObSpace, "each_object", os_each_obj, -1);
|
564
|
+
rb_define_module_function(rb_mObSpace, "garbage_collect", rb_gc_start, 0);
|