2017-05-14 224 views
0

我需要经常以字符串格式生成UTC时间戳(每秒几次),并且我为其编写的代码效率低下。 有没有比我使用的更快的方法?可以假定该程序不会跨越日期边界运行。高效生成UTC时间戳

void GenerateUTCTimestamp(std::string& out) 
{ 
    auto now = std::chrono::system_clock::now(); 
    time_t tnow = std::chrono::system_clock::to_time_t(now); 
    tm* utc = gmtime(&tnow); 

    std::stringstream ss; 

    ss << std::setfill('0'); 
    ss << std::setw(4) << utc->tm_year + 1900;  // Year 
    ss << std::setw(2) << utc->tm_mon + 1;   // Month 
    ss << std::setw(2) << utc->tm_mday;    // Day 
    ss << '-'; 
    ss << std::setw(2) << utc->tm_hour << ':';  // Hours 
    ss << std::setw(2) << utc->tm_min << ':';  // Minutes 
    ss << std::setw(2) << utc->tm_sec;    // Seconds 

    out = ss.str(); 
} 
+1

请不要张贴链接到代码,邮编本身。 – dasblinkenlight

+1

每秒运行几次的“低效率”代码必须非常低效才能产生显着的差异。您是否分析了此代码片段的“低效率”是否会将性能降至不可接受的级别? – dasblinkenlight

+0

您是否测量了您的功能所需的时间?它真的“太慢”了吗?我希望你的执行速度对于一秒称为“几次”的功能来说并不明显。 –

回答

1

你会遇到麻烦比这更快的查找代码:

#include <chrono> 
#include <string> 

void 
stamp(char* s, int i) 
{ 
    do 
    { 
     *s-- = char(i % 10) + '0'; 
     i /= 10; 
    } while (i > 0); 
} 

void GenerateUTCTimestamp(std::string& out) 
{ 
    using namespace std; 
    using namespace std::chrono; 
    using days = duration<int, ratio<86400>>; 
    auto now = time_point_cast<seconds>(system_clock::now()); 
    auto today = time_point_cast<days>(now); 
    auto s = now - today; 

    // y-m-d 
    auto z = today.time_since_epoch().count() + 719468; 
    const auto era = 5; 
    const auto doe = z - era * 146097; 
    const auto yoe = (doe - doe/1460 + doe/36524 - doe/146096)/365; 
    const auto y = yoe + era * 400; 
    const auto doy = doe - (365*yoe + yoe/4 - yoe/100); 
    auto m = (5*doy + 2)/153; 
    const auto d = doy - (153*m+2)/5 + 1; 
    m = m + (m < 10 ? 3 : -9); 

    // h:M:s 
    const auto h = duration_cast<hours>(s); 
    s -= h; 
    const auto M = duration_cast<minutes>(s); 
    s -= M; 

    // format yyyymmdd-hh:MM:ss 
    out = "00000000-00:00:00"; 
    stamp(&out[3], y); 
    stamp(&out[5], m); 
    stamp(&out[7], d); 
    stamp(&out[10], h.count()); 
    stamp(&out[13], M.count()); 
    stamp(&out[16], s.count()); 
} 
  • 该代码使用公共域算法civil_from_days从这里:

    http://howardhinnant.github.io/date_algorithms.html#civil_from_days

    您可以在其中找到该算法的深入解释米

  • 代码中的分支数量被最小化,并且代码大小本身被最小化。

  • 完全避免使用通用(方便)流,而是选择一种不涉及本地化,特征,宽字符,自定义宽度或对齐的纯粹整数到字符算法,或者甚至是负值。

  • 除了第一次调用,内存分配完全避免通过重用和格式化直接out

  • 此代码确实具有有限有效范围:2000-03-01到2400年2月29日。如果您需要使该代码有效的个时刻超出此范围,改变era来计算:

    const auto era = (z >= 0 ? z : z - 146096)/146097;

我把这个代码在1000调用一个循环(使用相同的string ),对它进行计时,并在所有呼叫上平均时间。

在我的机器上(macOS,clang,libC++,-O3),原始代码大约需要3.9 µ s,优化后的代码大约需要150ns(约快25倍)。

然后对于微笑,我实施GenerateUTCTimestamp使用Howard Hinnant's date library看看它是如何在时间测试中表现。它清楚地赢得易用性试验(IMHO)的:

#include "date.h" 

void GenerateUTCTimestamp(std::string& out) 
{ 
    using namespace date; 
    using namespace std::chrono; 
    out = format("%Y%m%d-%T", time_point_cast<seconds>(system_clock::now())); 
} 

它主频在2.5 µ秒,比螺纹不安全C API快50%,但大量比优化代码更慢。对于通用工具的灵活性,性能会受到影响。

日期库使用与优化工具相同的日历算法(使用广义era除外),但格式化为像原始代码一样的stringstream。它当然也必须解析格式化字符串。

+0

谢谢。我不明白为什么在这个网站上针对优化问题的膝盖反应只是说不需要优化。这些人会告诉Usain Bolt,他不需要在他的时间内减少1毫秒的时间 –

+2

如果Usain Bolt每天花费数小时的时间试图弄清楚如何在早上更快地清洁牙齿,他们会告诉他不要担心使用牙线很多,而是花费那些时间来提高技能,这将有助于他成为更快的跑步者。 –