QString::number(int): Avoid going through qlocale machinery
For increased performance, as measured with the number_qu?longlong QString benchmarks. The results are all good, reducing runtime by anywhere between 40 and 220 nanoseconds. The slowest test previously completing at ~330ns, now completing at ~105ns. Task-number: QTBUG-88484 Change-Id: Ie96e86e57b635bac01389aed531a6d9f087df983 Reviewed-by: Edward Welbourne <edward.welbourne@qt.io>bb10
parent
7ff72d0a0f
commit
5d0542a356
|
|
@ -460,6 +460,53 @@ qstrtoll(const char * nptr, const char **endptr, int base, bool *ok)
|
|||
return result;
|
||||
}
|
||||
|
||||
static Q_ALWAYS_INLINE void qulltoBasicLatin_helper(qulonglong number, int base, char16_t *&p)
|
||||
{
|
||||
// Performance-optimized code. Compiler can generate faster code when base is known.
|
||||
switch (base) {
|
||||
#define BIG_BASE_LOOP(b) \
|
||||
do { \
|
||||
const int r = number % b; \
|
||||
*--p = (r < 10 ? u'0' : u'a' - 10) + r; \
|
||||
number /= b; \
|
||||
} while (number)
|
||||
#ifndef __OPTIMIZE_SIZE__
|
||||
#define SMALL_BASE_LOOP(b) \
|
||||
do { \
|
||||
*--p = u'0' + number % b; \
|
||||
number /= b; \
|
||||
} while (number)
|
||||
|
||||
case 2: SMALL_BASE_LOOP(2); break;
|
||||
case 8: SMALL_BASE_LOOP(8); break;
|
||||
case 10: SMALL_BASE_LOOP(10); break;
|
||||
case 16: BIG_BASE_LOOP(16); break;
|
||||
#undef SMALL_BASE_LOOP
|
||||
#endif
|
||||
default: BIG_BASE_LOOP(base); break;
|
||||
#undef BIG_BASE_LOOP
|
||||
}
|
||||
}
|
||||
|
||||
// This is technically "qulonglong to ascii", but that name's taken
|
||||
QString qulltoBasicLatin(qulonglong number, int base, bool negative)
|
||||
{
|
||||
if (number == 0)
|
||||
return QStringLiteral("0");
|
||||
// Length of MIN_LLONG with the sign in front is 65; we never need surrogate pairs.
|
||||
// We do not need a terminator.
|
||||
const unsigned maxlen = 65;
|
||||
static_assert(CHAR_BIT * sizeof(number) + 1 <= maxlen);
|
||||
char16_t buff[maxlen];
|
||||
char16_t *const end = buff + maxlen, *p = end;
|
||||
|
||||
qulltoBasicLatin_helper(number, base, p);
|
||||
if (negative)
|
||||
*--p = u'-';
|
||||
|
||||
return QString(reinterpret_cast<QChar *>(p), end - p);
|
||||
}
|
||||
|
||||
QString qulltoa(qulonglong number, int base, const QStringView zero)
|
||||
{
|
||||
// Length of MAX_ULLONG in base 2 is 64; and we may need a surrogate pair
|
||||
|
|
@ -469,51 +516,8 @@ QString qulltoa(qulonglong number, int base, const QStringView zero)
|
|||
char16_t buff[maxlen];
|
||||
char16_t *const end = buff + maxlen, *p = end;
|
||||
|
||||
// Performance-optimized code. Compiler can generate faster code when base is known.
|
||||
if (base != 10 || zero == u"0") {
|
||||
switch (base) {
|
||||
#ifndef __OPTIMIZE_SIZE__
|
||||
case 10:
|
||||
while (number != 0) {
|
||||
const int c = number % 10;
|
||||
const qulonglong temp = number / 10;
|
||||
*--p = '0' + c;
|
||||
number = temp;
|
||||
}
|
||||
break;
|
||||
case 2:
|
||||
while (number != 0) {
|
||||
const int c = number % 2;
|
||||
const qulonglong temp = number / 2;
|
||||
*--p = '0' + c;
|
||||
number = temp;
|
||||
}
|
||||
break;
|
||||
case 8:
|
||||
while (number != 0) {
|
||||
const int c = number % 8;
|
||||
const qulonglong temp = number / 8;
|
||||
*--p = '0' + c;
|
||||
number = temp;
|
||||
}
|
||||
break;
|
||||
case 16:
|
||||
while (number != 0) {
|
||||
const int c = number % 16;
|
||||
const qulonglong temp = number / 16;
|
||||
*--p = c < 10 ? '0' + c : c - 10 + 'a';
|
||||
number = temp;
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
while (number != 0) {
|
||||
const int c = number % base;
|
||||
const qulonglong temp = number / base;
|
||||
*--p = c < 10 ? '0' + c : c - 10 + 'a';
|
||||
number = temp;
|
||||
}
|
||||
}
|
||||
qulltoBasicLatin_helper(number, base, p);
|
||||
} else if (zero.size() && !zero.at(0).isSurrogate()) {
|
||||
const char16_t zeroUcs2 = zero.at(0).unicode();
|
||||
while (number != 0) {
|
||||
|
|
|
|||
|
|
@ -68,6 +68,7 @@ double qt_asciiToDouble(const char *num, qsizetype numLen, bool &ok, int &proces
|
|||
void qt_doubleToAscii(double d, QLocaleData::DoubleForm form, int precision, char *buf, int bufSize,
|
||||
bool &sign, int &length, int &decpt);
|
||||
|
||||
QString qulltoBasicLatin(qulonglong l, int base, bool negative);
|
||||
QString qulltoa(qulonglong l, int base, const QStringView zero);
|
||||
Q_CORE_EXPORT QString qdtoa(qreal d, int *decpt, int *sign);
|
||||
|
||||
|
|
|
|||
|
|
@ -7307,7 +7307,16 @@ QString QString::number(qlonglong n, int base)
|
|||
base = 10;
|
||||
}
|
||||
#endif
|
||||
return QLocaleData::c()->longLongToString(n, -1, base);
|
||||
QT_WARNING_PUSH
|
||||
/* "unary minus operator applied to unsigned type, result still unsigned" */
|
||||
QT_WARNING_DISABLE_MSVC(4146)
|
||||
bool negative = n < 0;
|
||||
/*
|
||||
Negating std::numeric_limits<qlonglong>::min() hits undefined behavior, so
|
||||
taking an absolute value has to cast to unsigned to change sign.
|
||||
*/
|
||||
return qulltoBasicLatin(negative ? -qulonglong(n) : qulonglong(n), base, negative);
|
||||
QT_WARNING_POP
|
||||
}
|
||||
|
||||
/*!
|
||||
|
|
@ -7321,7 +7330,7 @@ QString QString::number(qulonglong n, int base)
|
|||
base = 10;
|
||||
}
|
||||
#endif
|
||||
return QLocaleData::c()->unsLongLongToString(n, -1, base);
|
||||
return qulltoBasicLatin(n, base, false);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue