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>
diff --git a/gn/sksl.gni b/gn/sksl.gni
index c1ba0c2..4f21e7c 100644
--- a/gn/sksl.gni
+++ b/gn/sksl.gni
@@ -45,6 +45,7 @@
   "$_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",
diff --git a/src/sksl/SkSLTinyUnorderedMap.h b/src/sksl/SkSLTinyUnorderedMap.h
new file mode 100644
index 0000000..7bfc738
--- /dev/null
+++ b/src/sksl/SkSLTinyUnorderedMap.h
@@ -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
diff --git a/src/sksl/ir/SkSLExpression.h b/src/sksl/ir/SkSLExpression.h
index c425180..7921980 100644
--- a/src/sksl/ir/SkSLExpression.h
+++ b/src/sksl/ir/SkSLExpression.h
@@ -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 @@
 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.