Yandex进行了测量,C ++比Rust更快更安全

剧透:C ++既不快也不慢,通常这不是重点。本文是揭穿大型俄罗斯公司关于Rust语言神话的光荣传统的延续。上一个是“ 比Rust,Mail.Ru Group进行测量要快 ”。


最近,我试图引诱一位同事,来自邻近部门的sishnik 锈的一面。但是我与同事的对话没有解决。因为,引用:


2019年,我参加了C ++ CoreHard会议,听了安东的报告安托什卡Polukhina上必不可少的C ++。据安东说,鲁斯特还很年轻,不是很快,也不是很安全。

Anton Polukhin是俄罗斯在标准化C ++工作组国际会议上的ISO代表,他是接受的C ++语言标准提案的作者。Anton在C ++方面确实很酷而且有权威。但是该报告包含一些有关Rust的严重事实错误。让我们把它们分开。


我们正在谈论从13:0022:35的报告



目录




№1. Rust C++.


(link:godbolt):


RustC++
pub fn square(num: i32) -> i32 {
    return num * num
}

auto square(std::int32_t num) {
    return num * num;
}

example::square:
  mov eax, edi
  imul eax, edi
  ret

square(int): # @square(int)
  mov eax, edi
  imul eax, edi
  ret


(13:35):


. ! . C++ Rust .

, , . , , . , Rust [-2147483648, 2147483647], C++ [-46340, 46340]. ? ?


-46340 46340 — , std::int32_t. - signed overflow. , PVS-Studio. , , CI , :


runtime error: signed integer overflow: 46341 * 46341 cannot be represented in type 'int'
runtime error: signed integer overflow: -46341 * -46341 cannot be represented in type 'int'

Rust .


, (13:58):


, C++ . . C++ . Rust' , . . Rust' , , , . , , Rust . - .

, Rust, , Rust LLVM — , Clang. , Rust «» C++ . , , . C++ . .


, int'a:


unsigned MAX_INT = 2147483647;

int hash_code(std::string x) {
    int h = 13;
    for (unsigned i = 0; i < 3; i++) {
        h += h * 27752 + x[i];
    }
    if (h < 0) h += MAX_INT;
    return h;
}

, , «bye», ( , ) . , , , MAX_INT .

PVS-Studio, . 27752 3 , , , - .


Rust (link:playground):


fn hash_code(x: String) -> i32 {
    let mut h = 13i32;
    for i in 0..3 {
        h += h * 27752 + x.as_bytes()[i] as i32;
    }
    if h < 0 {
        h += i32::max_value();
    }
    return h;
}

fn main() {
    let h = hash_code("bye".to_string());
    println!("hash: {}", h);
}

Debug Release , : wrapping*, saturating*, overflowing* checked*.


, .


— , C++ . . , , «» , .



№2. Rust .


(link:godbolt):


RustC++
pub fn foo(max: i32,
           num: i32) -> i32 {
    return max * num
}

pub fn bar(max: i32,
           num: i32) -> i32 {
    return bar(max, num) *
           bar(max, num)
}

auto foo(std::int32_t max,
         std::int32_t num) {
    return max * num;
}

std::int32_t bar(std::int32_t max,
                 std::int32_t num) {
    return bar(max, num) *
           bar(max, num);
}

example::foo:
  mov eax, edi
  imul eax, esi
  ret
example::bar:
  ret

foo(int, int): # @foo(int, int)
  mov eax, edi
  imul eax, esi
  ret
bar(int, int): # @bar(int, int)
  ret


(15:15):


Rust' C++ , bar . -, - . … , Rust , , UB — , , - . - , - . -- .

. - , NOP bar C++, Rust. LLVM.


LLVM IR , (link:godbolt):


code
#[no_mangle]
pub fn bar(max: i32, num: i32) -> i32 {
    return bar(max, num) * bar(max, num)
}

asm
bar:
    ret

IR
define i32 @bar(i32 %max, i32 %num) unnamed_addr #0 !dbg !5 {
start:
  ret i32 undef, !dbg !8
}


ret i32 undef — , LLVM.


LLVM 2006 . , , LLVM . , . LLVM 6 llvm.sideeffect, 2019 rustc -Z insert-sideeffect, llvm.sideeffect . (link:godbolt). , stable rustc .


C++ , LLVM Rust C.


, , LLVM, : " ". , Rust , , .



№3. Rust .


(16:00):


. Rust. bar foo. , Rust : - , . C++ . Rust . - .

(link:godbolt):


RustC++
fn foo(max: i32,
       num: i32) -> i32 {
    return max * num
}

pub fn bar(max: i32,
           num: i32) -> i32 {
    return foo(max, num) *
           foo(max, num)
}

