我需要格式化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。
不幸的是,它似乎无法格式化时间/日期的毫秒部分,并且需要从FILETIME
到std::time_t
的转换工作...
我应该忘记“自定义”格式字符串,而是为FILETIME
(或SYSTEMTIME
)类型提供自定义格式器吗?这样会大大提高性能吗?
感谢您提供的任何指导。
答案 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
零字节。
一如既往:尝试,衡量并获得乐趣!