发挥{fmt}

时间:2019-07-18 23:20:58

标签: c++ fmt

我需要格式化FILETIME值信息,并且需要一个宽字符串缓冲区,并且配置提供了格式化字符串。

我实际上在做什么:

  • Config提供格式字符串:L"{YYYY}-{MM}-{DD} {hh}:{mm}:{ss}.{mmm}"

  • 将FILETIME转换为系统时间:

    SYSTEMTIME stUTC;
    FileTimeToSystemTime(&fileTime, &stUTC);
  • 使用
  • 设置字符串格式
    fmt::format_to(std::back_inserter(buffer), strFormat,        
             fmt::arg(L"YYYY", stUTC.wYear),
             fmt::arg(L"MM", stUTC.wMonth),
             fmt::arg(L"DD", stUTC.wDay),
             fmt::arg(L"hh", stUTC.wHour),
             fmt::arg(L"mm", stUTC.wMinute),
             fmt::arg(L"ss", stUTC.wSecond),
             fmt::arg(L"mmm", stUTC.wMilliseconds));

我完全理解使用服务会带来成本:),但是我的代码正在调用此语句数百万次,并且显然存在性能损失(CPU使用率超过6%)。

欢迎我为改进此代码所做的一切。

我看到{fmt}有一个time API support。 不幸的是,它似乎无法格式化时间/日期的毫秒部分,并且需要从FILETIMEstd::time_t的转换工作...

我应该忘记“自定义”格式字符串,而是为FILETIME(或SYSTEMTIME)类型提供自定义格式器吗?这样会大大提高性能吗?

感谢您提供的任何指导。

1 个答案:

答案 0 :(得分:0)

在评论中,我建议将您的自定义时间格式字符串解析为一个简单的状态机。这样,它甚至不必是状态机。这只是一系列线性说明。

当前,fmt类需要做一些工作来解析格式类型,然后将整数转换为零填充的字符串。尽管不太可能,但它有可能像我建议的那样经过优化。

基本思想是拥有一个(大)查找表,该查找表当然可以在运行时生成,但出于快速说明的目的:

const wchar_t zeroPad4[10000][5] = { L"0000", L"0001", L"0002", ..., L"9999" };

如果需要,您可以具有1位,2位和3位查找表,或者,如果仅添加偏移量,则可以识别出这些值都包含在4位查找表中。

因此,要输出数字,您只需要知道SYSTEMTIME中的偏移量,值的类型以及要应用的字符串偏移量即可(4位数字为0、3位数字为1,等等)。假设SYSTEMTIME中的所有struct元素都是相同的类型,这会使事情变得更简单。而且,您可以合理地假设没有值需要范围检查,尽管不确定时可以添加。

您可以这样配置它:

struct Output {
    int dataOffset;  // offset into SYSTEMTIME struct
    int count;       // extra adjustment after string lookup
};

文字字符串呢?好了,您可以复制这些字符,也可以只是重新使用Output来使用负数dataOffset来表示从格式字符串开始的位置,而count可以保留在该模式下要输出的字符数。如果需要其他输出模式,请使用mode成员来扩展此结构。

好,让我们以您的字符串L"{YYYY}-{MM}-{DD} {hh}:{mm}:{ss}.{mmm}"为例。解析完之后,您将得到:

Output outputs[] {
    { offsetof(SYSTEMTIME, wYear), 0 },         // "{YYYY}"
    { -6, 1 },                                  // "-"
    { offsetof(SYSTEMTIME, wMonth), 2 },        // "{MM}"
    { -11, 1 },                                 // "-"
    { offsetof(SYSTEMTIME, wDay), 2 },          // "{DD}"
    { -16, 1 },                                 // " "
    // etc...  you get the idea
    { offsetof(SYSTEMTIME, wMilliseconds), 1 }, // "{mmm}"
    { -1, 0 },                                  // terminate
};

当您输入SYSTEMTIME,指向原始格式字符串的指针,查找表以及此基本指令数组时,您可以继续进行并输出很快就可以放入预先设置的缓冲区。

我确定您可以拿出代码来有效执行这些指令。

此方法的主要缺点是查找表的大小可能会导致缓存问题。但是,大多数查找将发生在前100个元素中。您还可以将表压缩为普通的char值,然后在复制时注入wchar_t零字节。

一如既往:尝试,衡量并获得乐趣!