auto foo(std::int32_t max,
         std::int32_t num) {
    return max * num;
}

std::int32_t bar(std::int32_t max,
                 std::int32_t num) {
    return foo(max, num) *
           foo(max, num);
}

example::bar:
    push    rax
    mov     eax, edi
    imul    eax, esi
    jo      .LBB0_1
    imul    eax, eax
    jo      .LBB0_5
    pop     rcx
    ret
.LBB0_1:
    lea     rdi, [rip + str.0]
    lea     rdx, [rip + .L...]
    jmp     .LBB0_2
.LBB0_5:
    lea     rdi, [rip + str.0]
    lea     rdx, [rip + .L...]
.LBB0_2:
    mov     esi, 33
    call    qword ptr [rip +..]
    ud2

bar(int, int): # @bar(int, int)
    mov     eax, edi
    imul    eax, esi
    jo      .LBB1_3
    imul    eax, eax
    jo      .LBB1_3
    ret
.LBB1_3:
    ud2


Rust , . -ftrapv C++ -C overflow-checks=on Rust, . C++ ud2, "Illegal instruction (core dumped)", Rust core::panicking::panic, . core::panicking::panic :


$ ./signed_overflow 
thread 'main' panicked at 'attempt to multiply with overflow', signed_overflow.rs:6:12
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace

"" , ? x86-64 , 16 , call 8- , . , push rax. Rust, C++(link:godbolt):


RustC++
extern "C" { fn foo(); }

pub fn bar(max: i32,
           num: i32) -> i32 {
    let z = max * num;
    unsafe { foo(); }
    return z
}

extern "C" void foo();

std::int32_t bar(std::int32_t max,
                 std::int32_t num) {
    auto z = max * num;
    foo();
    return z;
}

example::bar:
    push    rbx
    mov     ebx, edi
    imul    ebx, esi
    jo      .LBB0_2
    call    qword ptr [rip + foo..]
    mov     eax, ebx
    pop     rbx
    ret

bar(int, int): # @bar(int, int)
    push    rbx
    mov     ebx, edi
    imul    ebx, esi
    jo      .LBB0_1
    call    foo
    mov     eax, ebx
    pop     rbx
    ret


C++, Rust , push rbx . Q.E.D.


, C++ -ftrapv, . , Rust -C overflow-checks=on, (link:godbolt) C++, . -ftrapv gcc 2008 .



№4. Rust C++.


(18:10):


Rust ...

, Rust', . , "" , 17:30(link:godbolt):


RustC++
pub struct Stats {
    x: u32,
    y: u32,
    z: u32,
}

pub fn sum(a: &Stats,
           b: &Stats) -> Stats {
    return Stats {
        x: a.x + b.x,
        y: a.y + b.y,
        z: a.z + b.z
    };
}

struct Stats {
    std::uint32_t x,
                  y,
                  z;
};

auto sum(const Stats& a,
         const Stats& b) {
    return Stats {
        a.x + b.x,
        a.y + b.y,
        a.z + b.z
    };
}

example::sum:
  mov ecx, dword ptr [rdx]
  mov r8d, dword ptr [rdx + 4]
  add ecx, dword ptr [rsi]
  add r8d, dword ptr [rsi + 4]
  mov edx, dword ptr [rdx + 8]
  add edx, dword ptr [rsi + 8]
  mov rax, rdi
  mov dword ptr [rdi], ecx
  mov dword ptr [rdi + 4], r8d
  mov dword ptr [rdi + 8], edx
  ret

sum(Stats const&, Stats const&):
  mov eax, dword ptr [rsi]
  add eax, dword ptr [rdi]
  mov ecx, dword ptr [rsi + 4]
  add ecx, dword ptr [rdi + 4]
  mov edx, dword ptr [rsi + 8]
  add edx, dword ptr [rdi + 8]
  shl rcx, 32
  or rax, rcx
  ret


, , , → .


2019 CppCon There Are No Zero-cost Abstractions Chandler Carruth. 17:30 - , std::unique_ptr (link:godbolt). - noexcept, rvalue , std::move. Rust . . Rust extern "Rust" unsafe, (link:godbolt):


RustC++
extern "Rust" {
    fn bar(k: &i32);
    fn baz(_: Box<i32>);
}

pub fn foo(mut ptr: Box<i32>) {
    if *ptr > 42 {
        unsafe { bar(&*ptr) };
        *ptr = 42;
    }
    unsafe { baz(ptr) }
}

void bar(int* ptr)
    noexcept;

// Takes ownership.
void baz(std::unique_ptr<int>&& ptr)
    noexcept;

void foo(std::unique_ptr<int>&& ptr) {
    if (*ptr > 42) {
      bar(ptr.get());
      *ptr = 42;
    }
    baz(std::move(ptr));
}

