2016-07-25 8 views
6

Rozważmy fragmentCzy Rust może zoptymalizować kopie bitowe podczas ruchu obiektu w przyszłości?

struct Foo { 
    dummy: [u8; 65536], 
} 

fn bar(foo: Foo) { 
    println!("{:p}", &foo) 
} 

fn main() { 
    let o = Foo { dummy: [42u8; 65536] }; 
    println!("{:p}", &o); 
    bar(o); 
} 

Typowym result programu jest

0x7fffc1239890 
0x7fffc1229890 

gdzie adresy są różne.

Wygląda na to, że duża tablica dummy została skopiowana zgodnie z oczekiwaniami w implementacji przenoszenia kompilatora. Niestety, może to mieć nietrywialny wpływ na wydajność, ponieważ dummy jest bardzo dużą tablicą. Ten wpływ może zmusić ludzi do wyboru argumentu przekazującego przez odniesienie, nawet jeśli funkcja faktycznie "konsumuje" argument koncepcyjnie.

Od Foo nie pochodzi Copy, obiekt o jest przesuwany. Ponieważ Rust zabrania dostępu do przeniesionego obiektu, co uniemożliwia bar "ponowne użycie" oryginalnego obiektu o, zmuszając kompilator do wygenerowania potencjalnie drogiej kopii bitowej? Czy istnieje zasadnicza trudność, czy też zobaczymy, że kompilator kiedyś zoptymalizuje tę bitową kopię?

+7

Rustc optymalizuje ruchy. Nie robi tego w tym przypadku, prawdopodobnie dlatego, że llvm nie wstawił paska. Może to być spowodowane tym, że próbujesz obserwować wartości wskaźnika, a llvm nie jest pewien, czy można go bezpiecznie zoptymalizować. Próbowałem go bez użycia printów ': p' i użyłem test :: black_box, a kopia znika ze złożenia. – Manishearth

+0

@ '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' ' LLVM jest po prostu złe w usuwaniu ruchów dużych tablic. – Veedrac

Odpowiedz

13

Biorąc pod uwagę, że w Rust (w przeciwieństwie do C lub C++) na adres o wartości nie jest uważana za ważne, nie ma nic w kategoriach języku który zapobiega elizja kopii.

Jednak dzisiaj rustc niczego nie optymalizuje: wszystkie optymalizacje są delegowane do LLVM i wydaje się, że trafiłeś w ograniczenie optymalizatora LLVM tutaj (nie jest jasne, czy to ograniczenie jest spowodowane tym, że LLVM jest blisko semantyki C lub jest tylko pominięcie).

Tak, istnieją dwie drogi poprawy generowania kodu dla tego:

  • nauczanie LLVM do wykonywania tej optymalizacji (jeśli to możliwe)
  • nauczania rustc aby wykonać tę optymalizację (przechodzi optymalizacji idą rustc teraz że ma MIR)

, ale na razie możesz po prostu chcieć uniknąć przydzielania tak dużych obiektów na stosie, na przykład możesz to zrobić za pomocą Box.

+5

Mówiąc o przepustkach optymalizacyjnych MIR, pierwszą z nich byłaby prosta propagacja miejsca docelowego ruchu: https://github.com/rust-lang/rust/pull/34693. Problem ze śledzeniem to https://github.com/rust-lang/rust/issues/32966. – eddyb

+1

Zamiast po prostu unikać alokacji stosów, lepiej byłoby założyć, że ruch zostanie zoptymalizowany, a jedynie skrzynkowe rzeczy później, jeśli tak nie było. Przez większość czasu w Rust nie powinieneś myśleć o próbie uniknięcia kopiowania. –

+0

@MichaelYounkin: Częściowo się zgadzam. Problem polega na tym, że duże obiekty kopiowane kilka razy na stosie łatwo prowadzą do przepełnienia stosu, szczególnie w przypadku miejsc debugowania, w których nie występują optymalizacje. Jeśli bufor jest bardzo duży, koszt alokacji dynamicznej powinien być niższy kosztem zainicjowania samego bufora. –

Powiązane problemy