2016-04-28 11 views
5

Obecnie patrzę na doing more stuff with arrays, ale myślę, że wydajność tych operacji mogłaby być jeszcze lepsza, gdybyśmy mogli w jakiś sposób przetransformować do przodu tablicę z przodu, tylko po to, by ją przeciekać, gdy funkcja się zakończy. To pozwoliłoby nam na użycie wzmacniacza wycieku bez a) wprowadzania bezpieczeństwa i b) konfigurowania catch_panic(_). Czy jest to w jakiś sposób możliwe w Rust?Czy istnieje sposób na pre- i nieszczelność wartości?

Na przykład, tworząc ogólną tablicę z iteratora (to oczywiście nie działa):

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

Należy zauważyć, że jeśli my albo miał czasu kompilacji dostępu do wielkości T lub typu, może ukryć swoje wnętrzności przed wypożyczeniem (jak w przykładzie Leaked<T>), jest to całkowicie wykonalne.

+0

Jaka jest poprawa wydajności, której się spodziewacie? Nie zwiększasz 'len'? – malbarbo

+0

Jeśli próbuję zapobiec wyciekom przez łapanie paniki (która działa tylko na razie w wersji beta/nocnej), otrzymuję o 45% więcej przepustowości niż zbieranie w 'Vec'. Zakładam, że mogę uzyskać jeszcze lepsze wyniki przed wyciekiem. – llogiq

Odpowiedz

3

Jest to możliwe przy użyciu nodrop, ale może przeciekać.

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() 
    } 
} 

Załóżmy, że po pierwszym punkcie (a) jest zużywana od i i zapisywane r, panika dzieje. Pozostałe elementy z i zostaną odrzucone, ale pozycja a nie będzie. Chociaż przecieka pamięć is not uważana za niebezpieczną, nie jest pożądana.

Myślę, że podejście opisane w linku do pytania jest drogą do zrobienia. Jest podobny do implementacji Vec i ArrayVec. Używam podobnego podejścia w bibliotece tablic, którą piszę.

+0

Dziękujemy! Właśnie tego szukałem. – llogiq

+1

@llogiq Jeśli cię zainteresuje, napisałem kod, aby odpowiedzieć na inne pytanie, które sprawia, że ​​ten rodzaj konstrukcji nie wycieka. Zobacz http://stackoverflow.com/questions/36925673/how-can-i-initialize-an-array-using-a-function – malbarbo

+0

Jeszcze raz dziękuję - to w większości równa temu, co wymyśliłem. Moje benchmarki są również bardzo obiecujące - bardzo bliskie prostolinijnej konstrukcji. – llogiq

Powiązane problemy