Item 12 asks five questions and says to answer them with a spike rather than a rewrite. spike/embed/ is that spike: one script, six binaries, each one built to fail loudly at the thing it is asking about. It is deliberately not a dune target -- the root dune only excludes old-ocaml/, so a dune file here would land in @default and make the spike part of the build. It drives ocamlfind and clang by hand against the flan.cmxa dune already produces. The probes, in the order they would kill the idea: the smallest possible link, a C main() reaching one OCaml function; the whole compiler linked in and doing real work; the same again with lib/dynload_stubs.c from the unmerged dlopen branch, because that is the only C the compiler itself is built from; the game keeping the main thread while caml_startup happens on a pthread beside it; the SIGSEGV disposition read on both sides of caml_startup; and an 8 MiB arena checked byte for byte across a compaction. No result is written down yet. This is the apparatus.
55 lines
1.7 KiB
C
55 lines
1.7 KiB
C
/* Step 2: the whole compiler inside a C binary, and what it costs to start.
|
|
*
|
|
* The startup number is measured around caml_startup itself, not with time(1)
|
|
* on the process -- what a merged dev build would pay is the runtime coming up
|
|
* and every module initialiser running, not exec and dynamic linking, which it
|
|
* pays today anyway. */
|
|
#include <caml/callback.h>
|
|
#include <caml/alloc.h>
|
|
#include <caml/mlvalues.h>
|
|
#include <stdio.h>
|
|
#include <time.h>
|
|
|
|
#ifndef FLANSRC
|
|
#define FLANSRC "test/programs/edn.flan"
|
|
#endif
|
|
|
|
static double ms_since(struct timespec a) {
|
|
struct timespec b;
|
|
clock_gettime(CLOCK_MONOTONIC, &b);
|
|
return (b.tv_sec - a.tv_sec) * 1e3 + (b.tv_nsec - a.tv_nsec) / 1e6;
|
|
}
|
|
|
|
static const value *need(const char *n) {
|
|
const value *f = caml_named_value(n);
|
|
if (!f) fprintf(stderr, "spike: %s not registered\n", n);
|
|
return f;
|
|
}
|
|
|
|
int main(int argc, char **argv) {
|
|
struct timespec t0;
|
|
const char *src = argc > 1 ? argv[1] : FLANSRC;
|
|
const value *f;
|
|
|
|
clock_gettime(CLOCK_MONOTONIC, &t0);
|
|
caml_startup(argv);
|
|
printf("caml_startup (runtime + every module initialiser): %.3f ms\n", ms_since(t0));
|
|
|
|
f = need("spike_footprint");
|
|
if (f) printf("linked-module footprint: %s\n", String_val(caml_callback(*f, Val_unit)));
|
|
|
|
f = need("spike_compile");
|
|
if (f) {
|
|
clock_gettime(CLOCK_MONOTONIC, &t0);
|
|
printf("%s\n", String_val(caml_callback(*f, caml_copy_string(src))));
|
|
printf("first in-process compile (read+parse+check+emit): %.3f ms\n", ms_since(t0));
|
|
|
|
clock_gettime(CLOCK_MONOTONIC, &t0);
|
|
caml_callback(*f, caml_copy_string(src));
|
|
printf("second, warm: %.3f ms\n", ms_since(t0));
|
|
}
|
|
|
|
printf("spike: C main() still owns the process\n");
|
|
return 0;
|
|
}
|