通过分析我们已经发现,sprintf这里需要很长的时间。是否有更好的执行替代,仍然处理导致零y/m/d h/m/s领域?

SYSTEMTIME sysTime;
GetLocalTime( &sysTime );
char buf[80];
for (int i = 0; i < 100000; i++)
{

    sprintf(buf, "%4d-%02d-%02d %02d:%02d:%02d",
        sysTime.wYear, sysTime.wMonth, sysTime.wDay, 
        sysTime.wHour, sysTime.wMinute, sysTime.wSecond);

}

注:运算说明中的意见,这是一个精简的例子。"真正的"循环中包含额外的代码使用的变化的时间价值从一个数据库。分析已经确定了 sprintf() 作为罪犯。

有帮助吗?

解决方案

如果您正在编写自己的函数来完成这项工作,那么字符串值为0 .. 61的查找表将避免对除年份之外的所有内容进行任何算术运算。

编辑:请注意,要应对闰秒(并匹配 strftime () )您应该能够打印60和61的秒值。

char LeadingZeroIntegerValues[62][] = { "00", "01", "02", ... "59", "60", "61" };

或者, strftime() ?我不知道性能如何比较(它可能只是调用sprintf()),但它值得关注(它可能正在进行上述查找)。

其他提示

您可以尝试依次填写输出中的每个字符。

buf[0] = (sysTime.wYear / 1000) % 10 + '0' ;
buf[1] = (sysTime.wYear / 100) % 10 + '0';
buf[2] = (sysTime.wYear / 10) % 10 + '0';
buf[3] = sysTime.wYear % 10 + '0';
buf[4] = '-';

...等......

不漂亮,但你得到了照片。如果不出意外,它可能有助于解释为什么sprintf不会那么快。

OTOH,也许你可以缓存最后的结果。这样你只需要每秒生成一个。

Printf需要处理许多不同的格式。 您当然可以获取 printf的来源并将其用作滚动的基础您自己的版本,专门处理 sysTime 结构。这样你传递了一个参数,它确实完成了需要完成的工作,仅此而已。

“长”是什么意思?时间 - 因为 sprintf()是你的循环和“管道”中唯一的语句。循环(增量,比较)可以忽略不计, sprintf() 消耗最多的时间。

还记得有一天晚上在第三街失去结婚戒指的男人的老笑话,但是因为那里的灯光比较明亮,所以在5号找了它?你已经建立了一个旨在“证明”的例子。你假设 sprintf()是无效的。

如果您对“实际”内容进行了分析,您的结果会更准确。除了您使用的所有其他函数和算法之外,还包含 sprintf()的代码。或者,尝试编写您自己的版本,以解决您需要的特定零填充数字转换。

你可能对结果感到惊讶。

看起来Jaywalker建议采用一种非常相似的方法(不到一小时就打败了我)。

除了已经建议的查找表方法(下面的n2s []数组)之外,如何生成格式缓冲区以使通常的sprintf不那么密集?除非年/月/日/小时发生变化,否则下面的代码只需要每次循环填写分钟和秒。显然,如果其中任何一个已经改变,你确实会采取另一个sprintf命中,但总体而言,它可能不会超过你目前所见的(当与数组查找结合时)。


static char fbuf[80];
static SYSTEMTIME lastSysTime = {0, ..., 0};  // initialize to all zeros.

for (int i = 0; i < 100000; i++)
{
    if ((lastSysTime.wHour != sysTime.wHour)
    ||  (lastSysTime.wDay != sysTime.wDay)
    ||  (lastSysTime.wMonth != sysTime.wMonth)
    ||  (lastSysTime.wYear != sysTime.wYear))
    {
        sprintf(fbuf, "%4d-%02s-%02s %02s:%%02s:%%02s",
                sysTime.wYear, n2s[sysTime.wMonth],
                n2s[sysTime.wDay], n2s[sysTime.wHour]);

        lastSysTime.wHour = sysTime.wHour;
        lastSysTime.wDay = sysTime.wDay;
        lastSysTime.wMonth = sysTime.wMonth;
        lastSysTime.wYear = sysTime.wYear;
    }

    sprintf(buf, fbuf, n2s[sysTime.wMinute], n2s[sysTime.wSecond]);

}

如何缓存结果?这不是一种可能吗?考虑到你的代码中经常发生这种特殊的sprintf()调用,我假设在大多数连续调用之间,年,月和日不会改变。

因此,我们可以实现以下内容。声明旧的和当前的SYSTEMTIME结构:

SYSTEMTIME sysTime, oldSysTime;

另外,声明单独的部分以保存日期和时间:

char datePart[80];
char timePart[80];

首次,你必须同时填写sysTime,oldSysTime以及datePart和timePart。但是后续的sprintf()可以更快地完成,如下所示:

sprintf (timePart, "%02d:%02d:%02d", sysTime.wHour, sysTime.wMinute, sysTime.wSecond);
if (oldSysTime.wYear == sysTime.wYear && 
  oldSysTime.wMonth == sysTime.wMonth &&
  oldSysTime.wDay == sysTime.wDay) 
  {
     // we can reuse the date part
     strcpy (buff, datePart);
     strcat (buff, timePart);
  }
