gpt4 book ai didi

c++ - 如何概括 std::chrono::duration(s)?

转载 作者:行者123 更新时间:2023-11-28 04:08:05 24 4
gpt4 key购买 nike

我为我的大学类(class)编写了三个版本的算法。

一种是蛮力,另一种是贪婪,最后一种是启发式。

我希望能够测量每个算法完成所需的时间。

我正在使用 <chrono>库来实现这一点

现在我的代码是这样的:

#include <iostream>
#include <chrono>
#include <sstream>

using namespace std;

string getTimeElapsed(long time1, const string &unit1, long time2 = 0, const string &unit2 = "") {
stringstream s;
s << time1 << " [" << unit1 << "]";
if (time2) s << " " << time2 << " [" << unit2 << "]";
return s.str();
}

int main() {
auto begin = chrono::system_clock::now();
// algorithm goes here
auto solution = /* can be anything */
auto end = chrono::system_clock::now();
auto diff = end - begin;

string timeElapsed;
auto hours = chrono::duration_cast<chrono::hours>(diff).count();
auto minutes = chrono::duration_cast<chrono::minutes>(diff).count();
if (hours) {
minutes %= 60;
timeElapsed = getTimeElapsed(hours, "h", minutes, "min");
} else {
auto seconds = chrono::duration_cast<chrono::seconds>(diff).count();
if (minutes) {
seconds %= 60;
timeElapsed = getTimeElapsed(minutes, "min", seconds, "s");
} else {
auto milliseconds = chrono::duration_cast<chrono::milliseconds>(diff).count();
if (seconds) {
milliseconds %= 1000;
timeElapsed = getTimeElapsed(seconds, "s", milliseconds, "ms");
} else {
auto microseconds = chrono::duration_cast<chrono::microseconds>(diff).count();
if (milliseconds) {
microseconds %= 1000;
timeElapsed = getTimeElapsed(milliseconds, "ms", microseconds, "μs");
} else {
auto nanoseconds = chrono::duration_cast<chrono::nanoseconds>(diff).count();
if (microseconds) {
nanoseconds %= 1000;
timeElapsed = timeElapsed = getTimeElapsed(microseconds, "μs", nanoseconds, "ns");
} else timeElapsed = getTimeElapsed(nanoseconds, "ns");
}
}
}
}

cout << "Solution [" << solution << "] found in " << timeElapsed << endl;

return 0;
}

如您所见,堆叠的 if-else子句看起来真的很难看,你可以在这里看到一个模式:

if (timeUnit) { 
timeElapsed = /* process current time units */
} else {
/* step down a level and do the same for smaller time units */
}

我想让这个过程成为一个递归函数。

但是,我不知道这种函数的参数应该是什么,因为 chrono::duration是一个模板结构(?)

这个函数看起来有点像这样:

string prettyTimeElapsed(diff, timeUnit) {
// recursion bound condition
if (timeUnit is chrono::nanoseconds) return getTimeElapsed(timeUnit, "ns");

auto smallerTimeUnit = /* calculate smaller unit using current unit */
if (timeUnit) return getTimeElapsed(timeUnit, ???, smallerTimeUnit, ???);
else return prettyTimeElapsed(diff, smallerTimeUnit);
}

我正在考虑这样做:

auto timeUnits = {chrono::hours(), chrono::minutes(), ..., chrono::nanoseconds()};

然后我可以将指针(甚至是索引)指向时间单位并将其传递给函数。

问题是我不知道如何泛化这些结构。

CLion 突出显示错误 Deduced conflicting types (duration<[...], ratio<3600, [...]>> vs duration<[...], ratio<60, [...]>>) for initializer list element type

最佳答案

使用 chrono 时,最好的一般建议是仅在绝对必要时才转义类型系统(使用 .count())。这可能是与 C 或某些不理解计时的 C++ 库的接口(interface)。在 C++ 20 之前,这也意味着输出到流。

如果我们将自己置于类型系统中,我们可以获得很多总是正确的转换。

让我们更正问题中的代码以反射(reflect)这一点:

