LINUX.ORG.RU

Как избавится от копирования в RUST

 ,


0

5

Вобщем немного ковыряю по свободе RUST и сталкнулся с идиоцкой проблемой. Не могу избавится от лишнего копирования при создании объектов. Вот пример кода:

struct Foo { val: i32}

impl Foo {
  pub fn new() -> Foo {
    let o = Foo{val: 123};
    print!("Create new Foo: {:p}\n", &o);
    return o;
  }

  fn print(&mut self) {
    print!("In foo print {:p} -> {}\n", self, self.val);
  }
}

impl Drop for Foo {
  fn drop(&mut self) {
    print!("Destroy foo {:p}\n", self);
  }
}

fn main() {
  let mut f = Foo::new();
  f.print();
}

Выхлоп:

Create new Foo: 0x7ffff965fa10
In foo print 0x7ffff965fa58 -> 123
Destroy foo 0x7ffff965fa58

Как видно в конструкторе «new» у нас создается локальный объект на стеке, потом он копируется в во второй стековый объект f (внутри main). Вопрос как создавать объекты без лишнего копирования ?

★★★★

Попробуй компилировать с оптимизацией.

tailgunner ★★★★★ ()

1) Используйте println вместо print.

2) Вместо метода print можно использовать #[derive(Debug)]

3) По поводу самой проблемы. Макрос print, скорее всего, разворачивается в сильно большой код, что препятствует инлайнигу. Конструктор должен быть примитивным.

PS: Rust, не RUST.

RazrFalcon ★★★★★ ()

На самом деле довольно забавно. Какой-то баг оптимизатора.

struct Foo { val: i32}

impl Foo {
    pub fn new() -> (String, Foo) {
        let o = Foo {val: 123};
        (format!("{:p}", &o), o)
    }

    fn print(&self) {
        println!("In foo print {:p} -> {}", self, self.val);
    }
}

fn main() {
    let (p, f) = Foo::new();
    f.print();
    println!("{}\n{:p}", p, &f);
}
In foo print 0x7ffe8ca2fdc8 -> 123
0x7ffe8ca2f2e8
0x7ffe8ca2fdc8
Но если убрать f.print();, то всё ок:
0x7ffc3216a810
0x7ffc3216a810

Но если этот код, без принта, собрать с -O3 + lto, то опять бяка:

0x7ffdfea20d90
0x7ffdfea20d38

RazrFalcon ★★★★★ ()
Ответ на: комментарий от RazrFalcon

Выпелял принт - не помогло

struct Foo {
  val: i32,
  p: *const i32
}

impl Foo
{
   #-inline-
   pub fn new() -> Foo {
     let mut o = Foo {val: 123, p: std::ptr::null()};
     o.p = &o.val;
//              print!("Create new Foo: {:p}\n", &o);
     return o;
  }

  fn print(&mut self) {
    print!("In foo print {:p} : {:p} -> {}\n", &self.val, self.p, self.val);
  }
}

Output:

In foo print 0x7ffcdd36ebf0 : 0x7ffcdd36ec90 -> 123
Destroy foo 0x7ffcdd36ebf0

zaz ★★★★ ()
Последнее исправление: zaz (всего исправлений: 4)
Ответ на: комментарий от RazrFalcon

Но если убрать f.print();, то всё ок:

Я думаю фишка в том что при убирания f.print(); - оптимизатор выпиливает локальную переменную внутри main() и деструктор дергается сразу на вызоде из new()

zaz ★★★★ ()
Ответ на: комментарий от RazrFalcon

Спасибо. Зарегался на users.rust-lang.org - создал топик. Если чтото прояснится отпишусь здесь.

zaz ★★★★ ()

Ты их там запутал, приведи тот код, который в ОП. ИМХО, тут дело как раз в let внутри Foo::new(). Когда конструктор структуры возвращается, тогда нет копирования.

Как видно в конструкторе «new» у нас создается локальный объект на стеке

Ты его явно создаешь, и почему-то хочешь чтобы было не так.

consensus ()
Ответ на: комментарий от consensus

Когда конструктор структуры возвращается, тогда нет копирования.

Да я какбы догадывался и просмотрел все на уровне «llvm-ir» там есть преалокация перед вызовом new но компилятор неможет/нехочет использовать ее для внутренней переменной «o». На самом деле сдесь есть другой момент (с которого у меня и возникли вопросы, а то что топике это уже следствие), при размещении структуры в HEAP я тоже не могу избавится как минимум от одного memcpy! Например при компиляции:

let mut l = Box::new( Foo{val: 123, p: std::ptr::null(), arr: [0; 1024]} );
l.print();
Мы получаем размещении структуры временной структуры НА СТЕКЕ:
%1 = alloca %Foo
с последующей инициализацией полей в этой временной структуре, затем алоцируется память в куче и делается memcpy:
%12 = invoke i8* @_ZN5alloc4heap15exchange_malloc17hbaf03797f5275057E(i64 8216, i64 8)
          to label %"_ZN5alloc5boxed30_$LT$impl$u20$Box$LT$T$GT$$GT$3new17h525d8eea21062082E.exit" unwind label %unwind_custom_.i
...
call void @llvm.memcpy.p0i8.p0i8.i64(i8* %17, i8* %16, i64 8216, i32 8, i1 false)

Ты его явно создаешь, и почему-то хочешь чтобы было не так.

А как мне инстанциировать сложные структуры требующие не тривиальной инициализации ? Например такие:

struct Foo {arr: [i32; 128]}
impl Foo {
  fn new(base: i32) -> Foo {
    let o = Foo{arr: [0, 128]};
    for x in 0..10 {
      o.arr[i] = base + x;
    }
    return o;
  }
}

zaz ★★★★ ()

Как избавится от копирования в RUST

Для начала бы русский язык поучить.

anonymous ()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.