File: gc_stats.c

package info (click to toggle)
ocaml 5.3.0-3
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 43,124 kB
  • sloc: ml: 355,439; ansic: 51,636; sh: 25,098; asm: 5,413; makefile: 3,673; python: 919; javascript: 273; awk: 253; perl: 59; fortran: 21; cs: 9
file content (186 lines) | stat: -rw-r--r-- 7,246 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
/**************************************************************************/
/*                                                                        */
/*                                 OCaml                                  */
/*                                                                        */
/*             Gabriel Scherer, projet Partout, INRIA Saclay              */
/*                                                                        */
/*   Copyright 2022 Institut National de Recherche en Informatique et     */
/*     en Automatique.                                                    */
/*                                                                        */
/*   All rights reserved.  This file is distributed under the terms of    */
/*   the GNU Lesser General Public License version 2.1, with the          */
/*   special exception on linking described in the file LICENSE.          */
/*                                                                        */
/**************************************************************************/

#define CAML_INTERNALS

#include "caml/gc_stats.h"
#include "caml/memory.h"
#include "caml/minor_gc.h"
#include "caml/platform.h"
#include "caml/shared_heap.h"
#include "caml/startup_aux.h"

Caml_inline intnat intnat_max(intnat a, intnat b) {
  return (a > b ? a : b);
}

void caml_accum_heap_stats(struct heap_stats* acc, const struct heap_stats* h)
{
  acc->pool_words += h->pool_words;
  acc->pool_max_words = intnat_max(acc->pool_max_words, acc->pool_words);
  acc->pool_max_words = intnat_max(acc->pool_max_words, h->pool_max_words);
  acc->pool_live_words += h->pool_live_words;
  acc->pool_live_blocks += h->pool_live_blocks;
  acc->pool_frag_words += h->pool_frag_words;
  acc->large_words += h->large_words;
  acc->large_max_words = intnat_max(acc->large_max_words, acc->large_words);
  acc->large_max_words = intnat_max(acc->large_max_words, h->large_max_words);
  acc->large_blocks += h->large_blocks;
}

void caml_remove_heap_stats(struct heap_stats* acc, const struct heap_stats* h)
{
  acc->pool_words -= h->pool_words;
  acc->pool_live_words -= h->pool_live_words;
  acc->pool_live_blocks -= h->pool_live_blocks;
  acc->pool_frag_words -= h->pool_frag_words;
  acc->large_words -= h->large_words;
  acc->large_blocks -= h->large_blocks;
}

void caml_accum_alloc_stats(
  struct alloc_stats* acc,
  const struct alloc_stats* s)
{
  acc->minor_words += s->minor_words;
  acc->promoted_words += s->promoted_words;
  acc->major_words += s->major_words;
  acc->forced_major_collections += s->forced_major_collections;
}

void caml_collect_alloc_stats_sample(
  caml_domain_state *local,
  struct alloc_stats *sample)
{
  sample->minor_words = local->stat_minor_words;
  sample->promoted_words = local->stat_promoted_words;
  sample->major_words = local->stat_major_words;
  sample->forced_major_collections = local->stat_forced_major_collections;
}

void caml_reset_domain_alloc_stats(caml_domain_state *local)
{
  local->stat_minor_words = 0;
  local->stat_promoted_words = 0;
  local->stat_major_words = 0;
  local->stat_forced_major_collections = 0;
}

/* We handle orphaning allocation stats here,
   whereas orphaning of heap stats is done in shared_heap.c */
static caml_plat_mutex orphan_lock = CAML_PLAT_MUTEX_INITIALIZER;
static struct alloc_stats orphaned_alloc_stats = {0,};

void caml_accum_orphan_alloc_stats(struct alloc_stats *acc) {
  caml_plat_lock_blocking(&orphan_lock);
  caml_accum_alloc_stats(acc, &orphaned_alloc_stats);
  caml_plat_unlock(&orphan_lock);
}

