Эффективно конвертировать неподписанный шорт в символ * - PullRequest
6 голосов
/ 19 января 2011

Какой эффективный, переносимый способ преобразовать беззнаковое короткое в символ * (т.е. преобразовать 25 в '25').

Я бы хотел избежать таких вещей, как получение (std :: string) строк. В этом случае важна производительность, так как это преобразование должно произойти быстро и часто.

Я изучал такие вещи, как использование sprintf, но хотел бы изучить любые идеи.

Ответы [ 5 ]

6 голосов
/ 19 января 2011

Прежде всего, сделайте это правильно, затем сделайте это быстро - оптимизируйте, только если вы точно знаете, что фрагмент кода не работает.

snprintf() в буфер будет делать то, что вы хотите. Это самое быстрое решение? Не за что. Но это один из самых простых, и этого будет достаточно, чтобы привести ваш код в рабочее состояние. Оттуда, если вы видите, что эти вызовы snprintf() настолько трудоемки, что их нужно оптимизировать, тогда и только тогда ищет более быстрое решение.

2 голосов
/ 19 января 2011

Массив строк такой, что

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

array[255] = "255";

может быть?Вы могли бы написать небольшую программу, которая довольно легко генерирует для вас исходный код таблицы, а затем использовать этот файл в своем проекте.

Редактировать: я не понимаю, что вы имеете в виду, не желаязадействованные строки.

1 голос
/ 19 января 2011

Я собрал здесь тест различных функций, и вот что я придумал:

write_ushort: 7,81 с
uShortToStr: 8,16 с
преобразование: 6,71 с
use_sprintf: 49,66 с

(Write_ushort - моя версия, которую я попытался написать как можно более четко, а не микрооптимизировать, для форматирования в заданный символьный буфер; use_sprintf - это очевидный sprintf (buf, "% d).", х) и ничего больше; два других взяты из других ответов здесь.)

Это довольно удивительная разница между ними, не так ли?Кто бы мог подумать, что использовать 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 преобразовывал весь возможный диапазон беззнаковых шорт , а затем снова делал весь диапазон еще 2999 раз примерно0,25 мкс на конверсию в среднем на моем ~ 5-летнем ноутбуке.

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 };
};
1 голос
/ 19 января 2011

попробуйте это:

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;
}
1 голос
/ 19 января 2011

Я бы сказал, по крайней мере, попробуйте sprintf, и, поскольку вы пометили это как C ++, попробуйте StringStream , и фактически профилируйте их. Во многих случаях компилятор достаточно умен, чтобы создать что-то, что работает довольно хорошо. Только когда вы знаете, что это станет узким местом, вам действительно нужно найти более быстрый путь.

...