2015-07-07 8 views
7

Ho ragione di presumere che l'unica cosa che "rallenta" Rc s è che controlla se rilasciare l'oggetto quando cade? Oltre a ciò, "quanto" è il sovraccarico del dereferenziamento a Rc, cioè dovrei preoccuparmene?
Queste due funzioni sono quasi altrettanto veloci? O c'è una notevole differenza di velocità?Devo essere preoccupato per il sovraccarico di `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; 
} 

Poiché l'oggetto di riferimento in test_with_rc() sempre ha un solo riferimento e si comporta come un Box a quella funzione (vista dall'esterno, non internamente, ovviamente).

Ho il sospetto che gli Rc s siano effettivamente più veloci di quanto penso.

PS: Quando si parla di "veloce" intendo sia il dereferenziamento che l'allocazione/deallocazione.

+2

Come sempre con le domande sulle prestazioni, l'unico modo per sapere è profilare in-situ. I micro-benchmark non sono adatti. –

risposta

7

Rc<T> è molto, molto economico. Non è economico come lo T da un po '(i valori di boxing sono relativamente costosi in termini di micro-ottimizzazione), ma non è meno efficiente di Box<T>.

E 'proprio come Box, ma con un ulteriore paio di parole per i forti e deboli i conteggi di riferimento, e le uniche cose che hanno bisogno di toccare, che stanno creando un Rc (inizializza i valori), la clonazione di un Rc (incrementa il refcount) e rilasciare un Rc (diminuisce il Refcount ed esegue il distruttore se appropriato). Nel codice instabile è anche in corso il downgrade a/upgrade da Weak (roba altrettanto banale).

Il dereferenziamento è una semplice operazione di memoria proprio come con lo Box.

7

Per rispondere alla tua domanda, è possibile rivolgersi al test cassa, che contiene una soluzione di benchmarking (questo ha bisogno di una notte):

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

Ora compilazione & l'esecuzione di questo:

$ 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 

Cosa è accaduto? Apparentemente la RC è stata completamente compilata. Come dovrebbe essere, perché non abbiamo clonato it. Così cambia la rispettiva fn a:

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

Otteniamo la seguente:

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 

Quindi, basti dire, avrete probabilmente bisogno di preoccuparsi di altre cose prima di guardare in alto RC-indotta.

+0

* La RC apparentemente è stata completamente compilata * Non penso che si possa affermare che è ottimizzata. Se guardi [LLVM IR] (http://is.gd/AB68e3), puoi vedere che 'test_with_box' chiama' @je_mallocx (i64 8, i32 0) 'e' test_with_rc' chiama '@je_mallocx (i64 24, i32 0) ', quindi i due blocchi non sarebbero lo stesso assembly. Ci sono anche altre 4 istruzioni per il caso 'Rc'. – Shepmaster

Problemi correlati