Rickard Lindberg | Blog | Projects

Fork Exec Overhead

Written by Rickard Lindberg Profile picture of Rickard. in #linux #c #aoc

The other day I was trying to solve Day 4 - Advent of Code 2015.

My initial brute force approach looked something like this (but written in C):

for result in {1..1000000}; do
    echo -n "${puzzle_input}${result}" | md5sum
done

That is, compute the input for increasing numbers and send it to md5sum to compute the hash. (And stop if the hash matches the criteria.)

This took a very long time, but eventually I got the correct result.

This didn't feel like the right solution. What did I do wrong? I had a conversation with my friend, and he couldn't see a more clever solution either. Somehow we decided to try the same brute force approach but instead of computing the MD5 hash by using an external program, md5sum, we used a library. And now it was much faster. Even if we wrote that version in Python. Later I rewrote it in C using the openssl library to compute the MD5 sum. Even brute forcing part 2 finishes in around a second.

Perhaps that was what we were supposed to learn from this day? That spawning processes is expensive. Or there is a more clever solution yet to be found.

I had not thought that spawning process would create so much overhead. So I decided to dig a little deeper.

Here is a program that performs a plus 2 operation:

int main(int argc, char **argv) {
    int number = argv[1][0] - '0';
    return number + 2;
}

It assumes a single digit number is passed as the first argument. Then it returns the answer in the exit code:

$ ./plustwo 4
$ echo $?
6
$ ./plustwo 7
$ echo $?
9

The equivalent C function is this:

int plus_two(int number) {
    return number + 2;
}

Then I measured how long they took respectively using my rlprofiler. Here is the measurement code:

void measure() {
    unsigned int index;
    RLPROFILER_ZONE_BEGIN("plus_two_spawn");
    for (index = 0; index < NUM_LOOPS; index++) {
        assert(plus_two_spawn(3) == 5);
    }
    RLPROFILER_ZONE_END("plus_two_spawn");
    RLPROFILER_ZONE_BEGIN("plus_two");
    for (index = 0; index < NUM_LOOPS; index++) {
        assert(plus_two(4) == 6);
    }
    RLPROFILER_ZONE_END("plus_two");
}

The plustwo program is wrapped like this:

int plus_two_spawn(int number) {
    int result = 0;
    int status;
    char number_arg[2];
    number_arg[0] = '0' + number;
    number_arg[1] = '\0';
    pid_t pid;
    pid = fork();
    assert(pid != -1);
    if (pid == 0) {
        assert(execlp("./plustwo", "./plustwo", number_arg, NULL) != -1);
    } else {
        wait(&status);
        result = WEXITSTATUS(status);
    }
    return result;
}

That is, it does a fork+exec and obtains the result from the status code.

When I run this with NUM_LOOPS set to 100000, I get this:

========================================================================
Profiler
========================================================================
system clock frequency        = 1000000Hz
estimated cpu clock frequency = 2688015791Hz
elapsed time                  = 38786.33ms
------------------------------------------------------------------------
label          | hit count | exclusive            | total
---------------+-----------+----------------------+---------------------
plus_two_spawn |         1 | 100.00% (38786.07ms) | 100.00% (38786.07ms)
plus_two       |         1 |   0.00% (    0.25ms) |   0.00% (    0.25ms)
========================================================================

This means, that on average, the overhead of fork+exec is (38786.07-0.25)/100000 = 0.3878582 (~0.39 ms).

So if I need to brute-force check something 1 million times, the overhead of doing it via fork+exec is (0.3878582*1000000)/1000/60 = 6.4643033333333335 (6.46 minutes).

This was a fun exercise.

↑ 2026-11-14 week 46

What is Rickard working on and thinking about right now?

Every month I write a newsletter about just that. You will get updates about my current projects and thoughts about programming, and also get a chance to hit reply and interact with me. Subscribe to it below.

Powered by Buttondown (My Newsletter)

Profile picture of Rickard.

I'm Rickard Lindberg from Sweden. This is my home on the web. I like programming. I like both the craft of it and also to write software that solves problems. I also like running.

Me elsewhere: GitHub, Mastodon.