I wrote a basic Hippity Hop program in C, Python, and OCaml. Granted, this is probably not a very good benchmark of these three languages. But the results I got were something like this:
- Python: .350 seconds
- C: .050 seconds
- interpreted OCaml: .040 seconds
- compiled OCaml: .010
The python performance doesn’t really surprise me, but I’m rather shocked at how fast the OCaml is (especially the interpreted version). For comparison, I’ll post the C version and the OCaml version.
C
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
long get_count(char *name);
int main(int argc, char *argv[])
{
if (argc != 2){
printf("Filename must be specified as a positional argument.\n");
exit(EXIT_FAILURE);
}
long count_no = get_count(argv[1]);
int i;
for (i = 1; i <= count_no; i++){
if (((i % 3) == 0) && ((i % 5) == 0)){
printf("Hop\n");
continue;
}
if ((i % 3) == 0){
printf("Hoppity\n");
}
if ((i % 5) == 0){
printf("Hophop\n");
}
}
return 0;
}
long get_count(char *name){
FILE *fileptr = fopen(name, "r");
if (!fileptr){
printf("Unable to open file %s.\n", name);
exit(EXIT_FAILURE);
}
size_t text_len = 20;
char *file_text = calloc(text_len, sizeof(char));
while (!feof(fileptr)){
fread(file_text, sizeof(char), text_len, fileptr);
assert(!ferror(fileptr));
text_len += 20;
file_text = realloc(file_text, text_len * sizeof(char));
}
long file_as_int = strtol(file_text, NULL, 10);
free(file_text);
return file_as_int;
}
OCaml
open String;;
let trim str =
if str = "" then "" else
let search_pos init p next =
let rec search i =
if p i then raise(Failure "empty") else
match str.[i] with
| ' ' | '\n' | '\r' | '\t' -> search (next i)
| _ -> i
in
search init
in
let len = String.length str in
try
let left = search_pos 0 (fun i -> i >= len) (succ)
and right = search_pos (len - 1) (fun i -> i < 0) (pred)
in
String.sub str left (right - left + 1)
with
| Failure "empty" -> ""
;;
let rec iterate_over_numbers curr_num max_num =
(
if curr_num <= max_num then (
if ((curr_num mod 3) == 0) && ((curr_num mod 5) == 0) then
print_endline "Hop"
else if (curr_num mod 3) == 0 then
print_endline "Hoppity"
else if (curr_num mod 5) == 0 then
print_endline "Hophop";
iterate_over_numbers (curr_num + 1) max_num
))
;;
let fname = Sys.argv.(1);;
let infile = open_in fname;;
let file_text = trim (input_line infile);;
close_in infile;;
let input_number = int_of_string file_text;;
iterate_over_numbers 1 input_number;;
But I’m curious to know why I’m getting these results. Am I doing something dumb in my C program, or is this just something OCaml is faster at? It seems to me a bit strange that an interpreted program is running a little faster than the C version, and the compiled program is running 5 times as fast.
Your C code isn’t the equivalent of the OCaml code – you used ‘else if’ in the OCaml to avoid having to recompute moduli quite so much.
There’s an awful lot of code in that ‘read the long integer’. Why not just use
fscanf(); it skips blanks and all that automatically, and avoids you doing themalloc()etc. I don’t often recommend usingfscanf(), but this looks like a setup for it – a single line, possibly with spaces either side, no funny stuff.Curiosity killed the cat – but in this case, not the Leopard.
I downloaded OCaml 3.11.1 for MacOS X Intel and copied the OCaml code from the question into xxx.ml (OCaml), and compiled that into an object file xxx (using “ocamlc -o xxx xxx.ml”); I copied the C code verbatim into yyy.c and created a variant zzz.c using
fscanf()andfclose(), and compiled them using “gcc -O -o yyy yyy.c” and “gcc -O -o zzz zzz.c”. I created a file ‘file3’ containing: “987654” plus a newline. I created a shell script runthem.sh as shown. Note that ‘time’ is a pesky command that thinks its output must go to stderr even when you’d rather it didn’t – you have to work quite hard to get the output where you want it to go. (The range command generates numbers in the given range, inclusive – hence 11 values per program.)I ran all this on a modern MacBook Pro (3 GHz Core 2 Duo etc, 4GB RAM) running Leopard (10.5.8). The timings I got are shown by:
I don’t see the OCaml code running faster than the C code. I ran the tests with smaller numbers in the file that was read, and the results were similarly in favour of the C code:
Stop number: 345
Stop number: 87654
Obviously, YMMV – but it appears that OCaml is slower than C by a considerable margin, but that if the number in the given file is small enough, then start up and file reading dominate the process time.
The C timings, especially at the smaller numbers, are so fast that they are not all that reliable.