2017-05-14 227 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每天花費數小時的時間試圖弄清楚如何在早上更快地清潔牙齒,他們會告訴他不要擔心使用牙線很多,而是花費那些時間來提高技能,這將有助於他成爲更快的跑步者。 –