From ebdbd8a231501903831f8d1602be542ff7a717b8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Steinbrink?= Date: Mon, 24 Oct 2016 11:28:33 +0200 Subject: [PATCH 1/3] Avoid unnecessary reallocations of the "linebuf" string safeGetline() already clears the string buffer before writing to it, so the same buffer can be used multiple times, but the loops calling safeGetline() have the string scoped within the loop, so its constructed and destructed in each loop iteration, causing lots of unnecessary allocations. Parse times for some large .obj files (without asan): File A File B File C Before 2743ms 589ms 615ms After 2500ms 573ms 545ms --- tiny_obj_loader.h | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/tiny_obj_loader.h b/tiny_obj_loader.h index 2276adc..b39cb89 100644 --- a/tiny_obj_loader.h +++ b/tiny_obj_loader.h @@ -692,9 +692,8 @@ void LoadMtl(std::map *material_map, material_t material; InitMaterial(&material); + std::string linebuf; while (inStream->peek() != -1) { - std::string linebuf; - safeGetline(*inStream, linebuf); // Trim trailing whitespace. @@ -1090,8 +1089,8 @@ bool LoadObj(attrib_t *attrib, std::vector *shapes, shape_t shape; + std::string linebuf; while (inStream->peek() != -1) { - std::string linebuf; safeGetline(*inStream, linebuf); // Trim newline '\r\n' or '\n' From aa670fe91eee32072b360d802a6d6db86bc4eb0c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Steinbrink?= Date: Mon, 24 Oct 2016 11:53:00 +0200 Subject: [PATCH 2/3] Use a lookup table to speed up float parsing The pow() function is pretty expensive, so creating a small lookup table for the first few negative powers of ten provides a big speedup. Parse times for some large .obj files (without asan): File A File B File C Before 2500ms 573ms 545ms After 1239ms 294ms 271ms --- tiny_obj_loader.h | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/tiny_obj_loader.h b/tiny_obj_loader.h index b39cb89..f043628 100644 --- a/tiny_obj_loader.h +++ b/tiny_obj_loader.h @@ -418,8 +418,21 @@ static bool tryParseDouble(const char *s, const char *s_end, double *result) { read = 1; end_not_reached = (curr != s_end); while (end_not_reached && IS_DIGIT(*curr)) { + static const double pow_lut[] = { + 1.0, + 0.1, + 0.01, + 0.001, + 0.0001, + 0.00001, + 0.000001, + 0.0000001, + }; + const int lut_entries = sizeof pow_lut / sizeof pow_lut[0]; + // NOTE: Don't use powf here, it will absolutely murder precision. - mantissa += static_cast(*curr - 0x30) * pow(10.0, -read); + mantissa += static_cast(*curr - 0x30) * + (read < lut_entries ? pow_lut[read] : pow(10.0, -read)); read++; curr++; end_not_reached = (curr != s_end); From d6eeb14216f77cda6d089f737b2b21dcb30e326c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Steinbrink?= Date: Mon, 24 Oct 2016 14:45:40 +0200 Subject: [PATCH 3/3] Avoid unnecessary ldexp() and pow() calls Parse times for some large .obj files (without asan): File A File B File C Before 1239ms 294ms 271ms After 1037ms 203ms 190ms --- tiny_obj_loader.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tiny_obj_loader.h b/tiny_obj_loader.h index f043628..b62321b 100644 --- a/tiny_obj_loader.h +++ b/tiny_obj_loader.h @@ -472,8 +472,8 @@ static bool tryParseDouble(const char *s, const char *s_end, double *result) { } assemble: - *result = - (sign == '+' ? 1 : -1) * ldexp(mantissa * pow(5.0, exponent), exponent); + *result = (sign == '+' ? 1 : -1) * + (exponent ? ldexp(mantissa * pow(5.0, exponent), exponent) : mantissa); return true; fail: return false;