This change consists in avoiding filling the b58 buffer with zeros. The improvement is about 30% - 45%.
For example, calling listunspents
with my wallet results in 313ms in EncodeBase58
whereas before was 578ms.
This change consists in avoiding filling the b58 buffer with zeros. The improvement is about 30% - 45%.
For example, calling listunspents
with my wallet results in 313ms in EncodeBase58
whereas before was 578ms.
74 zeroes++;
75 }
76 // Allocate enough space in big-endian base58 representation.
77- std::vector<unsigned char> b58((pend - pbegin) * 138 / 100 + 1); // log(256) / log(58), rounded up.
78+ int size = (pend - pbegin) * 138 / 100 + 1;
79+ std::vector<unsigned char> b58(size); // log(256) / log(58), rounded up.
For example, calling listunspents with my wallet results in 313ms in EncodeBase58 whereas before was 578ms.
Interesting result. I was not aware anything was bottlenecked by base58 encoding. As you’ve demonstrated a concrete performance improvement: concept ACK
Untested ACK.
This should result in an asymptotic 2x speedup. I didn’t expect that it would matter anywhere, but as it seems it does, great.
88 *it = carry % 58;
89 carry /= 58;
90 }
91+
92 assert(carry == 0);
93+ length = i;
i
and length
?
It seems you could simply ++length;
here. I see your i < length
condition, but I don’t see how can it possibly be ever true.
++length
, it can be length += 0|1|2
, but there is possible to remove i
:
length = it - b58.begin()
81 while (pbegin != pend) {
82 int carry = *pbegin;
83+ int i = 0;
84 // Apply "b58 = b58 * 256 + ch".
85- for (std::vector<unsigned char>::reverse_iterator it = b58.rbegin(); it != b58.rend(); it++) {
86+ for (std::vector<unsigned char>::reverse_iterator it = b58.rbegin(); (carry != 0 || i < length) && (it != b58.rend()); it++, i++) {
++it
faster than it++
?
This should depend on the compiler and target machine anyway, but it is my understanding that in x86 there are separated instructions with different performance
For integers the compiler is certainly smart enough that there is no difference between prefix and postfix ++, if bare (the result is not actually used).
But this doesn’t have much to do with the instructions, just with language design. Iterators are objects. The definition of prefix and postfix ++
respectively is, when overloading them:
0Point& operator++(); // Prefix increment operator.
1Point operator++(int); // Postfix increment operator.
So: postfix operation returns a copy (the old value), whereas prefix increments in place and returns a reference (to self). This means prefix can, in principle, be implemented more efficiently.
.reserve
to digits
I think it can be even faster than it is now.
87 carry += 256 * (*it);
88 *it = carry % 58;
89 carry /= 58;
90 }
91+
92 assert(carry == 0);