Fast string to integer#
문제#
보통의 경우 10진수 또는 16진수의 string을 integer로 변환하기 위하여 strtoul( ) 를 사용합니다.
하지만, strtoul( )를 매우 많이 반복하는 경우 처리 속도가 느립니다.
다음 코드는 strtoul( )을 300,000,000회 반복하였으며, 약 5초가 소요되었습니다.
#include <iostream>
#include <chrono>
int main()
{
const char* hexString = "19AF"; // 0x19AF = 6575
auto start = std::chrono::high_resolution_clock::now();
for (int i = 0; i < 300000000; i++)
{
int value = strtoul(hexString, NULL, 16);
}
auto end = std::chrono::high_resolution_clock::now();
std::chrono::duration<double, std::milli> elapsed = end - start;
std::cout << "time: " << elapsed.count() << " ms" << std::endl;
return 0;
}개선#
LUT(LookUp Table)를 사용하면 strtoul( ) 보다 빠르게 처리할 수 있습니다.
10진수, 16진수의 string에 포함되는 문자는 다음과 같습니다.
- ‘0’~‘9’
- ‘a’~‘f’
- ‘A’~‘F’
따라서, 이에 대한 integer값을 가지는 LUT를 다음 코드의 initLut( ) 안에 미리 만들어 놓습니다.
그리고 각 자릿수에 LUT 적용하여 문자에 해당하는 정수값을 확보하고, Shift 연산자를 사용하여 string을 integer로 변환하면 속도가 빨라집니다.
다음 코드는 약 0.7초가 소요 되었습니다.
strtoul( )
#include <iostream>
#include <chrono>
char LUT['f'+1]; // 'f'=102
void initLut()
{
LUT['0'] = 0x0;
LUT['1'] = 0x1;
LUT['2'] = 0x2;
LUT['3'] = 0x3;
LUT['4'] = 0x4;
LUT['5'] = 0x5;
LUT['6'] = 0x6;
LUT['7'] = 0x7;
LUT['8'] = 0x8;
LUT['9'] = 0x9;
LUT['a'] = 0xa;
LUT['b'] = 0xb;
LUT['c'] = 0xc;
LUT['d'] = 0xd;
LUT['e'] = 0xe;
LUT['f'] = 0xf;
LUT['A'] = 0xa;
LUT['B'] = 0xb;
LUT['C'] = 0xc;
LUT['D'] = 0xd;
LUT['E'] = 0xe;
LUT['F'] = 0xf;
}
int main()
{
initLut();
const char* hexString = "19AF"; // 0x19AF = 6575
auto start = std::chrono::high_resolution_clock::now();
for (int i = 0; i < 300000000; i++)
{
int value =
(LUT[hexString[0]] << 12) |
(LUT[hexString[1]] << 8) |
(LUT[hexString[2]] << 4) |
LUT[hexString[3]];
}
auto end = std::chrono::high_resolution_clock::now();
std::chrono::duration<double, std::milli> elapsed = end - start;
std::cout << "time: " << elapsed.count() << " ms" << std::endl;
return 0;
}strtoul( )은 5초, LUT를 사용하는 경우 0.7초이므로, 약 1/7수준으로 처리 시간이 단축 되었습니다.