Большое спасибо за наводку. Я действительно не обратил внимание на это обстоятельство.
Но, как справедливо заметили выше, было бы неплохо объяснить в чём тут собака зарыта.
Зайду издалека. Поскольку Rust хоть и высокоуровневый язык, но всё же язык системного программирования, даже не опускаясь, благодаря его высокоуровневым абстракциям, до ручного выделения памяти, мы можем в некоторых случаях гибко выбирать, сколько памяти выделять при создании экземпляров структур данных, если эти типы данных контейнеры, хранящие в себе другие данные.
Как можно вкратце ознакомиться в той части стандартной документации, которая посвящена такой структуре данных, как вектор Vec, вектор имеет две таких смежных характеристики, как длина length
и емкость capacity
.
Первая указывает, сколько элементов выбранного типа хранится в векторе на текущий момент, вторая показывает, сколько элементов в нем вообще может храниться без переаллокации. Емкость равняется произведению размера типа элементов, которые в нем хранятся, на Н-ое их количество и определяет количество памяти, которое выделено вектору для хранения данных. Что дает емкость? Она позволяет избавиться от переаллокаций. Если длина вектора превысит его емкость, соответственно вектор не сможет разместить элементы в выделенном ему пространстве и запросит другой, более емкий кусок памяти, в котором по новой переразместятся его данные. Это и называется переаллокацией, когда уже существующие данные меняют свою прописку. Процесс этот, разумеется, небыстрый, и его желательно избегать. Как это можно сделать? Заранее определить, какая емкость потребуется вектору и выделить память под нее единоразово. Эту возможность и дает метод-конструктор Vec::with_capacity
. Что важно понимать, так это то, что в этом случае создается вектор указанной емкости, но нулевой длины.
Почему же бенчмарк ввел в заблуждение?
Обратим внимание на первый цикл:
for _ in 0..iters {
...
for i in 0..v.len() {
v.len()
раскрывается в v.inner.len()
. Поскольку вектор изначально пустой (см. выше), то внутренний цикл будет выполняться 0 раз.
Во втором же примере
for _ in 0..iters {
...
for i in 0..v.other_len() {
v.other_len()
раскрывается в v.len
, и внутренний цикл будет постоянно “бегать” от 0 до 1000, таким образом бенчмарк измеряет неидентичные с алгоритмической точки зрения куски кода и, следовательно, дает неверный результат.
cast @Stanislav-Lapata
// Даже здесь умудрился напутать с выводами, но исправился.