Avoid String::printf on a hot path.

Generating unique names is expensive! The inliner does it a lot. There
are two pain points:
- Assemble a candidate name with String::printf
- Use SymbolTable::lookup to make sure that name isn't taken already

Previously, these steps took roughly the same amount of time on my
machine. Afterwards, SymbolTable::lookup takes the vast majority of the
time spent in uniqueName. (Further optimizing symbol lookup is a Hard
Problem--trying to design something better than the current
implementation in the general case is not easy.)

This change gives nanobench a 1-3% win on my Macbook Pro.

median	mean
192µs	196µs	sksl_medium (before)
185µs	189µs	sksl_medium (after)

414µs	424µs	sksl_large  (before)
406µs	413µs	sksl_large  (after)

Change-Id: I8e0f7f053d124827a4d50a5006129e541a20c70d
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/534520
Commit-Queue: John Stiles <johnstiles@google.com>
Reviewed-by: Brian Osman <brianosman@google.com>
Auto-Submit: John Stiles <johnstiles@google.com>
This commit is contained in:
John Stiles 2022-04-28 10:25:43 -04:00 committed by SkCQ
parent 240c55f97b
commit c4eedcf76d
3 changed files with 23 additions and 10 deletions

View File

@ -641,8 +641,8 @@ generated_cc_atom(
visibility = ["//:__subpackages__"],
deps = [
":SkSLMangler_hdr",
"//include/core:SkString_hdr",
"//include/core:SkTypes_hdr",
"//include/private:SkSLString_hdr",
"//include/private:SkStringView_hdr",
"//src/sksl/ir:SkSLSymbolTable_hdr",
],

View File

@ -7,11 +7,12 @@
#include "src/sksl/SkSLMangler.h"
#include "include/core/SkString.h"
#include "include/core/SkTypes.h"
#include "include/private/SkSLString.h"
#include "include/private/SkStringView.h"
#include "src/sksl/ir/SkSLSymbolTable.h"
#include <algorithm>
#include <ctype.h>
namespace SkSL {
@ -47,16 +48,28 @@ std::string Mangler::uniqueName(std::string_view baseName, SymbolTable* symbolTa
// Append a unique numeric prefix to avoid name overlap. Check the symbol table to make sure
// we're not reusing an existing name. (Note that within a single compilation pass, this check
// isn't fully comprehensive, as code isn't always generated in top-to-bottom order.)
std::string uniqueName;
// This code is a performance hotspot. Assemble the string manually to save a few cycles.
char uniqueName[256];
uniqueName[0] = '_';
char* uniqueNameEnd = uniqueName + SK_ARRAY_COUNT(uniqueName);
for (;;) {
uniqueName = SkSL::String::printf("_%d_%.*s", fCounter++,
(int)baseName.size(), baseName.data());
if ((*symbolTable)[uniqueName] == nullptr) {
break;
// _123
char* endPtr = SkStrAppendS32(uniqueName + 1, fCounter++);
// _123_
*endPtr++ = '_';
// _123_baseNameTruncatedToFit (no null terminator, because string_view doesn't require one)
int baseNameCopyLength = std::min<int>(baseName.size(), uniqueNameEnd - endPtr);
memcpy(endPtr, baseName.data(), baseNameCopyLength);
endPtr += baseNameCopyLength;
std::string_view uniqueNameView(uniqueName, endPtr - uniqueName);
if ((*symbolTable)[uniqueNameView] == nullptr) {
return std::string(uniqueNameView);
}
}
return uniqueName;
}
} // namespace SkSL

View File

@ -140,7 +140,7 @@ public:
private:
struct SymbolKey {
std::string_view fName;
uint32_t fHash;
uint32_t fHash;
bool operator==(const SymbolKey& that) const { return fName == that.fName; }
bool operator!=(const SymbolKey& that) const { return fName != that.fName; }