#include <iostream>
#include <chrono>
#include <sstream>

std::string getTimeElapsed(long time1, const std::string &unit1, long time2 = 0, const std::string &unit2 = "") {
std::stringstream s;
s << time1 << " [" << unit1 << "]";
if (time2) s << " " << time2 << " [" << unit2 << "]";
return s.str();
}

int main() {
auto begin = std::chrono::system_clock::now();
// algorithm goes here
auto solution = "solution"; /* can be anything */
auto end = std::chrono::system_clock::now();
auto diff = end - begin;

std::string timeElapsed{""};
// Let's make the typing and reading easier for us but requires C++14
using namespace std::chrono_literals;
auto hours = std::chrono::duration_cast<std::chrono::hours>(diff);
auto minutes = std::chrono::duration_cast<std::chrono::minutes>(diff % 1h);
if (hours != 0h) {
// We need to escape the type system to call getTimeElapsed
timeElapsed = getTimeElapsed(hours.count(), "h", minutes.count(), "min");
} else {
auto seconds = std::chrono::duration_cast<std::chrono::seconds>(diff % 1min);
if (minutes != 0min) {
timeElapsed = getTimeElapsed(minutes.count(), "min", seconds.count(), "s");
} else {
auto milliseconds = std::chrono::duration_cast<std::chrono::milliseconds>(diff % 1s);
if (seconds != 0s) {
timeElapsed = getTimeElapsed(seconds.count(), "s", milliseconds.count(), "ms");
} else {
auto microseconds = std::chrono::duration_cast<std::chrono::microseconds>(diff % 1ms);
if (milliseconds != 0ms) {
timeElapsed = getTimeElapsed(milliseconds.count(), "ms", microseconds.count(), "μs");
} else {
auto nanoseconds = std::chrono::duration_cast<std::chrono::nanoseconds>(diff % 1us);
if (microseconds != 0us) {
timeElapsed = timeElapsed = getTimeElapsed(microseconds.count(), "μs", nanoseconds.count(), "ns");
} else timeElapsed = getTimeElapsed(nanoseconds.count(), "ns");
}
}
}
}

std::cout << "Solution [" << solution << "] found in " << timeElapsed << std::endl;

return 0;
}

现在我们尽可能长时间地坚持使用chrono。调用 getTimeElapsedchrono 不兼容,但是。

我并不完全满意,所以让我们在 getTimeElapsed 中也支持 duration:

template <typename Duration1, typename Duration2>
std::string getTimeElapsed(Duration1 time1, const std::string &unit1, Duration2 time2, const std::string &unit2) {
std::stringstream s;
s << time1.count() << " [" << unit1 << "]";
if (time2 != Duration2::zero()) s << " " << time2.count() << " [" << unit2 << "]";
return s.str();
}

template <typename Duration1>
std::string getTimeElapsed(Duration1 time1, const std::string &unit1) {
std::stringstream s;
s << time1.count() << " [" << unit1 << "]";
return s.str();
}

我们需要两个版本的 getTimeElapsed 因为在最后一个 else 中我们只使用一对时间和单位参数,这意味着我们不能满足 template 两个 Duration 类型的参数要求。现在代码看起来好多了(只保留相关更改):

...
if (hours != 0h) {
timeElapsed = getTimeElapsed(hours, "h", minutes, "min");
} else {
auto seconds = std::chrono::duration_cast<std::chrono::seconds>(diff % 1min);
if (minutes != 0min) {
timeElapsed = getTimeElapsed(minutes, "min", seconds, "s");
} else {
auto milliseconds = std::chrono::duration_cast<std::chrono::milliseconds>(diff % 1s);
if (seconds != 0s) {
timeElapsed = getTimeElapsed(seconds, "s", milliseconds, "ms");
} else {
auto microseconds = std::chrono::duration_cast<std::chrono::microseconds>(diff % 1ms);
if (milliseconds != 0ms) {
timeElapsed = getTimeElapsed(milliseconds, "ms", microseconds, "μs");
} else {
auto nanoseconds = std::chrono::duration_cast<std::chrono::nanoseconds>(diff % 1us);
if (microseconds != 0us) {
timeElapsed = timeElapsed = getTimeElapsed(microseconds, "μs", nanoseconds, "ns");
} else timeElapsed = getTimeElapsed(nanoseconds, "ns");
}
}
}
}
...

