Create TinyUnorderedMap class for small-N data storage.

Our code relies on std::unordered_map for storage of unordered data.
Unfortunately, while unordered_map is algorithmically quite efficient,
in many real-world scenarios--particularly with small amounts of data--
a simple vector with linear search runs rings around it.

This CL doubles the performance of `nanobench -m sksl_large`:
http://screen/7uGYGLCaTtaHU4j

Change-Id: Ia2f6cedfac338876c2da57642e9b34addd85b683
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/322320
Commit-Queue: John Stiles <johnstiles@google.com>
Commit-Queue: Brian Osman <brianosman@google.com>
Auto-Submit: John Stiles <johnstiles@google.com>
Reviewed-by: Brian Osman <brianosman@google.com>
This commit is contained in:
John Stiles 2020-10-05 09:56:42 -04:00 committed by Skia Commit-Bot
parent 750261f41a
commit b16b29a9fe
3 changed files with 69 additions and 1 deletions

View File

@ -45,6 +45,7 @@ skia_sksl_sources = [
"$_src/sksl/SkSLString.cpp",
"$_src/sksl/SkSLString.h",
"$_src/sksl/SkSLStringStream.h",
"$_src/sksl/SkSLTinyUnorderedMap.h",
"$_src/sksl/SkSLUtil.cpp",
"$_src/sksl/SkSLUtil.h",
"$_src/sksl/ir/SkSLBinaryExpression.h",

View File

@ -0,0 +1,66 @@
/*
* Copyright 2020 Google LLC
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#ifndef SKSL_TINYUNORDEREDMAP
#define SKSL_TINYUNORDEREDMAP
#include <algorithm>
#include <iterator>
#include <utility>
#include <vector>
namespace SkSL {
/** The TinyUnorderedMap presents a minimal, unordered_map-like interface, but is backed by a normal
vector. No attempt is made to be clever during lookups or insertions. This is intended to be
used in cases where we have tiny amount of data (e.g. less than 50 elements); CPUs are really
good at scanning contiguous cachelines. */
template <typename K, typename V>
class TinyUnorderedMap {
public:
using key_type = K;
using mapped_type = V;
using value_type = std::pair<const K, V>;
using iterator = typename std::vector<value_type>::iterator;
using const_iterator = typename std::vector<value_type>::const_iterator;
iterator begin() { return fData.begin(); }
const_iterator begin() const { return fData.begin(); }
iterator end() { return fData.end(); }
const_iterator end() const { return fData.end(); }
void clear() { fData.clear(); }
bool empty() { return fData.empty(); }
size_t size() { return fData.size(); }
void reserve(size_t amount) { fData.reserve(amount); }
iterator find(const K& key) {
return std::find_if(fData.begin(), fData.end(),
[&](const value_type& a) { return a.first == key; });
}
const_iterator find(const K& key) const {
return std::find_if(fData.begin(), fData.end(),
[&](const value_type& a) { return a.first == key; });
}
V& operator[](const K& key) {
auto iter = this->find(key);
if (iter != fData.end()) {
return iter->second;
}
fData.push_back(value_type{key, V{}});
return fData.back().second;
}
private:
std::vector<value_type> fData;
};
} // namespace SkSL
#endif

View File

@ -8,6 +8,7 @@
#ifndef SKSL_EXPRESSION
#define SKSL_EXPRESSION
#include "src/sksl/SkSLTinyUnorderedMap.h"
#include "src/sksl/ir/SkSLStatement.h"
#include "src/sksl/ir/SkSLType.h"
@ -19,7 +20,7 @@ struct Expression;
class IRGenerator;
struct Variable;
typedef std::unordered_map<const Variable*, std::unique_ptr<Expression>*> DefinitionMap;
using DefinitionMap = TinyUnorderedMap<const Variable*, std::unique_ptr<Expression>*>;
/**
* Abstract supertype of all expressions.