Tôi đã đánh giá hiệu suất của std::none_of
dựa trên ba cách triển khai thủ công khác nhau bằng i) một vòng lặp for
, ii) vòng lặp for
và iii) vòng lặp. Trước sự ngạc nhiên của tôi, tôi thấy rằng trong khi cả ba triển khai thủ công đều mất khoảng thời gian tương tự, std::none_of
nhanh hơn đáng kể. Câu hỏi của tôi là - tại sao lại như vậy?Tại sao std :: none_of nhanh hơn vòng quay tay?
Tôi đã sử dụng thư viện điểm chuẩn của Google và được biên dịch với -std=c++14 -O3
. Khi chạy thử nghiệm, tôi đã hạn chế ái lực của quy trình đối với một bộ xử lý đơn lẻ. Tôi nhận được kết quả sau sử dụng GCC 6.2:
Benchmark Time CPU Iterations
--------------------------------------------------------
benchmarkSTL 28813 ns 28780 ns 24283
benchmarkManual 46203 ns 46191 ns 15063
benchmarkRange 48368 ns 48243 ns 16245
benchmarkIterator 44732 ns 44710 ns 15698
On Clang 3.9, std::none_of
cũng là nhanh hơn so với hướng dẫn for
loop mặc dù sự khác biệt tốc độ nhỏ hơn. Đây là mã thử nghiệm (chỉ bao gồm hướng dẫn sử dụng vòng lặp cho ngắn gọn):
#include <algorithm>
#include <array>
#include <benchmark/benchmark.h>
#include <functional>
#include <random>
const size_t N = 100000;
const unsigned value = 31415926;
template<size_t N>
std::array<unsigned, N> generateData() {
std::mt19937 randomEngine(0);
std::array<unsigned, N> data;
std::generate(data.begin(), data.end(), randomEngine);
return data;
}
void benchmarkSTL(benchmark::State & state) {
auto data = generateData<N>();
while (state.KeepRunning()) {
bool result = std::none_of(
data.begin(),
data.end(),
std::bind(std::equal_to<unsigned>(), std::placeholders::_1, value));
assert(result);
}
}
void benchmarkManual(benchmark::State & state) {
auto data = generateData<N>();
while (state.KeepRunning()) {
bool result = true;
for (size_t i = 0; i < N; i++) {
if (data[i] == value) {
result = false;
break;
}
}
assert(result);
}
}
BENCHMARK(benchmarkSTL);
BENCHMARK(benchmarkManual);
BENCHMARK_MAIN();
Lưu ý rằng việc tạo dữ liệu bằng trình tạo số ngẫu nhiên không liên quan. Tôi nhận được kết quả tương tự khi chỉ cần đặt phần tử i
-th thành i
và kiểm tra xem giá trị N + 1
có được chứa không.
Tại sao không so sánh a) việc triển khai và b) mã được tạo? –
Tôi không biết tại sao trong trường hợp của bạn nhưng vì thư viện được cung cấp bởi trình biên dịch, họ có thể sử dụng các thủ thuật nền tảng cụ thể (ví dụ như gợi ý nhánh?) Để cung cấp hành vi Standard C++. Vì vậy, kết quả này (nếu hợp lệ) sẽ không làm tôi ngạc nhiên. – Galik
Nó có vẻ liên quan đến thực tế bạn đang sử dụng các số nguyên không dấu: Trình biên dịch có thể giả định rằng các số nguyên đã ký không bao giờ là tràn (vì nó là hành vi không xác định theo tiêu chuẩn) và sử dụng thực tế này để tối ưu hóa mạnh mẽ. Rõ ràng 'std :: none_of' có một chuyên môn cho các số nguyên không dấu mà bạn không nhận được trong các chức năng viết tay của bạn. Nếu bạn chuyển sang 'long' thay vì' size_t' và 'unsigned' thì phiên bản thủ công thực sự nhanh hơn. – Corristo