太棒了!但是,我们仍然邀请用户将他们想要的任何内容发送到 getTimeElapsed,除非他们碰巧有一个 .count() 成员,否则将导致编译器错误。让我们稍微限制一下我们的模板:

template <typename Rep1, typename Ratio1, typename Rep2, typename Ratio2>
std::string getTimeElapsed(std::chrono::duration<Rep1, Ratio1> time1, const std::string &unit1, std::chrono::duration<Rep2, Ratio2> time2, const std::string &unit2) {
std::stringstream s;
s << time1.count() << " [" << unit1 << "]";
if (time2 != time2.zero()) s << " " << time2.count() << " [" << unit2 << "]";
return s.str();
}

template <typename Rep, typename Ratio>
std::string getTimeElapsed(std::chrono::duration<Rep, Ratio> time1, const std::string &unit1) {
std::stringstream s;
s << time1.count() << " [" << unit1 << "]";
return s.str();
}

我们不需要为此更改调用代码。我相信这足以帮助您理解如何在更通用的上下文中使用 std::chrono::duration,这是您遇到的一个子问题。

现在我们可以开始解决您的问题,我认为(通过阅读评论)实际上是“我如何整理嵌套的 if 语句并仅打印前两个非零值单位。”

这并不像乍看起来那么简单。在我看来,递归很少是答案。将其视为对单元类型的循环也是过度设计,您需要编写一些代码以从元组中获取当前类型的索引,将其增加一,然后使用它来索引相同的元组获得分辨率更高的下一个单元。然后,当所有这些都说完了之后,您仍然需要知道要打印什么单位来为值提供上下文。我宁愿看到 getTimeElapsed 写成如下:

std::string getTimeElapsed(std::chrono::nanoseconds elapsed, size_t maxUnits = 2)
{
using namespace std::chrono_literals;
std::ostringstream formatted("");

int usedUnits{};

auto hours = std::chrono::duration_cast<std::chrono::hours>(elapsed);
if (hours != 0h)
{
formatted << hours.count() << " [h] ";
++usedUnits;
}

auto minutes = std::chrono::duration_cast<std::chrono::minutes>(elapsed % 1h);
if (minutes != 0min)
{
formatted << minutes.count() << " [min] ";
++usedUnits;
}

auto seconds = std::chrono::duration_cast<std::chrono::seconds>(elapsed % 1min);
if (seconds != 0min && usedUnits != maxUnits)
{
formatted << seconds.count() << " [s] ";
++usedUnits;
}

auto milliseconds = std::chrono::duration_cast<std::chrono::milliseconds>(elapsed % 1s);
if (milliseconds != 0ms && usedUnits != maxUnits)
{
formatted << milliseconds.count() << " [ms] ";
++usedUnits;
}

auto microseconds = std::chrono::duration_cast<std::chrono::microseconds>(elapsed % 1ms);
if (microseconds != 0us && usedUnits != maxUnits)
{
formatted << microseconds.count() << " [us] ";
++usedUnits;
}

auto nanoseconds = std::chrono::duration_cast<std::chrono::nanoseconds>(elapsed % 1us);
if (nanoseconds != 0us && usedUnits != maxUnits)
{
formatted << nanoseconds.count() << " [us] ";
++usedUnits;
}

return formatted.str();
}

将总运行时间取为 std::chrono::nanoseconds(您从 end - begin 已经拥有)并将其传递给 getTimeElapsed。我们现在进行与之前相同的计算以获取组件单位,但也会跟踪我们计算了多少个单位。如果 elapsed 为 1'000'000'000ns,则结果为“1 [s]”;如果 elapsed 为 1'234'568ns,则结果为“1 [ms] 234 [us]”。有尾随空格,但我会把它留给你来解决。

这也意味着我们不再需要我们之前重构的模板,但我添加了它们以展示我在整个重构过程中的思考过程。最终程序如下所示:

