2016-04-28 11 views
5

Attualmente sto esaminando doing more stuff with arrays, ma penso che le prestazioni di quelle operazioni potrebbero essere ancora migliori se ci fosse consentito di trasmettere in qualche modo in uno Leaked<T> l'array in primo piano, solo per rimuoverlo quando termina la funzione. Questo ci consentirebbe di utilizzare l'amplificazione delle perdite senza a) l'introduzione della non sicurezza eb) l'impostazione di catch_panic(_). Questo è in qualche modo possibile in Rust?C'è un modo per pre-e-perdere un valore?

Ad esempio, la creazione di un generico array da un iteratore (questo ovviamente non funziona):

#[inline] 
fn map_inner<I, S, F, T, N>(list: I, f: F) -> GenericArray<T, N> 
where I: IntoIterator<Item=S>, F: Fn(&S) -> T, N: ArrayLength<T> { 
    unsafe { 
     // pre-leak the whole array, it's uninitialized anyway 
     let mut res : GenericArray<Leaked<T>, N> = std::mem::uninitialized(); 
     let i = list.into_iter(); 
     for r in res.iter_mut() { 
      // this could panic anytime 
      std::ptr::write(r, Leaked::new(f(i.next().unwrap()))) 
     } 
     // transmuting un-leaks the array 
     std::mem::transmute::<GenericArray<Leaked<T>, N>, 
           GenericArray<T, N>>(res) 
    } 
} 

devo notare che se ci sia avuto tempo di compilazione accesso alle dimensioni di T o un tipo che può nascondere le sue viscere da borrowck (come Leaked<T> nell'esempio), questo è perfettamente fattibile.

+0

Quali sono le prestazioni che stai migliorando? Non incrementare 'len'? – malbarbo

+0

Se cerco di evitare le perdite rilevando il panico (che funziona solo su beta/nightly comunque per ora), ottengo circa il 45% di throughput in più rispetto alla raccolta in un 'Vec'. Presumo di poter ottenere risultati ancora migliori con la pre-perdita. – llogiq

risposta

3

È possibile utilizzare nodrop, ma potrebbe perdere.

fn map_inner<I, S, F, T, N>(list: I, f: F) -> GenericArray<T, N> 
where I: IntoIterator<Item=S>, F: Fn(&S) -> T, N: ArrayLength<T> { 
    unsafe { 
     // pre-leak the whole array, it's uninitialized anyway 
     let mut res : NoDrop<GenericArray<T, N>> = NoDrop::new(std::mem::uninitialized()); 
     let i = list.into_iter(); 
     for r in res.iter_mut() { 
      // this could panic anytime 
      std::ptr::write(r, f(i.next().unwrap())) 
     } 
     res.into_inner() 
    } 
} 

Supponiamo che, dopo la prima voce (a) viene consumato da i e scritti r, un panico accade. Gli articoli rimanenti da i sarebbero in calo, ma l'articolo a non lo farebbe. Anche se la perdita di memoria is not non è sicura, non è consigliabile.

Penso che l'approccio descritto nel collegamento alla domanda sia la strada da percorrere. È simile alle implementazioni Vec e ArrayVec. Sto usando un approccio simile nella libreria di array che sto scrivendo.

+0

Grazie! Questo è esattamente quello che stavo cercando. – llogiq

+1

@llogiq Se ti interessa, ho scritto del codice per rispondere ad altre domande che fanno sì che questo tipo di costrutto non perda. Vedi http://stackoverflow.com/questions/36925673/how-can-i-initialize-an-array-using-a-function – malbarbo

+0

Grazie ancora - è quasi uguale a quello che ho trovato. I miei benchmark sono molto promettenti, molto vicini alla semplice costruzione di array. – llogiq