5

I'm parallelizing some work in my OCaml program (with parmap) but I would prefer not to hard code the number of cores into my application. Is there a way to get the number of cores at runtime? I would prefer not to add any more dependencies (nothing beyond parmap or JS's core). I have a feeling I'm over looking some simple call in stdlib...

EDIT: it doesn't have to be portable. Working on linux is good enough.

rgrinberg
  • 9,638
  • 7
  • 27
  • 44
  • 1
    if you don't find anything else (not advocating this as a good example, but something I've hacked before) you can always parse `proc` to find it. – Kristopher Micinski Apr 29 '13 at 00:04
  • Ya that would be the worst case scenario... – rgrinberg Apr 29 '13 at 00:22
  • I would either simply wrap `cpuinfo` with a clean interface (I know several libraries which do this, but not in OCaml) or try using FFI from [one from C](http://stackoverflow.com/questions/4586405/get-number-of-cpus-in-linux-using-c). (I know you said you preferred to avoid the second solution, but I've used something similar for profiling in OCaml before.) Note that even that question has wide agreement that parsing `proc` isn't such a bad thing. – Kristopher Micinski Apr 29 '13 at 00:27
  • You probably should fork parmap and include a function to get the number of cores as an imported C function. See [there](http://stackoverflow.com/questions/150355/programmatically-find-the-number-of-cores-on-a-machine) for details as to how to detect the number of cores in C on several platforms. Personally I would change the `Setcore` module to include that function. – didierc Apr 29 '13 at 05:01
  • Then of course push back your change upstream for inclusion :) – didierc Apr 29 '13 at 05:03

1 Answers1

8

I once had the same question. That's what I eventually came with (I didn't want C bindings):

let cpu_count () = 
  try match Sys.os_type with 
  | "Win32" -> int_of_string (Sys.getenv "NUMBER_OF_PROCESSORS") 
  | _ ->
      let i = Unix.open_process_in "getconf _NPROCESSORS_ONLN" in
      let close () = ignore (Unix.close_process_in i) in
      try Scanf.fscanf i "%d" (fun n -> close (); n) with e -> close (); raise e
  with
  | Not_found | Sys_error _ | Failure _ | Scanf.Scan_failure _ 
  | End_of_file | Unix.Unix_error (_, _, _) -> 1

If you don't want Unix you could replace open_process_in by a Sys.command writing to a temporary file. Tested on linux and osx, reported to work on mingw but not on cygwin at the time.

Update. Note that this doesn't work on freebsd where as mentioned here you need to use sysctl -n hw.ncpu. However since Sys.os_type doesn't have the right granularity you'd need to conditionalize on the result of uname -s whenever Sys.os_type is different from Win32.

Daniel Bünzli
  • 5,119
  • 20
  • 21
  • 1
    I checked how `parmap` calculates this, and it seems the default is just `2`. :(, you may want to add your suggestion to that project. Cheers. – nlucaroni Apr 29 '13 at 17:29
  • Yes I forgot to mention that in post since I've actually did try parmap's `get_default_processors` (or something like that) function and it did not work as advertised. – rgrinberg Apr 29 '13 at 17:59
  • This doesn't return the number of *cores*. It returns the number of *processors*. If your machine has hyperthreading, you'll get (say) 36 processors even though there are only 18 cores. – Lucian Wischik Dec 09 '21 at 14:30