#include <chrono>
#include <iostream>
#include <sstream>

std::string getTimeElapsed(std::chrono::nanoseconds elapsed, size_t maxUnits = 2)
{
using namespace std::chrono_literals;
std::ostringstream formatted("");

int usedUnits{};

auto hours = std::chrono::duration_cast<std::chrono::hours>(elapsed);
if (hours != 0h)
{
formatted << hours.count() << " [h] ";
++usedUnits;
}

auto minutes = std::chrono::duration_cast<std::chrono::minutes>(elapsed % 1h);
if (minutes != 0min)
{
formatted << minutes.count() << " [min] ";
++usedUnits;
}

auto seconds = std::chrono::duration_cast<std::chrono::seconds>(elapsed % 1min);
if (seconds != 0min && usedUnits != maxUnits)
{
formatted << seconds.count() << " [s] ";
++usedUnits;
}

auto milliseconds = std::chrono::duration_cast<std::chrono::milliseconds>(elapsed % 1s);
if (milliseconds != 0ms && usedUnits != maxUnits)
{
formatted << milliseconds.count() << " [ms] ";
++usedUnits;
}

auto microseconds = std::chrono::duration_cast<std::chrono::microseconds>(elapsed % 1ms);
if (microseconds != 0us && usedUnits != maxUnits)
{
formatted << microseconds.count() << " [us] ";
++usedUnits;
}

auto nanoseconds = std::chrono::duration_cast<std::chrono::nanoseconds>(elapsed % 1us);
if (nanoseconds != 0us && usedUnits != maxUnits)
{
formatted << nanoseconds.count() << " [us] ";
++usedUnits;
}

return formatted.str();
}

int main() {
auto begin = std::chrono::system_clock::now();
// algorithm goes here
auto solution = "solution"; /* can be anything */
auto end = std::chrono::system_clock::now();
auto diff = end - begin;

using namespace std::chrono_literals;
std::cout << "Solution [" << solution << "] found in " << getTimeElapsed(1'234'567ns) << std::endl;

return 0;
}

如果您想更进一步并且永远不需要转义类型系统,那么我建议您查看 Howard Hinnant's date图书馆。该库是 C++20 中新的 chrono 功能的基础,并将字符串格式引入表格。只需以任何适合您的方式从库中包含 date.h 并按如下方式修改 getTimeElapsed:

std::string getTimeElapsed(std::chrono::nanoseconds elapsed, size_t maxUnits = 2)
{
using namespace std::chrono_literals;
std::ostringstream formatted("");

int usedUnits{};

auto hours = std::chrono::duration_cast<std::chrono::hours>(elapsed);
if (hours != 0h)
{
formatted << hours << " ";
++usedUnits;
}

auto minutes = std::chrono::duration_cast<std::chrono::minutes>(elapsed % 1h);
if (minutes != 0min)
{
formatted << minutes << " ";
++usedUnits;
}

auto seconds = std::chrono::duration_cast<std::chrono::seconds>(elapsed % 1min);
if (seconds != 0min && usedUnits != maxUnits)
{
formatted << seconds << " ";
++usedUnits;
}

auto milliseconds = std::chrono::duration_cast<std::chrono::milliseconds>(elapsed % 1s);
if (milliseconds != 0ms && usedUnits != maxUnits)
{
formatted << milliseconds << " ";
++usedUnits;
}

auto microseconds = std::chrono::duration_cast<std::chrono::microseconds>(elapsed % 1ms);
if (microseconds != 0us && usedUnits != maxUnits)
{
formatted << microseconds << " ";
++usedUnits;
}

auto nanoseconds = std::chrono::duration_cast<std::chrono::nanoseconds>(elapsed % 1us);
if (nanoseconds != 0us && usedUnits != maxUnits)
{
formatted << nanoseconds << " ";
++usedUnits;
}

return formatted.str();
}

使用与之前相同的值,结果现在为:“1ms 234us”代表 1'234'567ns。

关于c++ - 如何概括 std::chrono::duration(s)?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58352866/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com