else {
     // we need to regenerate the date part as well
     sprintf (datePart, "%4d-%02d-%02d", sysTime.wYear, sysTime.wMonth, sysTime.wDay);
     strcpy (buff, datePart);
     strcat (buff, timePart);
}

memcpy (&oldSysTime, &sysTime, sizeof (SYSTEMTIME));

上面的代码有一些冗余,使代码更容易理解。你可以很容易地分解出来。如果您知道即使是小时和分钟的变化也不会比您对例行程序的调用更快,您可以进一步加快速度。

我会做一些事情...

  • 缓目前的时间所以你不必再生的每一次时间戳
  • 做的时间转换手动。最慢的一部分 printf-家庭功能是格式串分析,这是愚蠢的要投入的周期,分析在每一个环执行。
  • 尝试使用2-byte查表的所有转化({ "00", "01", "02", ..., "99" }).这是因为要避免moduluar算,2字节表意味着你只需要使用一个模,为一年。

你可能会通过手动滚动一个例程来增加返回buf中的数字,因为你可以避免重复解析格式字符串而不必处理sprintf处理的许多更复杂的情况。我不愿意实际建议这样做。

我建议你试着弄清楚你是否能以某种方式减少生成这些字符串所需的数量,它们是否可选,是否可以缓存等等。

我工作的一个类似问题的时刻。

我需要记录的调试声明有时间戳、文件、线路数量等于嵌入式系统。我们已经有一个记录仪的地方,但当我打开的旋钮到充分记录',它吃我们所有的进程周期,并把我们的系统在严峻的国家,国家没有计算设备以往任何时候都应得到的经验。

有人说"你无法衡量/观察到的东西,而不改变你的测/观察。"

所以我改变事情,以改善性能。当前状态的事情是,我是2倍的速度比原来 功能的电话 (瓶颈在于记录系统不在的功能的电话,但在该日志的读者,这是一个独立的可执行,我可以放弃的如果我写我自己的记录stack)。

该接口,我需要提供的东西喜欢 void log(int channel, char *filename, int lineno, format, ...).我需要追加的道名称(其目前不会 直线索 在名单上!对于每一个调试声明!) 和时间戳,其中包括毫秒计数器。这里有一些东西Im做到使这个速度更快

  • 转换成字符串通道名字所以我可以 strcpy 而不是搜索名单。定义的宏观 LOG(channel, ...etc) 作为 log(#channel, ...etc).你可以使用 memcpy 如果你解决的长度串通过定义 LOG(channel, ...) log("...."#channel - sizeof("...."#channel) + *11*) 获得固定的 10 字节的通道的长度
  • 产生时间戳串几次。你可以使用asctime或东西。然后memcpy定长串的每一个调试声明。
  • 如果你要生的时间戳串在真正的时间然后看看表与转让(不memcpy!) 是完美的。但是,这仅适用于2位数和可能为一年。
  • 什么大约三个数字(秒)和五位数(lineno)?我不喜欢投资和我不喜欢的定义投资(digit = ((value /= value) % 10))是因为div和多器官功能障碍 .我写的职能如下,后来发现,类似的东西是在AMD优化手册》(大会)这给了我信心,这些都是有关最快C的实现。

    void itoa03(char *string, unsigned int value)
    {
       *string++ = '0' + ((value = value * 2684355) >> 28);
       *string++ = '0' + ((value = ((value & 0x0FFFFFFF)) * 10) >> 28);
       *string++ = '0' + ((value = ((value & 0x0FFFFFFF)) * 10) >> 28);
       *string++ = ' ';/* null terminate here if thats what you need */
    }
    

    同样,对于在线号码,

    void itoa05(char *string, unsigned int value)
    {
       *string++ = ' ';
       *string++ = '0' + ((value = value * 26844 + 12) >> 28);
       *string++ = '0' + ((value = ((value & 0x0FFFFFFF)) * 10) >> 28);
       *string++ = '0' + ((value = ((value & 0x0FFFFFFF)) * 10) >> 28);
       *string++ = '0' + ((value = ((value & 0x0FFFFFFF)) * 10) >> 28);
       *string++ = '0' + ((value = ((value & 0x0FFFFFFF)) * 10) >> 28);
       *string++ = ' ';/* null terminate here if thats what you need */
    }
    

总体而言,我的代码是很快,现在。的 vsnprintf() 我需要使用需要大约91%的时间和休息的我的代码需要的只有9%(而其余部分即码除了 vsprintf() 用于采取54%前)

我测试的两种快速格式化程序是 FastFormat Karma :: generate 提升精神)。

您可能还会发现对其进行基准测试或至少查找现有基准测试非常有用。

例如这一个(虽然它缺少FastFormat):

StringStream是我从Google获得的建议。

http://bytes.com/forum/thread132583.html

很难想象你会在格式化整数时击败sprintf。你确定sprintf是你的问题吗?

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top