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


#1

(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?


#2

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).


#3

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?


#4

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.


#5

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.


#6

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

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

#7

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?


#8

Is this related to this issue?


#9

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.


#10

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