Does Rust has any gc time in run time?

I am new to programming, in my comprehend(seems to be wrong?), gc time has two part: 1. sweep the variale need to drop; 2. drop the vraiable. Rust needs no time for the part 1, But dose it need time for the part 2? If this understanding is right, in gerneral scene in a language having gc, how much percent of the part1 has in the whole gc time?
I am using a language having gc, while running code, there construct many big objects. So I have high percent gc time. I like to know how much performace gain I would get if these codes run on rust.

Cleaning up resources in itself is usually not called GC. Instead, GC normally refers to a system where there is a separate memory manager process/routine running periodically and checking what objects need to be freed. It is usually understood purely in the context of memory management, and it isn't concerned with other resources, furthermore it isn't required to be deterministic.

In contrast, Rust has no such system. The resource handling mechanism in Rust is much more like RAII in C++: the compiler tracks, at compile time, which values are alive, uninitialized, moved from, etc., and when a live value goes out of scope or its lifetime otherwise ends, the compiler will insert a call to its Drop impl. This is deterministic and when drops happen can be known simply by looking at the source text.

Of course, when Drop::drop() does something for a type (e.g., frees memory, closes a file handle, ends a network connection, whatever), then naturally that can't be a no-op, and it requires some work. That is simply unavoidable (if you don't want to leak all resources).

There is no "general scheme", and this is impossible to tell without measuring it for a specific piece of code.

This is also impossible to tell without actually measuring. Usually, if you allocate many objects, you can expect GC pressure to be a major fraction of your runtime in a GC language, but it's not like we magically know that rewriting your code in Rust will be, say, 50% faster.

8 Likes

Thanks much for your reply.

Here is an example:

fn generate_data() {
    let t1 = Instant::now();
    let data1 = vec![1; 2_000_000_000];
    let data2 = vec![1; 2_000_000_000];
    let data3 = vec![1; 2_000_000_000];
    let t2 = Instant::now();
    let datas = vec![data1, data2, data3];
    println!("run time {}", (t2 - t1).as_millis());
    
    let t3 = Instant::now();
    generate_time(datas);
    let t4 = Instant::now();
    println!("drop time {}", (t4 - t3).as_millis());
}

fn generate_time(datas: Vec<Vec<i32>>) {}

run time 2885
drop time 698

Here generate_time has just the function to Drop::drop() the data generated before(in my understanding, maybe wrong), so can I say the Drop::drop() almost takes 700ms?

Here is the same example in Julia:

function generate_data()
    data1 = fill(Int32(1), 2_000_000_000)
    data2 = fill(Int32(1), 2_000_000_000)
    data3 = fill(Int32(1), 2_000_000_000)
    data1 = nothing
    data2 = nothing
    data3 = nothing
    GC.gc()
end

GC.gc(true)
@time generate_data()
4.399614 seconds (6 allocations: 22.352 GiB, 25.31% gc time)

The last line calls gc, when the call this, the RAM used of the computer reduced, so I guess gc time and drop time both happened. As can be seen, the whold gc time (gc + drop) in Julia is 1s, and the whole gc time (0 + drop) is 0.7s. By camparing this, can I say that when these codes run in rust, the "gc" time would reduce 0.3s?

Calling destructors for "gc time" is not correct because it is not garbage collection. Gc refers to a specific method of cleaning up memory, and Rust uses a different method.

Anyway, running your code on my laptop in release mode, I get these numbers:

run time 7986
drop time 457

However, my laptop froze for a second or so in the middle of the test while moving the memory used by all other processes to disk (this is called swapping), since the test uses 24 GB of memory, and my laptop has 24 GB of ram. Swapping like this is very expensive and it is the reason it takes so long.

Removing one of the three vectors and reducing the usage to 16 GB of memory, I get these running times:

run time 1696
drop time 24

As you can see, this reduction is much bigger than one third, and this is because I didn't run out of ram during the test.

Most of that time was probably spent by the operating system rather than the drop function.

7 Likes

First of all, dropping different types may have different runtime costs (obviously). Dropping a vector of primitives only requires a single deallocation, since vectors are contiguous, and there's nothing else to be transitively cleaned up (since a primitive integer doesn't hold on to any other resources, it's a plain old value). Thus, it's approximately a constant-time operation, on the same order of magnitude as a comparable single memory allocation (typically in the low microseconds on a mid-range mainstream desktop/laptop machine).

Normally, deallocating a vector or freeing/allocating a single block of memory definitely doesn't take almost a second under reasonable circumstances. Of course, if you are intentionally testing the limits of the system by requesting way more memory than a well-designed program should ever need (no, please absolutely do not load 6 billion elements into memory at once in real code!), then you may get artifacts of the system trying to deal with it. This is not by any means indicative of the performance characteristics of Rust in itself, however. So no, you are not correct in saying that "Drop takes 700 milliseconds".

2 Likes

I test these codes in computer having a ram of 128GB:

macro_rules! t {
    ($a: stmt) => {
        let t = Instant::now();
        $a
        println!("{:?}", t.elapsed());
    }
}


let data1 = vec![1; 2_000_000_000];
let data2 = vec![1; 2_000_000_000];
let data3 = vec![1; 2_000_000_000];
let datas = vec![data1, data2, data3];
t!(drop(datas));
768.7557ms //this is output


let data1 = vec![1; 2_000_000_000];
t!(drop(data1));
312.6862ms

As you can see, the time for dropping in my computer did not reduce that much as yours. I guess it is because of the difference in hardware of computers? So, if your computer have enough ram, then your above output should be:

run time 1696 * 3
drop time 24 * 3

run time 1696
drop time 24

This is my computer config

Julia Version 1.8.2
Commit 36034abf26 (2022-09-29 15:21 UTC)
Platform Info:
  OS: Windows (x86_64-w64-mingw32)
  CPU: 20 × 12th Gen Intel(R) Core(TM) i7-12700K
  WORD_SIZE: 64
  LIBM: libopenlibm
  LLVM: libLLVM-13.0.1 (ORCJIT, goldmont)
  Threads: 14 on 20 virtual cores
Environment:
  JULIA_NUM_THREADS = 14

I am curious what hardware difference can make 10x gap. Maybe you can try to dropping two vectors from the three?