Я большой сторонник максимально возможного использования стандартной библиотеки C ++. Современные реализации библиотек быстры и стабильны, просты в использовании, имеют понятные интерфейсы и унифицированную семантику. Это было не всегда так, но это было в последние несколько лет. здесь я собираюсь взглянуть на массивы, подобные массивам в C ++, в частности, на типы std :: array и std :: vector.
Оба этих типа интегрируются с различными стандартными библиотечными алгоритмами аналогичным образом. Первый чисто статический; Впрочем, пока второй динамичен. Сегодня мы рассмотрим статическую производительность.
Вам также может понравиться: Arrays.hashCode () Vs. Objects.hash ()
Во-первых, давайте посмотрим на общее удобство использования. Честно говоря, поскольку STL имеет массу поддержки устаревших типов C, они оба работают довольно хорошо:
C ++
1
for(const auto& i : std_numbers)
2
{
3
std::cout << i << std::endl;
4
}
5
for(const auto& i : a_numbers)
7
{
8
std::cout << i << std::endl;
9
}
10
auto min = std::min_element(std_numbers.begin(), std_numbers.end());
12
std::cout << "std Min: " << *min << std::endl;
13
min = std::min_element(std::begin(a_numbers), std::end(a_numbers));
15
std::cout << "a Min: " << *min << std::endl;
Здесь есть только небольшие различия - особенно при вызове алгоритма (то есть std :: min_element (.)). Тип std :: array имеет встроенную поддержку итераторов; нам нужно использовать функции адаптера из STL для генерации итератора из массива. Это конкретное отличие тривиально.
Есть еще один, которого нет.
Вы можете получить доступ к данным в массиве std ::, используя скобочную запись или метод std :: array :: at (.):
C ++
xxxxxxxxxx
1
std_numbers[0] = 100;
2
for(const auto& i : std_numbers)
3
{
4
std::cout << i << std::endl;
5
}
6
std_numbers.at(0) = 1000;
8
for(const auto& i : std_numbers)
9
{
10
std::cout << i << std::endl;
11
}
12
a_numbers[0] = 100;
14
for(const auto& i : a_numbers)
15
{
16
std::cout << i << std::endl;
17
}
Std :: array :: at (.) Обращается к элементам в массиве с проверкой границ. Обозначения в скобках отсутствуют, как и для родных массивов. Это большое дело! Неопределенные ошибки в массивах являются основным источником значительных уязвимостей в программном обеспечении, которые можно использовать. Вы можете избежать этих проблем с помощью std :: array :: at (.). Это само по себе является причиной использования std :: array.
Как насчет производительности?
Давайте рассмотрим несколько конкретных операций - создание, назначение, поиск и копирование. Вот код, который мы будем использовать:
C ++
xxxxxxxxxx
1
template<typename T>
2
void evaluation_engine(const T& f, const std::string& label = "Container")
3
{
4
std::vector<double> samples;
5
for (auto i = 0; i < sample_max; ++i)
6
{
7
const auto start_time = std::chrono::high_resolution_clock::now();
8
for (auto j = 0; j < loop_max; ++j)
9
{
10
f();
11
}
12
const auto stop_time = std::chrono::high_resolution_clock::now();
13
const auto duration = stop_time — start_time;
14
samples.push_back(duration.count());
15
}
16
print_statistics(samples, label);
17
}
Sample_max равно 100, а loop_max 1000. Эта функция принимает лямбда-выражение или функцию и оценивает ее, синхронизируя доступ. print_statistics () вычислит несколько статистических данных и напечатает их (опущено).
Наши тесты на создание довольно просты:
C ++
xxxxxxxxxx
1
evaluation_engine([]()
2
{
3
constexpr std::array<int, 5> numbers {0, 1, 2, 3, 4};
4
}, "STL Creation");
5
evaluation_engine([]()
6
{
7
constexpr int numbers[] {0, 1, 2, 3, 4};
8
}, "Array");
Это дает нам:
Простой текст
xxxxxxxxxx
1
Statistics for STL Creation
2
minimum: 1787; maximum: 1859; mean: 1824.66; std: 27.2845
3
4
Statistics for Array
5
minimum: 1830; maximum: 1910; mean: 1862.86; std: 27.3357
Создание на основе STL эквивалентно созданию собственного массива. Здесь это кажется немного быстрее, но это, вероятно, артефакт состояния моего компьютера при выполнении этих тестов. Как насчет доступа? Сначала давайте посмотрим на доступ к массиву:
C ++
xxxxxxxxxx
1
evaluation_engine([&a_numbers]()
2
{
3
const auto value = a_numbers[3];
4
}, "Array Access");
Дает нам такие результаты:
Простой текст
xxxxxxxxxx
1
Statistics for Array Access
2
minimum: 2147; maximum: 2241; mean: 2160.32; std: 11.3568
Теперь std :: array:
C ++
xxxxxxxxxx
1
evaluation_engine([&std_numbers]()
2
{
3
const auto value = std_numbers[3];
4
}, "STL Access");
Простой текст
xxxxxxxxxx
1
Statistics for STL Access
2
minimum: 3629; maximum: 3998; mean: 3760.79; std: 121.434
Использование std :: array :: at (.)
Простой текст
xxxxxxxxxx
1
Statistics for STL at() Access
2
minimum: 4320; maximum: 5094; mean: 4684.56; std: 242.694
Теперь посмотрим на доступ к std :: array:
C ++
xxxxxxxxxx
1
evaluation_engine([&std_numbers]()
2
{
3
const auto value = std_numbers.at(3);
4
}, "STL at() Access");
Теперь я использую часы с высоким разрешением в этих измерениях, и меня не очень интересует время настенных часов. Я заинтересован в сравнительной производительности, хотя. Основываясь на этих измерениях, мы видим, что доступ к собственным массивам чуть более чем на 40% медленнее, чем доступ к std :: array. Доступ к Std :: array :: at (.) На 20% медленнее, чем этот.
В моей системе эти меры в наносекундах, так что это все еще чертовски быстро. Тем не менее, использование std :: array не является бесплатным.
Как насчет назначения?
Простой текст
xxxxxxxxxx
1
Statistics for Array assignment
2
minimum: 2057; maximum: 2775; mean: 2205.31; std: 81.6836
3
4
Statistics for STL assignment
5
minimum: 3323; maximum: 3556; mean: 3403.62; std: 28.7593
6
Statistics for STL at() Assignment
8
minimum: 3640; maximum: 3882; mean: 3705.12; std: 42.9675
Снова, немного диапазона, с std :: array :: at (.), Являющимся самым медленным. Давайте посмотрим на копирование:
Простой текст
xxxxxxxxxx
1
Statistics for Array copy
2
minimum: 2091; maximum: 2156; mean: 2102.45; std: 11.4301
3
Statistics for STL copy
5
minimum: 2608; maximum: 2729; mean: 2626.83; std: 17.4178
Ясно, что во всех случаях, кроме первоначального создания, собственный массив более производительный, чем тип std :: array. Имейте в виду, что в каждом из этих случаев я выполняю 100 000 операций, и мы говорим о разнице в разнице между собственным массивом и временем std :: array в течение двух миллисекунд.
Какой использовать? Ну, я бы предложил вам использовать std :: array практически во всех случаях - по крайней мере, всегда начинайте с него. В некоторых случаях вам может потребоваться перейти к собственному массиву, но, честно говоря, обычно вы можете повысить производительность за счет улучшения алгоритмов, чем за счет изменения структуры данных, которую вы используете.
Дальнейшее чтение
Две строки кода и три особенности C ++ 17: шаблон перегрузки