Cloning a Vec<String> significantly slower compared to C++?

94 points HanleyArnold

I am not very proficient in C++, but while attempting a comparison benchmark I came across an interesting result - cloning a vector of strings is significantly slower (between x5 and x10, depending on the platform tested) in Rust compared to C++.

I'm using the following code in C++ to clone the strings:

void foo(vector<string> strings) {

              
  auto start = high_resolution_clock::now(); // Start time

  for (int i = 0; i < 1000; ++i) {

    vector<string> temp(strings);

    if (strings[0].c_str() == temp[0].c_str()) {
      abort();
    }
  }

  auto end = high_resolution_clock::now(); // End time
  auto duration =
      duration_cast<microseconds>(end - start) / 1000; 
  cout << "Time taken for each iteration: " << duration.count() << " micros"
       << endl;
}

and this code for Rust:

fn foo(strings: Vec<String>) {
    let start = Instant::now(); // Start time

    for _ in 0..1000 {
        let _unused = strings.clone();
    }

    let duration = start.elapsed(); // Calculate duration
    println!("Time for iteration: {} micros", duration.as_micros() / 1000);
}

Running both with the contents of /usr/share/dict/words (235976 strings of varying sizes) yields very bad results for Rust. For example on my Intel Ubuntu, using g++ 11.4 and x86\_64, Rust clocks in at around 10ms, while C++ gets 1.8ms...

Does anybody have any idea where this difference can stem from? I also tried swapping my allocator for jemalloc but I'm getting similar differences

EDIT: I made sure that in the C++ implementation the strings actually get copied (i.e. c\_str() returns different addresses for the same element after the copy)

EDIT 2: Yes, I ran the rust code with --release, and the C++ code with -O3

EDIT 3: No, it is not a reasonable assumption that the compiler optimizes away the allocation. Even if that were the case - the loop time should have been zero microseconds and not 1000-10000 as it is in reality

EDIT 4: Thanks a lot for the help! As several commenters observed, C++ strings have the small string optimization implemented by default, so reading from /usr/share/dict/words gives it an unfair advantage. Switching the test to long arbitrary strings changed it, and I now get either comparable results, or a clear advantage for Rust, depending on the platform I test