example::foo:
    push    rbx
    mov     rbx, rdi
    cmp     dword ptr [rdi], 43
    jl      .LBB0_2
    mov     rdi, rbx
    call    qword ptr [rip + bar..]
    mov     dword ptr [rbx], 42
.LBB0_2:
    mov     rdi, rbx
    pop     rbx
    jmp     qword ptr [rip + baz..]

foo(std::unique_ptr<...>&&):
    pushq   %rbx
    movq    %rdi, %rbx
    movq    (%rdi), %rdi
    cmpl    $43, (%rdi)
    jl      .LBB0_2
    callq   bar(int*)
    movq    (%rbx), %rax
    movl    $42, (%rax)
.LBB0_2:
    movq    %rbx, %rdi
    popq    %rbx
    jmp     baz(...)


Rust . noexcept, rvalue std::move. . , , .


2019 Rust C++ Benchmarks Game. C++ . . .



№5. C → ++ — noop, C → Rust — PAIN!!!!!!!


(18:30):


, Rust , . , , . ++ , . Rust' - .

.


, Rust , , . Starcraft, .


, Rust cargo, . , , . 2020 crates.io 40 000 .


:


# Cargo.toml
[dependencies]
flate2 = "1.0"

cargo . flate2 , miniz, C, Rust. flate2 .



№6. unsafe Rust.


(19:14):


unsafe Rust', , , .

Rust' .


, unsafe — , Rust , unsafe :


  1. ;
  2. unsafe ;
  3. ;
  4. unsafe ;
  5. union.

Rust . lifetime-, unsafe . , , - . You can’t "turn off the borrow checker" in Rust.


unsafe " , ". , , . , . , malloc NULL , Rust . , , , malloc, : " , ; , , ". unsafe.



№7. Rust .


(19:25):


C++ , , - - , - null . . Rust . - . Rust , , C++.

Microsoft, 70% , Rust . , Rust.


, unsafe Rust, , … , , . , , Rust .


, , , Rust C++ , Rust . Rust . , unsafe .


unsafe :


// Warning: Calling this method with an out-of-bounds index is undefined behavior.
unsafe fn unchecked_get_elem_by_index(elems: &[u8], index: usize) -> u8 {
    *elems.get_unchecked(index)
}

slice::get_uncheckedunsafe , . get_elem_by_index , , . unsafe(link:playground):


// Warning: Calling this method with an out-of-bounds index is undefined behavior.
unsafe fn unchecked_get_elem_by_index(elems: &[u8], index: usize) -> u8 {
    *elems.get_unchecked(index)
}

fn main() {
    let elems = &[42];
    let elem = unsafe { unchecked_get_elem_by_index(elems, 0) };
    dbg!(elem);
}

, , . unsafe.


, unsafe (link:playground):


// Warning: Calling this method with an out-of-bounds index is undefined behavior.
unsafe fn unchecked_get_elem_by_index(elems: &[u8], index: usize) -> u8 {
    *elems.get_unchecked(index)
}

fn get_elem_by_index(elems: &[u8], index: usize) -> Option<u8> {
    if index < elems.len() {
        let elem = unsafe { unchecked_get_elem_by_index(elems, index) };
        Some(elem)
    } else {
        None
    }
}

fn main() {
    let elems = &[42];
    let elem = get_elem_by_index(elems, 0);
    dbg!(&elem);
}

, . , Rust ( slice::get), , unsafe Rust . unchecked_get_elem_by_index , C.


LTO :


C
#include <stdint.h>

uint32_t mul(uint32_t a, uint32_t b) {
    return a * b;
}

Rust
#[link(name="mul", kind="static")]
extern {
    fn mul(a: u32, b: u32) -> u32;
}

#[inline(never)]
#[no_mangle]
fn multiply(a: u32, b: u32) -> u32 {
    unsafe { mul(a, b) }
}

fn main() {
    println!("42*42 = {}!", multiply(42,42));
    println!("87*31 = {}!", multiply(87,31));
}

asm
000000000002eda0 <multiply>:
   2eda0:   89 f8                   mov    %edi,%eax
   2eda2:   0f af c6                imul   %esi,%eax
   2eda5:   c3                      retq


. , C(link:godbolt), , Rust.



№8. Rust .


(20:38):


X. . X, , . . . . , X, .

2018 , Rust, , , . , unsafe safe , , .


, (), unsafe, , .


, Mutex, RwLock, thread::spawn. . , Rust ; , Mutex , , , . ? .




, . " - C++" , C++, .


但是,我希望得到公认的专家的全面报道,至少不包含严重的事实错误。


非常感谢Dmitry KashitsynAlexei Kladov审阅了这篇文章。


All Articles