【问题标题】:Efficiently convert an unsigned short to a char*有效地将 unsigned short 转换为 char*
【发布时间】:2011-06-11 09:10:50
【问题描述】:

将 unsigned short 转换为 char*(即将 25 转换为 '25')是一种高效、可移植的方法。

我想避免诸如获取 (std::string) 字符串之类的事情。在这种情况下,性能很重要,因为这种转换需要快速且频繁地进行。

我正在研究诸如使用 sprintf 之类的东西,但想探索任何和所有想法。

【问题讨论】:

  • 您是否尝试过使用sprintf/snprintf?完成后,您是否分析了代码并确定这是一个性能热点?
  • 下面链接的文章底部的表格清楚地说明了 stdlib 实现在效率和实现优化方面的位置:codeproject.com/KB/recipes/Tokenizer.aspx

标签: c++ char unsigned short


【解决方案1】:

试试这个:

int convert(unsigned short val, char* dest)
{
  int i = 0;
  if (val > 10000)
  {
    dest[i++] = (val / 10000) | 0x30;
    val %= 10000;
  }
  if (val > 1000)
  {
    dest[i++] = (val / 1000) | 0x30;
    val %= 1000;
  }
  if (val > 100)
  {
    dest[i++] = (val / 100) | 0x30;
    val %= 100;
  }
  if (val > 10)
  {
    dest[i++] = (val / 10) | 0x30;
    val %= 10;
  }
  dest[i++] = (val) | 0x30;
  dest[i] = 0;
  return i;
}

【讨论】:

  • 也许是最终的dest[i] = '\0'; ?
【解决方案2】:

我在这里对各种功能进行了测试,这就是我想出的:

write_ushort:7.81 秒
uShortToStr:8.16 秒
转换:6.71 秒
use_sprintf:49.66 秒

(Write_ushort 是我的版本,我试图尽可能清楚地写出来,而不是微优化,以格式化为给定的字符缓冲区;use_sprintf 是显而易见的 sprintf(buf, "%d", x) 什么都没有否则;其他两个取自此处的其他答案。)

这是他们之间非常惊人的区别,不是吗?谁会想到使用 sprintf 面临几乎一个数量级的差异?哦,是的,我对每个测试的函数迭代了多少次?

// Taken directly from my hacked up test, but should be clear.
// Compiled with gcc 4.4.3 and -O2.  This test is interesting, but not authoritative.
int main() {
  using namespace std;
  char buf[100];

#define G2(NAME,STMT) \
  { \
    clock_t begin = clock(); \
    for (int count = 0; count < 3000; ++count) { \
      for (unsigned x = 0; x <= USHRT_MAX; ++x) { \
        NAME(x, buf, sizeof buf); \
      } \
    } \
    clock_t end = clock(); \
    STMT \
  }
#define G(NAME) G2(NAME,) G2(NAME,cout << #NAME ": " << double(end - begin) / CLOCKS_PER_SEC << " s\n";)
  G(write_ushort)
  G(uShortToStr)
  G(convert)
  G(use_sprintf)
#undef G
#undef G2

  return 0;
}

Sprintf 转换了整个可能范围的未签名短裤,然后在我大约 5 年使用的笔记本电脑上以平均每次转换大约 0.25 µs 的速度再次完成整个范围 2,999 次

Sprintf 是可移植的;它是否也足以满足您的要求?


我的版本:

// Returns number of non-null bytes written, or would be written.
// If ret is null, does not write anything; otherwise retlen is the length of
// ret, and must include space for the number plus a terminating null.
int write_ushort(unsigned short x, char *ret, int retlen) {
  assert(!ret || retlen >= 1);

  char s[uint_width_10<USHRT_MAX>::value];  // easy implementation agnosticism
  char *n = s;
  if (x == 0) {
    *n++ = '0';
  }
  else while (x != 0) {
    *n++ = '0' + x % 10;
    x /= 10;
  }

  int const digits = n - s;
  if (ret) {
    // not needed by checking retlen and only writing to available space
    //assert(retlen >= digits + 1);

    while (--retlen && n != s) {
      *ret++ = *--n;
    }
    *ret = '\0';
  }
  return digits;
}

编译时日志 TMP 函数并不是什么新鲜事,但包括这个完整的示例,因为它是我使用的:

template<unsigned N>
struct uint_width_10_nonzero {
  enum { value = uint_width_10_nonzero<N/10>::value + 1 };
};
template<>
struct uint_width_10_nonzero<0> {
  enum { value = 0 };
};
template<unsigned N>
struct uint_width_10 {
  enum { value = uint_width_10_nonzero<N>::value };
};
template<>
struct uint_width_10<0> {
  enum { value = 1 };
};

【讨论】:

  • 其实差别并没有那么惊人,sprintf 有各种各样的格式说明符......它必须在其算法中考虑到可能存在的格式说明符。我从来没有见过 sprintf 甚至像 boost::format 这样的速度较慢的野兽在配置文件中占用了相当大比例的运行时间,因此在大多数情况下它不值得优化。
  • 一个小天使告诉我不要在这篇文章中使用讽刺,有人会对此发表评论。我没听。因为函数执行了近 2 亿次(USHRT_MAX,这里是 65,535,* 3,000),所以看起来差别很大。 @ÖöTiib:您重申了我在没有使用分析器的情况下展示了 OP 的结论,因为我无权访问 OP 的代码来分析它。
  • 是的,但我也提供了解决方案,因为......谁知道呢。如果你没有看到真正的问题,就不能讽刺。出于某种原因,基于文本的接口和协议变得流行,如果两个模块相互通信,大量使用 sprintf 来告诉对方数百万个未签名的短裤,那么它可能会成为瓶颈。在这种情况下,我当然更愿意切换到二进制接口/协议,但由于其他一些原因,重新考虑接口可能会很昂贵/不可能。
【解决方案3】:

这样的字符串数组

array[25] = "25";
array[26] = "26";

array[255] = "255";

也许?你可以编写一个小程序,很容易地为你生成表格源代码,然后在你的项目中使用这个文件。

编辑:我不明白你不想涉及字符串的意思。

【讨论】:

  • 我认为字符串的意思是“std::string”。我也很困惑。
【解决方案4】:

首先,做对了,然后做的快——只有当你可以确定一段代码没有性能时才进行优化。

snprintf() 进入缓冲区会做你想做的事。这是最快的解决方案吗?一点也不。但它是最简单的,它足以让您的代码进入工作状态。从那里开始,如果您发现对 snprintf() 的调用非常费力以至于需要对其进行优化,只有在那时才能寻求更快的解决方案。

【讨论】:

    【解决方案5】:

    我会说至少尝试 sprintf,因为您已将其标记为 C++,请尝试 StringStream,并实际分析它们。在许多情况下,编译器足够聪明,可以构建出运行良好的东西。只有当您知道这将成为瓶颈时,您才需要真正找到更快的方法。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2015-01-20
      • 1970-01-01
      • 1970-01-01
      • 2012-12-22
      • 2019-04-16
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多