2016-06-28 3 views
2
struct Vec { 
    data: [f32; 3], 
} 

impl Vec { 
    fn dot(&self, other: &Vec) -> f32 { 
     .. 
    } 
    // vs 
    fn dot(self, other: Vec) -> f32 { 
     .. 
    } 
} 

В настоящее время я пишу векторную математическую библиотеку, и мне интересно, следует ли мне брать или копировать свои типы векторов.Должен ли я брать или копировать свои небольшие типы данных?

В настоящий момент я реализую Copy для Vec, что делает API немного приятнее, потому что вам не нужно писать & все время.

Но для этого требуются гораздо более сложные ограничения, потому что теперь все мои ограничения также должны удовлетворять Copy.

Какая из них потенциально дает лучшую производительность и почему?

Какой из них потенциально дает лучшую эргономичность и почему?

Edit:

Я создал небольшой microbenchmark

test bref_f32 ... bench: 2,736,055 ns/iter (+/- 364,885) 
test bref_f64 ... bench: 4,872,076 ns/iter (+/- 436,928) 
test copy_f32 ... bench: 2,708,568 ns/iter (+/- 31,162) 
test copy_f64 ... bench: 4,890,014 ns/iter (+/- 553,050) 

Кажется, что нет никакой разницы между ref и copy для этого примера с точки зрения производительности.

Copy, похоже, обеспечивает лучшую эргономику для пользователей библиотеки.

+2

Вы действительно уверены, что хотите спросить о «идиоматическом», а не «производительности» или «упрощении границ»? Потому что идиомы очень * субъективны *, и я боюсь, что обе альтернативы могут быть рекомендованы, и нет однозначного объективного ответа ... в этом случае ваш вопрос будет заправлен для закрытия (в первую очередь основанный на мнении). –

+0

@ MatthieuM. Я предположил, что есть некоторые направляющие строки, для которых должен выглядеть похожий код Rust, я перефразировал вопрос и добавил пример. –

ответ

1

Ржавчина не является чисто академическим языком с высокими эстетическими целями и особой целью. Rust - это язык системного программирования, который подразумевает прагматизм.

Общее правило заключается в том, что ваш интерфейс должен правильно документировать собственности:

  • прохода по значению, когда вы отказаться от собственности
  • прохода по ссылке (возможно, измененной), когда вы временно отказаться от владения

однако свойство Copy является прекрасным примером прагматизма в игре. Он признает, что передача по ссылке может быть громоздкой (кто-нибудь хочет ввести (&1 + &x) * &y?) И, следовательно, создает побег-люк для типов, которые не обязательно должны быть аффинными (т. Е. Никаких специальных действий при уничтожении).

В результате, семантически, если ваш тип может быть гарантировано быть и оставаться Copy, затем пометив его как таковой дает пользователям некоторую свободу действий на ее использование, которое может улучшить эргономику. Я бы посоветовал вам пометить его, однако, хотя напоминание о том, что позднее удаление признака Copy будет отстающим несовместимым изменением.

Как только тип Copy, я без колебаний воспользуюсь этим фактом и передам его по значению. В конце концов, если тип никогда не передается по значению, тогда не было смысла делать его Copy.


Единственное предостережение было бы связано с производительностью.

Copyне вынуждает копию, она просто позволяет это. Это означает, что оптимизатор имеет всю широту для использования копии ... или нет.

Для небольших типов производительность вряд ли будет отличаться от того, что происходит; для более крупных типов я бы рекомендовал проводить сравнительный анализ различных интерфейсов, если производительность имеет значение. Vec всего в 1,5 раза больше размера указателя/ссылки на 64-битной архитектуре, поэтому он действительно находится в серой области. Иногда копирование будет медленнее (большая копия), но иногда преимущества наличия локальной копии позволят оптимизировать, которые не будут запускаться с помощью указателя.

Однако такой бенчмаркинг чреват опасностью, особенно потому, что он сильно зависит от того, является ли функция встроенной или нет (при более или менее удалении для удаления копий).

4

Я бы порекомендовал заимствования в этом сценарии, потому что это не похоже на то, что собственность является проблемой. Таким образом, я предполагаю, что ваш код будет выглядеть

struct Vec { 
    data: [f32; 3], 
} 

impl Vec { 
    fn dot(&self, other: &Vec) -> f32 {..} 
} 
+1

Не могли бы вы объяснить, почему вы порекомендовали бы это? –

+0

Я бы рекомендовал его, потому что он использует больше памяти для создания всего нового вектора, что и происходит при копировании вектора. Если вам не нужно создавать новый вектор, не делайте этого. –

+1

* Это то, что происходит, когда вы копируете вектор *, который может быть причудливым оптимизирующим компилятором, если он решит, что он может быть упрощен. – Shepmaster

Смежные вопросы