railsbench.patch 12 KB

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