Why do Rust programs use more memory than the C, Haskell and OCaml versions?

(Note: this a a copy of my StackOverflow question)

I looked at how much RAM was used by Rust programs (with the top and massif commands) and I wonder why they use so much memory.

Here is an example:

use std::io;

fn main() {
    println!("What's your name?");
    let mut input = String::new();
    io::stdin().read_line(&mut input).unwrap();
    println!("Hello {}!", input);
}

I saw that 6 MB of memory was used before I input something.

Here is how I compiled and executed the program:

cargo build --release
./target/release/main

The equivalent C program:

#include <stdio.h>

int main(void) {
    printf("What's your name?\n");
    char input[100] = {0};
    scanf("%s", input);
    printf("Hello %s!\n", input);
    return 0;
}

only uses 0.6 MB. In this case, the Rust program uses 10 times more memory. In other cases, I saw that the Rust program uses 5 times more memory.

I also tested with other languages to compare.

The OCaml version:

let () =
    print_endline "What's your name?";
    let line = read_line () in
    print_string "Hello ";
    print_endline line

uses 1 MB.

The Haskell version:

main = do
    putStrLn "What's your name?"
    name <- getLine
    putStrLn ("Hello " ++ name ++ "!")

uses 3 MB.

The Python version:

print("What's your name?")
name = input()
print("Hello", name, "!")

uses 7 MB, almost the same as the Rust version!

I'm running Linux (ArchLinux) with Rust 1.3 (I also tried the nightly with similar results).

Here is more data from the htop command:

VIRT    RES     SHR     MEM%    Command
15572   2936    804     0.1     ocaml
21728   2732    2528    0.1     haskell
22540   7480    4308    0.2     python
4056    668     600     0.0     c
24180   6164    1928    0.2     rust
13060   1524    1387    0.0     cpp

Here are massif results.

For every program, I ran massif twice, as following:

valgrind --tool=massif --time-unit=B ./program
valgrind --tool=massif  --pages-as-heap=yes --time-unit=B ./program

Here are the results with all the programs (as shown by ms_print):

(links on the StackOverflow question - sorry, I cannot post more than 2 links since I am a new user)

Summary (ram usage):

|------------|----------|----------|----------|----------|----------|----------|
|            |     C    | Haskell  |   OCaml  |   Rust   |  Python  |    C++   |
|------------|----------|----------|----------|----------|----------|----------|
| First run  |    1 B   | 63.12 KB | 5.993 MB |   816 B  | 1.321 MB | 71.10 KB |
|------------|----------|----------|----------|----------|----------|----------|
| Second run | 6.031 MB | 24.20 MB | 17.14 MB | 25.60 MB | 27.43 MB | 14.74 MB |
|------------|----------|----------|----------|----------|----------|----------|

The first run is without the --pages-as-heap=yes parameter.

I also ran massif with the --stacks=yes option for C, C++ and Rust:

C version:

(link on the StackOverflow question)

(3.141 KB)

C++ version:

(72.63 KB)

Rust version:

(link on the StackOverflow question)

(8.602 KB)

What does explain such a huge difference between heap block allocation and page allocation in Rust?

For one, c is missing the massive standard library.

On my machine, the following c++ program uses ~1.6MB (optimized and unoptimized).

#include<string>
#include<iostream>

int main() {
    std::string name;
    std::cout << "What's your name?\n";
    std::cin >> name;
    std::cout << "Hello " << name << "!\n";
    return 0;
}

And the rust program uses 2.132 unoptimized and 1.88 with LTO and optimizations enabled.

(nightly, arch linux x86_64).

Actually, the Rust code equivalent to the C code would be:

#![feature(core_slice_ext)]
#![feature(lang_items)]
#![feature(libc)]
#![feature(no_std)]
#![feature(start)]
#![no_std]

extern crate libc;

extern "C" {
    fn printf(fmt: *const u8, ...) -> i32;
    fn scanf(fmt: *const u8, ...) -> i32;
}

#[start]
fn start(_argc: isize, _argv: *const *const u8) -> isize {
    unsafe {
        printf(b"What's your name?\n\0".as_ptr());
        let mut input = [0u8; 100];
        scanf(b"%s\0".as_ptr(), &mut input);
        printf(b"Hello %s!\n\0".as_ptr(), &input);
        0
    }
}

#[lang="eh_personality"] extern fn eh_personality() {}
#[lang="panic_fmt"] fn panic_fmt() -> ! { loop {} }
#[lang="stack_exhausted"] extern fn stack_exhausted() {}

The original Rust version gives me 968 KB used, the above version gives 376 KB.

Which begs the question, are you compiling in release mode?

7 Likes

I added my result for this C++ program in my first post.

I can see that the Rust version uses close to twice as much more memory than the C++ version.

This version uses less memory than the C version on my computer.

Yes, I compiled my version with cargo build --release

I can see that my Rust version uses 5-10 times more memory than yours on my computer.

On a nightly switching to the system allocator might make the comparison a bit more fair:

#![feature(alloc_system)]
extern crate alloc_system;
4 Likes

With this code, my Rust version of the program only uses a bit more memory than the C++ version, which is to be expected, I believe.

Does my original version uses more memory because it needs to load jemalloc in the process' memory?

Is this related to this issue?

Hi.

Can someone answer this question, please?

As someone said on this StackOverflow question, "rust has a somewhat high initial memory requirement for a compiled language with a supposedly zero-sized runtime".

Is this a known issue?
Will it be fixed in future Rust versions?

Thanks.

I've read on /r/rust today that jamalloc has a bigger initial memory usage of ~6Mb.