2015-07-07 21 views
7

Czy mam rację, zakładając, że jedyną rzeczą, która "spowalnia", jest to, że zwalnia obiekt po jego upadku? Poza tym, "ile" jest kosztem dereferencji Rc, tj. Czy powinienem się tym przejmować?
Czy te dwie funkcje są prawie równie szybkie? Czy jest zauważalna różnica w prędkości?Czy muszę się martwić o obciążenie "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; 
} 

Ponieważ obiekt odwołuje się test_with_rc() zawsze tylko jedno odniesienie i zachowuje się jak Box w tej funkcji (patrząc od zewnątrz, a nie od wewnątrz, oczywiście).

Podejrzewam, że Rc s są rzeczywiście szybsze niż myślę.

PS: Mówiąc o "szybkim" mam na myśli zarówno dereferencję, jak i alokację/zwolnienie.

+2

Jak zawsze w przypadku pytań dotyczących wydajności, jedynym sposobem na poznanie jest profilowanie go na miejscu. Mikro-testy porównawcze nie są odpowiednie. –

Odpowiedz

7

Rc<T> jest bardzo, bardzo tanie. To nie jest tak tanie jak T przez całkiem sporo (wartości boksu są stosunkowo kosztowne w kategoriach mikrooptymalizacji), ale niewiele mniej wydajne niż Box<T>.

To tak jak Box, ale z dodatkową parę słów o mocnych i słabych liczy odniesienia, a jedyne rzeczy, które muszą dotknąć że tworzysz Rc (inicjuje wartości), klonowanie Rc (zwiększa RefCount) i upuszczenie Rc (zmniejsza wartość refektora i uruchamia destruktor, jeśli jest to właściwe). W kodzie niestabilnym jest też downgrading do/upgrading z Weak (podobnie trywialne rzeczy).

Dereferencja to prosta operacja na pamięci, podobnie jak w przypadku Box.

7

Aby odpowiedzieć na to pytanie, można włączyć do test skrzyni, która zawiera rozwiązania benchmarkingu (ten potrzebuje nightly):

#![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) 
} 

Teraz kompilacją & działa następująco:

$ 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 

Co stało się? RC najwyraźniej został całkowicie skompilowany. Tak jak być powinno, ponieważ nie mamy sklonowanego tego. Tak zmieniając odpowiednie fn do:

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

Otrzymujemy następujące:

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 

tak, wystarczy powiedzieć, prawdopodobnie będziesz musiał martwić się o inne rzeczy przed patrząc na RC-indukowanej napowietrznych.

+0

* RC najwyraźniej został całkowicie skompilowany * Nie sądzę, że możesz twierdzić, że jest zoptymalizowany. Jeśli spojrzysz na [LLVM IR] (http://is.gd/AB68e3), zobaczysz, że 'test_with_box' wywołuje' @je_mallocx (i64 8, i32 0) 'i' test_with_rc' wywołuje '@je_mallocx (i64 24, i32 0) ', więc dwa bloki nie będą tym samym zespołem. Są jeszcze 4 dalsze instrukcje dla sprawy 'Rc'. – Shepmaster

Powiązane problemy