railsbench.patch 14 KB

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