railsbench.patch 14 KB

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