【问题标题】:uint64 UTC timeuint64 UTC 时间
【发布时间】:2009-05-21 00:08:57
【问题描述】:

我有一个没有格式存储在 uint64 中的 UTC 日期时间,即:20090520145024798 我需要得到这个时间的小时、分钟、秒和毫秒。通过将其转换为字符串并使用子字符串,我可以很容易地做到这一点。但是,这段代码需要非常快,所以我想避免字符串操作。有没有更快的方法,也许使用位操作来做到这一点?哦,顺便说一句,这需要在 Linux 上用 C++ 完成。

【问题讨论】:

    标签: c++ bit-manipulation utc


    【解决方案1】:
    uint64 u = 20090520145024798;
    unsigned long w = u % 1000000000;
    unsigned millisec = w % 1000;
    w /= 1000;
    unsigned sec = w % 100;
    w /= 100;
    unsigned min = w % 100;
    unsigned hour = w / 100;
    unsigned long v = w / 1000000000;
    unsigned day = v % 100;
    v /= 100;
    unsigned month = v % 100;
    unsigned year = v / 100;
    

    这个解决方案之所以中间从uint64 u切换到unsigned long w(和v)是因为YYYYMMDD和HHMMSSIII适合32位,32位除法比64位除法快一些系统。

    【讨论】:

    • 我知道您担心 64 位除法,您可以避免除其中一个(对)之外的所有除法。从 1E9 开始拆分,将 8 位日期放在一个 int32 中,将 9 位时间放在另一个中。
    【解决方案2】:

    根据 pts 和 onebyone 的建议,以下是他们在 2 核处理器上使用 32 位和 64 位操作的方法的基准:

    #include <stdio.h>
    #include <sys/time.h>
    #include <sys/resource.h>
    
    typedef unsigned long long uint64;
    
    struct outs {
        unsigned millisec, sec, min, hour, day, month, year;
    };
    
    void tbreakdown2(uint64 u, struct outs *outp) {
        outp->millisec = u % 1000;
        u /= 1000;
        outp->sec = u % 100;
        u /= 100;
        outp->min = u % 100;
        u /= 100;
        outp->hour = u % 100;
        unsigned long v = u / 100;
        outp->day = v % 100;
        v /= 100;
        outp->month = v % 100;
        outp->year = v / 100;
    }
    
    
    void tbreakdown(uint64 u, struct outs *outp) {
        unsigned int  daypart, timepart; //4000000000
                                         //  YYYYMMDD
                                         //HHMMssssss
    
        daypart = u / 1000000000ULL;
        timepart = u % 1000000000ULL;
    
        outp->millisec = timepart % 1000;
        timepart /= 1000;
        outp->sec = timepart % 100;
        timepart /= 100;
        outp->min = timepart % 100;
        timepart /= 100;
        outp->hour = timepart;
    
        outp->day = daypart % 100;
        daypart /= 100;
        outp->month = daypart % 100;
        daypart /= 100;
        outp->year = daypart;
    }
    
    uint64 inval = 20090520145024798ULL;
    
    void printstruct(uint64 u, struct outs *outp) {
        printf("%018llu\n", u);
        printf("%04d-%02d-%02d %02d:%02d:%02d.%04d\n",
                outp->year, outp->month, outp->day,
                outp->hour, outp->min, outp->sec,
                outp->millisec);
    }
    
    void print_elapsed(struct timeval *tv_begin, struct timeval *tv_end) {
        unsigned long long mcs_begin, mcs_end, mcs_delta;
    
        mcs_begin = (unsigned long long)tv_begin->tv_sec * 1000000ULL;
        mcs_begin += tv_begin->tv_usec;
        mcs_end = (unsigned long long)tv_end->tv_sec * 1000000ULL;
        mcs_end += tv_end->tv_usec;
    
        mcs_delta = mcs_end - mcs_begin;
    
        printf("Elapsed time: %llu.%llu\n", mcs_delta / 1000000ULL, mcs_delta % 1000000ULL);
    }
    
    int main() {
        struct outs out;
        struct outs *outp = &out;
        struct rusage rusage_s;
        struct rusage begin, end;
    
        __sync_synchronize();
        printf("Testing impl 1:\n");
        tbreakdown(inval, outp);
        printstruct(inval, outp);
    
        __sync_synchronize();
        getrusage(RUSAGE_SELF, &begin);
        for (int i = 0; i < 100000000; i++) {
            __sync_synchronize();
            tbreakdown(inval, outp);
            __sync_synchronize();
        }
        getrusage(RUSAGE_SELF, &end);
        print_elapsed(&begin.ru_utime, &end.ru_utime);
    
        printf("Testing impl 2:\n");
        tbreakdown2(inval, outp);
        printstruct(inval, outp);
    
        __sync_synchronize();
        getrusage(RUSAGE_SELF, &begin);
        for (int i = 0; i < 100000000; i++) {
            __sync_synchronize();
            tbreakdown2(inval, outp);
            __sync_synchronize();
        }
        getrusage(RUSAGE_SELF, &end);
        print_elapsed(&begin.ru_utime, &end.ru_utime);
    
        return 0;
    }
    

    还有输出:

    =====32-bit=====
    Testing impl 1:
    020090520145024798
    2009-05-20 14:50:24.0798
    Elapsed time: 6.840427
    Testing impl 2:
    020090520145024798
    2009-05-20 14:50:24.0798
    Elapsed time: 19.921245
    
    =====64-bit=====
    Testing impl 1:
    020090520145024798
    2009-05-20 14:50:24.0798
    Elapsed time: 3.152197
    Testing impl 2:
    020090520145024798
    2009-05-20 14:50:24.0798
    Elapsed time: 4.200262
    

    如您所见,避免过多的 64 位操作即使在本机 64 位模式下也有帮助 - 但在 32 位模式下会产生巨大差异。

    基准测试是在 2.2GHz 的 core2duo T7500 处理器下执行的,并在 -O3 上使用 gcc 4.3.3 编译。您看到的那些内存屏障是为了确保编译器不会尝试优化实际操作,同时允许它在选择时内联它。

    【讨论】:

    • 第一次调用 __sync_synchronize() 给你的是什么(main 的第 6 行)?我知道您可能正在尝试保护对 outp 的写入不下沉,但是 printf 不会起到相同的效果(调用调用 i/o 的函数)吗?谢谢。
    • @tholomew,因为编译器知道outp 还没有转义函数,它不需要将printf 视为outp 的内存屏障(因为没有任何printf 可以可能取决于或影响outp 的值)。
    • c++ 抽象状态机在调用库 I/O 函数时显示出可观察的行为:“[C++] 抽象机的可观察行为是其对易失性数据的读取和写入以及对库的调用的序列I/O 函数。访问由 volatile 左值指定的对象、修改对象、调用库 I/O 函数或调用执行任何这些操作的函数都是副作用,即执行状态的变化环境。”并且副作用保证在序列点发生/可观察。
    • @tholomew,当然,但序列点与内存屏障无关。这里的问题更多的是“假设规则”——如果一个程序的行为就好像它是由 C++ 抽象机实现的,那么它就是一个有效的翻译。将outp = &amp;out 向下移动并进入函数调用的行为“好像”它是按程序顺序初始化的,所以它不是非法的。使用编译器内在函数会迫使 GCC 在这里不做任何聪明的事情。
    猜你喜欢
    • 1970-01-01
    • 2014-02-16
    • 2017-04-27
    • 1970-01-01
    • 1970-01-01
    • 2018-11-22
    • 2012-07-24
    • 1970-01-01
    相关资源
    最近更新 更多