Я пишу библиотеку линейных алгебр в Rust.
У меня есть функция, чтобы получить ссылку на ячейку матрицы в данной строке и столбце. Эта функция начинается с пары утверждений, что строка и столбец находятся в пределах границ:
#[inline(always)]
pub fn get(&self, row: usize, col: usize) -> &T {
assert!(col < self.num_cols.as_nat());
assert!(row < self.num_rows.as_nat());
unsafe {
self.get_unchecked(row, col)
}
}
В тесных циклах я думал, что быстрее пропустить проверку границ, поэтому я предоставляю метод get_unchecked
:
#[inline(always)]
pub unsafe fn get_unchecked(&self, row: usize, col: usize) -> &T {
self.data.get_unchecked(self.row_col_index(row, col))
}
Причудливая вещь заключается в том, что, когда я использую эти методы для реализации умножения матриц (через итераторы строк и столбцов), мои тесты показывают, что на самом деле это происходит на 33% быстрее, когда я проверяю границы. Почему это происходит?
Я пробовал это на двух разных компьютерах: Linux и другой OSX, и оба показывают эффект.
Полный код в github. Соответствующий файл lib.rs. Интересующие функции:
-
get
в строке 68 -
get_unchecked
в строке 81 -
next
в строке 551 -
mul
в строке 796 -
matrix_mul
(эталон) в строке 1038
Обратите внимание, что я использую номера уровня типа для параметризации моих матриц (с возможностью динамических размеров тоже с помощью фиктивных помеченных типов), поэтому эталон умножает две матрицы 100x100.
UPDATE:
Я значительно упростил код, удалив материал, который не используется непосредственно в эталонном тесте, и удаляет общие параметры. Я также написал реализацию умножения без использования итераторов, и эта версия не вызывает тот же эффект. См. здесь для этой версии кода. Клонирование ветки minimal-performance
и работающей cargo bench
будет оценивать две разные реализации умножения (обратите внимание, что утверждения закомментированы для начала в этой ветке).
Также следует отметить, что если я изменю функции get*
, чтобы возвращать копии данных вместо ссылок (f64
вместо &f64
), эффект исчезает (но код немного медленнее по кругу).