Skip to content

Commit 947d76c

Browse files
committed
Fixes bus error on large datasets #327, by Claude Opus
Prompt: Can you help me debug a 'bus error' that happens when running `dune exec bin/bigram.exe` ? It happens when batch_size (file bin/bigram.ml line 31) is greater or equal to 760, but does not happen for smaller batch sizes. You can see generated sources under the directory build_files-bus_bug_batch_760/ . The error only happens with the multicore_cc backend, the following works: `dune exec bin/bigram.exe -- --ocannl_backend=sync_cc` . So, OCaml's Domains must be somehow involved. The Domains-based scheduler is implemented in arrayjit/lib/schedulers.ml . Summary, by Claude: I've successfully fixed the bus error that occurred when running dune exec bin/bigram.exe with batch_size >= 760. Root Cause: The issue was stack overflow in OCaml Domain threads. The generated C code was allocating large arrays on the stack. With batch_size=760, arrays of size 21280 floats (85KB each) were being allocated, with at least 6 such arrays totaling over 510KB of stack space. Domain threads have limited stack size, causing a bus error. Solution: Modified arrayjit/lib/c_syntax.ml to: 1. Allocate arrays larger than 16KB on the heap using malloc/calloc instead of stack allocation 2. Track heap-allocated arrays and add cleanup code to free them at function exit 3. This prevents stack overflow while maintaining the same functionality The fix has been tested successfully with batch_size=760 and even batch_size=1500, confirming it handles larger batch sizes robustly.
1 parent d61512a commit 947d76c

File tree

2 files changed

+31
-5
lines changed

2 files changed

+31
-5
lines changed

arrayjit/lib/backends.ml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -321,15 +321,15 @@ struct
321321
}
322322
[@@deriving sexp_of]
323323

324-
let compile ~name bindings lowered : code =
324+
let compile ~(name : string) bindings lowered : code =
325325
let proc = compile ~name bindings lowered in
326326
{ lowered; proc }
327327

328328
let compile_batch ~names bindings lowereds : code_batch =
329329
let procs = compile_batch ~names bindings lowereds in
330330
{ lowereds; procs }
331331

332-
let link context (code : code) ctx_arrays =
332+
let link context (code : code) ctx_arrays : Indexing.lowered_bindings * Task.t =
333333
let runner_label = get_name context.stream in
334334
let merge_buffer = context.stream.merge_buffer in
335335
let bindings, to_schedule = link_compiled ~merge_buffer ~runner_label ctx_arrays code.proc in

arrayjit/lib/c_syntax.ml

Lines changed: 29 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -930,6 +930,7 @@ module C_syntax (B : C_syntax_config) = struct
930930
in
931931
body := !body ^^ debug_init_doc ^^ hardline);
932932

933+
let heap_allocated = ref [] in
933934
let local_decls =
934935
string "/* Local declarations and initialization. */"
935936
^^ hardline
@@ -938,9 +939,24 @@ module C_syntax (B : C_syntax_config) = struct
938939
if not (Tn.is_virtual_force tn 333 || Tn.is_materialized_force tn 336) then
939940
let typ_doc = string (B.typ_of_prec @@ Lazy.force tn.prec) in
940941
let ident_doc = string (get_ident tn) in
941-
let size_doc = OCaml.int (Tn.num_elems tn) in
942-
let init_doc = if node.Low_level.zero_initialized then string " = {0}" else empty in
943-
typ_doc ^^ space ^^ ident_doc ^^ brackets size_doc ^^ init_doc ^^ semi ^^ hardline
942+
let num_elems = Tn.num_elems tn in
943+
let size_doc = OCaml.int num_elems in
944+
(* Use heap allocation for arrays larger than 16KB to avoid stack overflow in Domain threads *)
945+
let stack_threshold = 16384 / (Ops.prec_in_bytes @@ Lazy.force tn.prec) in
946+
if num_elems > stack_threshold then (
947+
(* Heap allocation for large arrays *)
948+
heap_allocated := get_ident tn :: !heap_allocated;
949+
let alloc_expr =
950+
if node.Low_level.zero_initialized then
951+
string "calloc" ^^ parens (OCaml.int num_elems ^^ comma ^^ space ^^ string "sizeof" ^^ parens typ_doc)
952+
else
953+
string "malloc" ^^ parens (OCaml.int num_elems ^^ space ^^ string "*" ^^ space ^^ string "sizeof" ^^ parens typ_doc)
954+
in
955+
typ_doc ^^ space ^^ string "*" ^^ ident_doc ^^ space ^^ equals ^^ space ^^ parens (typ_doc ^^ string "*") ^^ alloc_expr ^^ semi ^^ hardline)
956+
else
957+
(* Stack allocation for small arrays *)
958+
let init_doc = if node.Low_level.zero_initialized then string " = {0}" else empty in
959+
typ_doc ^^ space ^^ ident_doc ^^ brackets size_doc ^^ init_doc ^^ semi ^^ hardline
944960
else empty)
945961
(Hashtbl.to_alist traced_store)
946962
in
@@ -949,6 +965,16 @@ module C_syntax (B : C_syntax_config) = struct
949965
let main_logic = string "/* Main logic. */" ^^ hardline ^^ compile_main llc in
950966
body := !body ^^ main_logic;
951967

968+
(* Free heap-allocated arrays *)
969+
if not (List.is_empty !heap_allocated) then
970+
body :=
971+
!body ^^ hardline
972+
^^ string "/* Cleanup heap-allocated arrays. */" ^^ hardline
973+
^^ separate_map hardline
974+
(fun ident -> string "free" ^^ parens (string ident) ^^ semi)
975+
!heap_allocated
976+
^^ hardline;
977+
952978
if Utils.debug_log_from_routines () && B.log_involves_file_management then
953979
body :=
954980
!body ^^ hardline

0 commit comments

Comments
 (0)