2015-07-07 6 views
7

Имею ли я право предположить, что единственное, что «замедляет» Rc s, заключается в том, что он проверяет, следует ли освобождать объект при его удалении? Кроме того, «сколько» является накладными расходами на разыменование Rc, то есть я должен беспокоиться об этом?
Являются ли эти две функции почти одинаково быстрыми? Или есть заметная разница в скорости?Должен ли я беспокоиться о накладных расходах на «Rc»?

fn test_with_box() { 
    let b = Box::new(1.0); 
    let x = b * 2; 
} 

fn test_with_rc() { 
    let rc = Rc::new(1.0); 
    let x = rc * 2; 
} 

Поскольку объект ссылки в test_with_rc() всегда имеет только одну ссылку и ведет себя как Box в этой функции (если смотреть со стороны, а не внутренне, конечно).

Я подозреваю, что Rc s на самом деле быстрее, чем я думаю.

PS: Когда речь идет о «быстрой», я имею в виду как разыменование, так и выделение/освобождение.

+2

Как всегда с вопросами о производительности, единственный способ узнать - это профиль его на месте. Микро-тесты не подходят. –

ответ

7

Rc<T> очень, очень дешево. Это не так дешево, как T довольно немного (значения бокса относительно дороги в условиях микро-оптимизации), но почти менее эффективны, чем Box<T>.

Это так же, как Box, но с дополнительной парой слов для сильных и слабых опорных отсчетов, и единственное, что нужно трогать, которые создает Rc (инициализирует значения), Клонирование Rc (увеличивает RefCount) и сброс Rc (уменьшает коэффициент пересчета и запускает деструктор, если это необходимо). В нестабильном коде также понижается до/upgrade от Weak (аналогично тривиальный материал).

Dereferencing - простая операция с памятью, как и с Box.

7

Чтобы ответить на ваш вопрос, вы можете обратиться к test клети, которая содержит решение бенчмаркинг (это необходимо ночное):

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

use std::rc::Rc; 

fn test_with_box() { 
    let b = Box::new(1.0); 
    test::black_box(*b * 2.0); 
} 

fn test_with_rc() { 
    let rc = Rc::new(1.0); 
    test::black_box(*rc * 2.0); 
} 

#[bench] 
fn bench_box(b: &mut test::Bencher) { 
    b.iter(test_with_box) 
} 

#[bench] 
fn bench_rc(b: &mut test::Bencher) { 
    b.iter(test_with_rc) 
} 

Теперь сводной & работает это:

$ rustc --test -O rc.rs && ./rc --bench 

running 2 tests 
test bench_box ... bench:   22 ns/iter (+/- 0) 
test bench_rc ... bench:   22 ns/iter (+/- 0) 

test result: ok. 0 passed; 0 failed; 0 ignored; 2 measured 

Что получилось? Очевидно, что RC был полностью составлен. Как и должно быть, потому что у нас нет , клонированных. Таким образом, изменение соответствующего fn к:

fn test_with_rc() { 
    let rc = Rc::new(1.0); 
    test::black_box(*rc.clone() * 2.0); 
} 

Мы получаем следующее:

running 2 tests 
test bench_box ... bench:   23 ns/iter (+/- 1) 
test bench_rc ... bench:   25 ns/iter (+/- 1) 

test result: ok. 0 passed; 0 failed; 0 ignored; 2 measured 

Так, достаточно сказать, что вам, возможно, придется беспокоиться о других вещах, прежде чем смотреть на RC-индуцированное над головой.

+0

* RC, по-видимому, полностью скомпилирован * Я не думаю, что вы можете утверждать, что он оптимизирован. Если вы посмотрите на [LLVM IR] (http://is.gd/AB68e3), вы увидите, что 'test_with_box' вызывает вызовы' @je_mallocx (i64 8, i32 0) 'и' test_with_rc' '@je_mallocx (i64 24, i32 0) ', поэтому два блока не будут одной и той же сборкой. Также есть еще 4 инструкции для случая «Rc». – Shepmaster

Смежные вопросы