Мы разрабатывали фрагмент кода, который будет проверять, когда пользователю не разрешается попасть в сектор за определенный период времени, один из моих коллег создал функцию, которая в коде ниже isAllowed и содержит несколько сравнений, Я использовал другой подход, который является функцией isAllowed2, которая использует количество секунд между периодами времени.
Сначала мы не сомневались, что его функция будет быстрее, но это не так, если вы действительно выполняете код и сравниваете скорость, даже если разницу - это то, что мы можем полностью игнорировать, мы хотели бы знать, почему это что тот, который "должен" быть быстрее, на самом деле медленнее.
Учитывая следующий код:
#include <iostream>
#include <chrono>
using namespace std;
using namespace std::chrono;
struct timing {
short hour;
short minute;
};
bool isAllowed(timing &from, timing &to, timing &actual) {
return !(((from.hour > to.hour && (actual.hour >= from.hour || actual.hour <= to.hour)) ||
(actual.hour >= from.hour && actual.hour <= to.hour)) &&
!(actual.minute > from.minute && actual.minute < to.minute));
}
long getSecs(short hour, short minutes) {
return (hour * 3600) + (minutes * 60);
}
bool isAllowed2(timing &from, timing &to, timing ¤t) {
long secsFrom = getSecs(from.hour, from.minute);
long secsTo = getSecs(to.hour, to.minute);
long secsCurrent = getSecs(current.hour, current.minute);
if (secsFrom > secsTo) secsTo += 24 * 60 * 60;
if (secsCurrent > secsFrom && secsCurrent < secsTo) {
return false;
}
return true;
}
int main() {
//debug messages
std::string okay = " - ok";
std::string error = " - er";
std::string receive = " - allowed";
std::string notReceive = " - denied";
//testing times
int const testDataCount = 5;
timing from[testDataCount] = {
{ 16, 30 },
{ 8, 30 },
{ 10, 30 },
{ 0, 30 },
{ 0, 0 }
};
timing to[testDataCount] = {
{ 8, 30 },
{ 20, 0 },
{ 20, 0 },
{ 6, 0 },
{ 7, 0 }
};
for (int i = 0; i < testDataCount; i++) {
std::cout << i + 1 << ": " << from[i].hour << ":" << from[i].minute << " to " << to[i].hour << ":"
<< to[i].minute << std::endl;
}
//test current times
timing current[5] = {
{ 12, 0 },
{ 23, 0 },
{ 17, 30 },
{ 15, 12 },
{ 0, 20 }
};
bool ergValues[][testDataCount] = {
{ true, false, false, true, true },
{ false, true, true, true, true },
{ false, false, false, true, true },
{ true, false, false, true, true },
{ false, true, true, true, false }
};
long totalNs1 = 0;
long totalNs2 = 0;
for (int i = 0; i < 4; i++) {
std::cout << std::endl << i + 1 << ". Test: " << current[i].hour << ":" << current[i].minute << std::endl;
for (int j = 0; j < testDataCount; j++) {
high_resolution_clock::time_point t1 = high_resolution_clock::now();
bool response = isAllowed(from[j], to[j], current[i]);
high_resolution_clock::time_point t2 = high_resolution_clock::now();
high_resolution_clock::time_point t3 = high_resolution_clock::now();
bool response2 = isAllowed2(from[j], to[j], current[i]);
high_resolution_clock::time_point t4 = high_resolution_clock::now();
long ns1 = duration_cast<std::chrono::nanoseconds>(t2 - t1).count();
totalNs1 += ns1;
long ns2 = duration_cast<std::chrono::nanoseconds>(t4 - t3).count();
totalNs2 += ns2;
std::cout << j + 1 << "\t\t:1:" << ns1 << "ns: " << response << (response == ergValues[i][j] ? okay : error) << "\t\t:2:" << ns2 << "ms: " << response2 << (response2 == ergValues[i][j] ? okay : error) << "\t\t"
<< (ergValues[i][j] ? receive : notReceive) << std::endl;
}
}
std::cout << "\r\ntotalNs1 = " << totalNs1 << "\r\ntotalNs2 = " << totalNs2 << "\r\n\r\n";
return 0;
}
Результат, очевидно, всегда будет отличаться, но независимо от того, что totalNs2 всегда будет меньше, чем totalNs1.
Пример:
totalNs1 = 38796
totalNs2 = 25913
Я тестировал это на AMD Phenom II X4 и Intel i7-3770, как на Windows 10, так и на Debian 8, с аналогичными результатами.
Итак, наконец, вопрос в том, почему функция isAllowed2 быстрее, чем isAllowed?
Примечание. Это в основном вопрос любопытства, если кто-то считает, что название или теги не являются наиболее подходящими, пожалуйста, дайте мне знать, и я их соответствующим образом изменю, пожалуйста, извините за возможные ошибки грамматики поскольку английский не является моим родным языком.
ИЗМЕНИТЬ
Тем временем я изучал дальше, основываясь на всех предложениях и комментариях, включая этот > невероятно подробный ответ, понимая, насколько может быть неточным микро-бенчмаркинг (ОГРОМНЫЙ спасибо Baum mit Augen за ссылку этот удивительный разговор, который очень помог) Я закончил использование Google microbenchmark library, чтобы получить более "точные" результаты, кажется, что функция isAllowed на самом деле быстрее (скомпилирована без оптимизации), как показано на выходе из библиотеки.
Run on (8 X 2395 MHz CPU s)
-----------------------------------------------------------------------
Benchmark Time CPU Iterations
-----------------------------------------------------------------------
BM_isAllowed/2/min_time:2.000 22 ns 22 ns 128000000
BM_isAllowed/4/min_time:2.000 22 ns 22 ns 137846154
BM_isAllowed/8/min_time:2.000 22 ns 22 ns 128000000
BM_isAllowed/16/min_time:2.000 22 ns 22 ns 128000000
BM_isAllowed/22/min_time:2.000 22 ns 22 ns 137846154
BM_isAllowed2/2/min_time:2.000 24 ns 24 ns 112000000
BM_isAllowed2/4/min_time:2.000 24 ns 24 ns 119466667
BM_isAllowed2/8/min_time:2.000 24 ns 24 ns 119466667
BM_isAllowed2/16/min_time:2.000 24 ns 24 ns 119466667
BM_isAllowed2/22/min_time:2.000 24 ns 24 ns 119466667
Примечание. Как отметил Мартин Боннер, функция isAllowed, похоже, имеет логический недостаток, не используйте его производственный код.
Ниже вы можете найти код, который я использовал для этого теста, сообщите мне, есть ли в нем недостатки, поскольку я не знаком с Библиотека Google.
Важно, этот код был скомпилирован с Visual Studio 2015, и оптимизация должна быть отключена для раздела, который мы хотим проверить.
#include "benchmark/benchmark.h"
using namespace std;
using namespace benchmark;
#pragma optimize( "[optimization-list]", {on | off} )
volatile const long extraDay = 24 * 60 * 60;
#pragma optimize( "", off )
struct timing {
short hour;
short minute;
timing(short hour, short minute) : hour(hour), minute(minute) {}
};
static void BM_isAllowed(benchmark::State& state) {
while (state.KeepRunning())
{
timing from(state.range(0), state.range(0));
timing to(state.range(0), state.range(0));
timing current(state.range(0), state.range(0));
bool b = !(((from.hour > to.hour && (current.hour >= from.hour || current.hour <= to.hour)) ||
(current.hour >= from.hour && current.hour <= to.hour)) &&
!(current.minute > from.minute && current.minute < to.minute));
}
}
static void BM_isAllowed2(benchmark::State& state) {
while (state.KeepRunning())
{
timing from(state.range(0), state.range(0));
timing to(state.range(0), state.range(0));
timing current(state.range(0), state.range(0));
bool b;
long secsFrom = secsFrom = (from.hour * 3600) + (from.minute * 60);
long secsTo = (to.hour * 3600) + (to.minute * 60);
long secsCurrent = (current.hour * 3600) + (current.minute * 60);
if (secsFrom > secsTo)
secsTo += extraDay;
if (secsCurrent > secsFrom && secsCurrent < secsTo)
b = false;
else
b = true;
}
}
#pragma optimize( "", on )
BENCHMARK(BM_isAllowed)->RangeMultiplier(2)->Range(2, 22)->MinTime(2);
BENCHMARK(BM_isAllowed2)->RangeMultiplier(2)->Range(2, 22)->MinTime(2);
BENCHMARK_MAIN();