Я пишу библиотеку линейной алгебры в 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, и оба показывают эффект.
Полный код on github. Соответствующий файл - lib.rs. Функции интерес представляют:
get
по линии 68get_unchecked
по линии 81next
в строке 551mul
в строке 796matrix_mul
(эталонный) на линии 1038
Обратите внимание, что я использую номера уровня типа для параметризации моего матрицы (с возможностью динамических размеров тоже с помощью фиктивных меток), поэтому эталон умножает две матрицы 100x100.
UPDATE:
Я значительно упрощен код, удаляя материал непосредственно не используется в тесте и удаления общих параметров. Я также написал реализацию умножения без использования итераторов, и эта версия не вызывает такого же эффекта. См. here для этой версии кода. Клонирование ветви minimal-performance
и работает cargo bench
проведет сравнение двух различных вариантов умножения (обратите внимание, что утверждения закомментированы для начала в этой ветке).
Следует также отметить, что если я изменяю get*
функции возвращать копии данных вместо ссылки (f64
вместо &f64
), эффект исчезает (но код немного медленнее, круглый).
Старый вопрос: вы скомпилировали с оптимизацией компилятора (с флагом '--release')? ;) –
Без всякой идеи относительно ржавчины: ваш бенчмаркинг нормальный? Кэш-эффекты, дисперсия, синхронизация тестовых данных ... – sascha
@LukasKalbertodt Да, я запускаю свои тесты с «грузовой кабиной», которая автоматически компилируется с выпуском. –