Обновить
Комментарии 23
1. Я надеюсь, для бенчмарков проект компилируется с оптимизациями: cargo build --release?
Ещё возможно вам будет интересно: у Rust есть встроенный движок бенчмарков: doc.rust-lang.org/book/benchmark-tests.html

2.
pub fn new(file_path: &str) -> Model
Плохой, плохой код!
Во-первых, вместо &str правильнее передавать AsRef<Path>: см. std::fs::File::open. Некоторые пути непредставимы в валидном utf-8, коими являются все строки в Rust.

Во-вторых, не прививайте себе привычку везде тыкать .unwrap(). Почитайте прекрасную статью об обработке ошибок в Rust: blog.burntsushi.net/rust-error-handling. Да, большая, но она раскладывает всё по полочкам.

3.
или использовать стандартный типаж std::num::Zero
Ещё есть стабильный std::default::Default. Но Zero, конечно, по смыслу подходит больше.
Я думаю, что для кода приложения, а не библиотеки, использовать &str для имени файла — вполне нормально. Если понадобится что-то другое, всегда можно будет исправить; а использование AsRef никак не поможет, если опять же в самом начале передавать строку.

То же самое, в общем-то, справедливо и для обработки ошибок, с учётом «игрушечности» рендерера и целей, с которыми он делается.
То же самое, в общем-то, справедливо и для обработки ошибок
Если знаете язык — да. Но пока автор его только изучает, как по мне лучше сразу приучаться правильно. «Игрушечность» рендера наоборот только тому способствует: кода мало, соответственно, и переделывать тоже немного. Представьте, как в большом проекте искать и исправлять .unwrap(), написанные по старой привычке, исправлять возвращаемые значения функций и т.д.

На самом деле, жаль, что Rust так легко позволяет обойти свою модель обработки ошибок. Это ведь даже не unsafe код.
Ну на счёт «приучаться правильно» я с вами, в принципе, согласен, но не согласен с тем, что это «обход» модели ошибок. Паники — такая же часть модели ошибок, как и Result.
Да. Для бенчмарков компилировалось release в Rust и -O3 в gcc.
Некоторые пути непредставимы в валидном utf-8, коими являются все строки в Rust.

А можно подробнее об этом? Ни разу не слышал о не текстовых путях. Какая-то специфическая область?
Разве что только поиск информации, как сделать ту или иную штуку в расте осложняется тем, что язык быстро меняется. Подчас находишь какие-то ответы, пробуешь их, а они не работают, потому-что оказывается буквально несколько недель назад в 1.1 этот метод переименовали и т. п. Столкнулся с этим на примере метода from_str(), который удалили из Rust 1.1.
Очень мило.
Как написал автор, в статье могут быть грубые ошибки и упущения.
Если сидеть на стабильном канале, то вам ничего не уберут и не переименуют в следующих версиях. Здесь, видимо, автор нашёл примеры, написанные на Rust версии < 1.0, то есть ещё до стабильного релиза. Действительно сейчас на Stack Overflow и других сайтах достаточно много примеров, написанных на устаревших версиях языка.
from_str() удалили не из Rust 1.1, его нет уже давным-давно. Стандартным способом сконвертировать строку во что-то как минимум полгода является метод parse().
std::vector<int> face = model->face(i);


Копирование массива вершин треугольника на каждой итерации. Вот откуда тормоза в С++ версии наверное.
Если это C++11, то тут вроде не должно быть копирования, а только перемещение.

А вообще пытаться угадать, где программа проводит больше всего времени, весьма сомнительное занятие. Нужно использовать профайлер или дебаггер.
Да, таки копируется. По уму, нужно что-то вроде
std::vector<int> const& Model::face(int idx) const {
    return faces_[idx];
}
...
auto const& face = model->face(i);
Внес исправления в C++ согласно вашим предложениям. Небольшое ускорение есть, но существенного влияния это не оказывает:
cepreu@cepreu-P5K:~/Загрузки/tinyrenderer-f6fecb7ad493264ecd15e230411bfb1cca539a12$ time ./a.out 

real	0m1.463s
user	0m1.453s
sys	0m0.008s
А если Model на стеке создавать, а не выделять память в цикле?
Да, ещё было бы интересно бенчмаркать отдельно загрузку модели и рендеринг модели.
Такие вещи с -O3 оптимизируются )
Современный gcc даже без C++11 режима не делает никакого копирования и мува при такой передаче. То есть конструктор копирования для вектора не вызывается вообще он создается ровно 1 раз и заполняется и потом используется.
Причём не только GCC. RVO давно уже поддерживается большей частью современных компиляторов.
А в стандарте это даже введено под названием copy/move-elimination.
Смысл в том что заполнять вектор на стеке не нужно. В данном случае интерфейс должен проектироваться таким образом, чтобы предоставлять прямой доступ к внутреннему массиву. Из такого интерфейса же явно следует что создаётся копия внутреннего массива и копирование таки будет.

можно дописать
face.push_back(-1);

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