Резюме : ваш эталонный тест имеет недостатки;просто используйте Vec
(как описано здесь ), возможно, с into_boxed_slice
, поскольку это невероятно маловероятно, что он будет медленнее, чем выделенный массив кучи.
К сожалению, ваши тесты неверны.Прежде всего, вы, вероятно, не компилировали с оптимизациями (--release
для груза, -O
для rustc).Потому что, если бы вы имели, компилятор Rust удалил бы весь ваш код.См. сборку здесь .Зачем?Поскольку вы никогда не наблюдаете вектор / массив, поэтому нет необходимости выполнять всю эту работу в первую очередь.
Кроме того, ваш тест не проверяет то, что вы действительно хотите проверить.Вы сравниваете массив, выделенный стеком, с вектором, выделенным в куче.Вы должны сравнить Vec
с массивом, выделенным для кучи.
Не расстраивайтесь: писать тесты невероятно сложно по многим причинам.Просто помните: если вы мало знаете о написании тестов, лучше не доверяйте собственным тестам, не спросив сначала других.
Я исправил ваш тест и включил все три возможности: Vec
, массив в стеке и массив в куче.Вы можете найти полный код здесь .Результаты:
running 3 tests
test array_heap ... bench: 9,600,979 ns/iter (+/- 1,438,433)
test array_stack ... bench: 9,232,623 ns/iter (+/- 720,699)
test vec_heap ... bench: 9,259,912 ns/iter (+/- 691,095)
Сюрприз: разница между версиями намного меньше, чем дисперсия измерения.Таким образом, мы можем предположить, что они все одинаково быстрые.
Обратите внимание, что этот тест все еще довольно плох.Два цикла можно просто заменить одним циклом, установив для всех элементов массива значение LENGTH - 1
.Из-за быстрого взгляда на сборку (и из-за довольно продолжительного времени в 9 мс) я думаю, что LLVM недостаточно умен, чтобы фактически выполнить эту оптимизацию.Но такие вещи важны, и об этом следует помнить.
Наконец, давайте обсудим, почему оба решения должны быть одинаково быстрыми и существуют ли различия в скорости.
Секция данных Vec<T>
имеет точно такую же структуру памяти, что и [T]
: просто много T
с непрерывно в памяти.Супер просто.Это также означает, что оба демонстрируют одинаковое поведение кэширования (в частности, очень удобное для кэширования).
Единственное отличие состоит в том, что Vec
может иметь больше возможностей, чем элементы.Так что Vec
сам хранит (pointer, length, capacity)
.Это на одно слово больше, чем простой (в штучной упаковке) фрагмент (который хранит (pointer, length)
).Массив в штучной упаковке не должен хранить длину, поскольку он уже находится в типе, так что это просто простой указатель.Неважно, будем ли мы хранить одно, два или три слова, если у вас все равно будут миллионы элементов.
Доступ к одному элементу одинаков для всех трех: сначала мы выполняем проверку границ, а затем вычисляем целевой указатель с помощью base_pointer + size_of::<T>() * index
.Но важно отметить, что массив, хранящий свою длину в типе, означает, что оптимизатор может легче удалить проверку границ!Это может быть реальным преимуществом.
Однако проверки границ уже обычно удаляются интеллектуальным оптимизатором.В коде бенчмарка, который я разместил выше, в сборке нет проверок границТаким образом, хотя упакованный массив может быть немного быстрее за счет удаленных проверок границ, (а) это будет незначительным различием в производительности и (б) очень маловероятно, что у вас будет много ситуаций, когда проверка привязки удаляется для массива, ноне для Vec / ломтик.