void caml_orphan_alloc_stats(caml_domain_state *domain) {
  struct alloc_stats alloc_stats;

  /* move alloc stats from the domain to [alloc_stats] */
  caml_collect_alloc_stats_sample(domain, &alloc_stats);
  caml_reset_domain_alloc_stats(domain);

  /* push them into the orphan stats */
  caml_plat_lock_blocking(&orphan_lock);
  caml_accum_alloc_stats(&orphaned_alloc_stats, &alloc_stats);
  caml_plat_unlock(&orphan_lock);
}

/* The "sampled stats" of a domain are a recent copy of its
   domain-local stats, accessed without synchronization and only
   updated ("sampled") during stop-the-world events -- each minor
   collection, major cycle (which potentially includes compaction),
   all of these events could happen during domain termination. */
static struct gc_stats* sampled_gc_stats;

void caml_init_gc_stats (uintnat max_domains)
{
  sampled_gc_stats =
    caml_stat_calloc_noexc(max_domains, sizeof(struct gc_stats));
  if (sampled_gc_stats == NULL)
    caml_fatal_error("Failed to allocate sampled_gc_stats");
}

/* Update the sampled stats for the given domain during a STW section. */
void caml_collect_gc_stats_sample_stw(caml_domain_state* domain)
{
  struct gc_stats* stats = &sampled_gc_stats[domain->id];
  if (caml_domain_terminating(domain)) {
    /* If the domain is terminating, we should not update the sample
       with accurate domain-local data, but instead clear the sample
       so that a new domain spawning there in the future can start
       with empty stats.

       The current stats will also be 'orphaned' during domain
       termination, so they will remain accounted for in the global
       statistics. (Orphaning right now would be correct but
       insufficient as further stat updates may come after the current
       STW section.)  */
    memset(stats, 0, sizeof(*stats));
  } else {
    caml_collect_alloc_stats_sample(domain, &stats->alloc_stats);
    caml_collect_heap_stats_sample(domain->shared_heap, &stats->heap_stats);
  }
}

/* Compute global stats for the whole runtime. */
void caml_compute_gc_stats(struct gc_stats* buf)
{
  intnat pool_max = 0, large_max = 0;
  int my_id = Caml_state->id;
  memset(buf, 0, sizeof(*buf));

  caml_accum_orphan_heap_stats(&buf->heap_stats);
  caml_accum_orphan_alloc_stats(&buf->alloc_stats);

  /* The instantaneous maximum heap size cannot be computed
     from per-domain statistics, and would be very expensive
     to maintain directly. Here, we just sum the per-domain
     maxima, which is completely wrong.

     FIXME: maybe maintain coarse global maxima?

     The summation starts here from the orphan-heap maxima.
  */
  pool_max = buf->heap_stats.pool_max_words;
  large_max = buf->heap_stats.large_max_words;

  for (int i = 0; i < caml_params->max_domains; i++) {
    /* For allocation stats, we use the live stats of the current domain
       and the sampled stats of other domains.

       For the heap stats, we always used the sampled stats. */
    struct gc_stats* s = &sampled_gc_stats[i];
    if (i != my_id) {
      caml_accum_alloc_stats(&buf->alloc_stats, &s->alloc_stats);
      caml_accum_heap_stats(&buf->heap_stats, &s->heap_stats);
    }
    else {
      struct alloc_stats alloc_stats;
      caml_collect_alloc_stats_sample(Caml_state, &alloc_stats);
      caml_accum_alloc_stats(&buf->alloc_stats, &alloc_stats);
      caml_accum_heap_stats(&buf->heap_stats, &s->heap_stats);
      //FIXME use live heap stats instead of sampled heap stats below?
    }
    pool_max += s->heap_stats.pool_max_words;
    large_max += s->heap_stats.large_max_words;
  }
  buf->heap_stats.pool_max_words = pool_max;
  buf->heap_stats.large_max_words = large_max;
}