railsbench 0.9.2 → 0.9.8
Sign up to get free protection for your applications and to get access to all the features.
- 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);
|