簡體   English   中英

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

[英]How to generalize std::chrono::duration(s)?

我為我的大學 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 */
}

我想讓該過程成為遞歸 function。

但是,我不知道這種 function 的參數應該是什么,因為 chrono chrono::duration是一個模板結構(?)

這個 function 看起來有點像這樣:

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()};

然后我可以將指針(甚至是索引)指向時間單位並將其傳遞給 function。

問題是我不知道如何概括這些結構。

CLion 高亮顯示一個錯誤Deduced conflicting types (duration<[...], ratio<3600, [...]>> vs duration<[...], ratio<60, [...]>>) for initializer list element type

使用chrono時最好的一般建議是僅在絕對必須時轉義類型系統(使用.count() )。 這可能與 C 或一些不了解計時的 C++ 庫接口。 在 C++ 20 之前,這也意味着輸出到 stream。

如果我們把自己留在類型系統中,我們可以得到很多很好的轉換,而且總是正確的。

讓我們更正問題中的代碼以反映這一點:

#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 調用getTimeElapsed尚不兼容chrono

我並不完全滿意,所以讓我們在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中,我們只使用一個時間和單位參數對,這意味着我們不能滿足兩種Duration類型的template參數要求。 現在代碼看起來好多了(只保留相關的更改):

...
    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

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 nanoseconds (從end - begin )並將其傳遞給getTimeElapsed 我們現在進行與以前相同的計算以獲取組件單位,但還要跟蹤我們計算了多少單位。 如果elapsed為1'000'000'000ns,則結果為“1 [s]”; 如果elapsed為 1'234'568ns,則結果為“1 [ms] 234 [us]”。 有尾隨空格,但我會把它留給你來解決。

這也意味着我們不再需要我們之前重構的template ,但我添加它們是為了展示我在整個重構過程中的思考過程。 最終程序如下所示:

#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 的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。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM