Add SystemBenchmarks.

This benchmarks `sw::LRUCache`, which currently resides in the `src/Device` folder (but will be moved to `src/System`) with 43489.

Bug: b/153338950
Change-Id: I169b6ecb4a35349726a01b1da52472eaef3dfd74
Reviewed-on: https://swiftshader-review.googlesource.com/c/SwiftShader/+/43492
Reviewed-by: Alexis Hétu <sugoi@google.com>
Reviewed-by: Antonio Maiorano <amaiorano@google.com>
Tested-by: Ben Clayton <bclayton@google.com>
Kokoro-Result: kokoro <noreply+kokoro@google.com>
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 53f7921..dba0006 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -1209,18 +1209,44 @@
         add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/third_party/benchmark)
     endif()
 
+    # Reactor benchmarks
     set(REACTOR_BENCHMARK_LIST
         ${SOURCE_DIR}/Reactor/ReactorBenchmarks.cpp
     )
 
     add_executable(ReactorBenchmarks ${REACTOR_BENCHMARK_LIST})
     target_link_libraries(ReactorBenchmarks benchmark::benchmark marl ${Reactor})
-
     set_target_properties(ReactorBenchmarks PROPERTIES
         COMPILE_OPTIONS "${SWIFTSHADER_COMPILE_OPTIONS};${WARNINGS_AS_ERRORS}"
         LINK_FLAGS "${SWIFTSHADER_LINK_FLAGS}"
         FOLDER "Benchmarks"
     )
+
+    # System benchmarks
+    set(SYSTEM_BENCHMARKS_LIST
+        ${SOURCE_DIR}/System/Debug.cpp
+
+        ${TESTS_DIR}/SystemBenchmarks/main.cpp
+        ${TESTS_DIR}/SystemBenchmarks/LRUCacheBenchmarks.cpp
+    )
+
+    set(SYSTEM_BENCHMARKS_INCLUDE_DIR
+        ${CMAKE_CURRENT_SOURCE_DIR}/src/
+    )
+
+    add_executable(system-benchmarks ${SYSTEM_BENCHMARKS_LIST})
+    target_link_libraries(system-benchmarks benchmark::benchmark)
+    set_target_properties(system-benchmarks PROPERTIES
+        INCLUDE_DIRECTORIES "${SYSTEM_BENCHMARKS_INCLUDE_DIR}"
+        COMPILE_OPTIONS "${SWIFTSHADER_COMPILE_OPTIONS};${WARNINGS_AS_ERRORS}"
+        LINK_FLAGS "${SWIFTSHADER_LINK_FLAGS}"
+        FOLDER "Benchmarks"
+    )
+
+    target_link_libraries(system-benchmarks gtest gmock)
+    if(NOT WIN32)
+        target_link_libraries(system-benchmarks pthread ${SWIFTSHADER_LIBS})
+    endif()
 endif(SWIFTSHADER_BUILD_BENCHMARKS)
 
 if(SWIFTSHADER_BUILD_TESTS AND SWIFTSHADER_BUILD_VULKAN)
diff --git a/tests/SystemBenchmarks/LRUCacheBenchmarks.cpp b/tests/SystemBenchmarks/LRUCacheBenchmarks.cpp
new file mode 100644
index 0000000..66638d5
--- /dev/null
+++ b/tests/SystemBenchmarks/LRUCacheBenchmarks.cpp
@@ -0,0 +1,210 @@
+// Copyright 2020 The SwiftShader Authors. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//    http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "Device/LRUCache.hpp"
+
+#include "benchmark/benchmark.h"
+
+#include <array>
+
+namespace {
+
+// https://en.wikipedia.org/wiki/Xorshift
+class FastRnd
+{
+public:
+	inline size_t operator()()
+	{
+		x ^= x << 13;
+		x ^= x >> 7;
+		x ^= x << 17;
+		return x;
+	}
+
+private:
+	size_t x = 3243298;
+};
+
+struct ComplexKey
+{
+	std::array<size_t, 8> words;
+};
+
+bool operator==(const ComplexKey &a, const ComplexKey &b)
+{
+	for(size_t w = 0; w < a.words.size(); w++)
+	{
+		if(a.words[w] != b.words[w]) { return false; }
+	}
+	return true;
+}
+
+struct ComplexKeyHash
+{
+	size_t operator()(const ComplexKey &key) const
+	{
+		size_t hash = 12227;
+		for(size_t w = 0; w < key.words.size(); w++)
+		{
+			hash = hash * 11801 + key.words[w];
+		}
+		return hash;
+	}
+};
+
+}  // namespace
+
+class LRUCacheBenchmark : public benchmark::Fixture
+{
+public:
+	void SetUp(const ::benchmark::State &state)
+	{
+		size = state.range(0);
+	}
+
+	void TearDown(const ::benchmark::State &state) {}
+
+	size_t size;
+};
+
+BENCHMARK_DEFINE_F(LRUCacheBenchmark, AddInt)
+(benchmark::State &state)
+{
+	sw::LRUCache<size_t, size_t> cache(size);
+	FastRnd rnd;
+
+	int i = 0;
+	for(auto _ : state)
+	{
+		cache.add(rnd() % size, i);
+		i++;
+	}
+}
+BENCHMARK_REGISTER_F(LRUCacheBenchmark, AddInt)->RangeMultiplier(8)->Range(1, 0x100000)->ArgName("cache-size");
+
+BENCHMARK_DEFINE_F(LRUCacheBenchmark, GetIntCacheHit)
+(benchmark::State &state)
+{
+	sw::LRUCache<size_t, size_t> cache(size);
+	FastRnd rnd;
+
+	for(size_t i = 0; i < size; i++)
+	{
+		cache.add(i, i);
+	}
+
+	for(auto _ : state)
+	{
+		cache.query(rnd() % size);
+	}
+}
+BENCHMARK_REGISTER_F(LRUCacheBenchmark, GetIntCacheHit)->RangeMultiplier(8)->Range(1, 0x100000)->ArgName("cache-size");
+
+BENCHMARK_DEFINE_F(LRUCacheBenchmark, GetIntCacheMiss)
+(benchmark::State &state)
+{
+	sw::LRUCache<size_t, size_t> cache(size);
+	FastRnd rnd;
+	for(size_t i = 0; i < size; i++)
+	{
+		cache.add(size + i, i);
+	}
+
+	for(auto _ : state)
+	{
+		cache.query(rnd() % size);
+	}
+}
+BENCHMARK_REGISTER_F(LRUCacheBenchmark, GetIntCacheMiss)->RangeMultiplier(8)->Range(1, 0x100000)->ArgName("cache-size");
+
+BENCHMARK_DEFINE_F(LRUCacheBenchmark, AddRandomComplexKey)
+(benchmark::State &state)
+{
+	sw::LRUCache<ComplexKey, size_t> cache(size);
+	FastRnd rnd;
+
+	int i = 0;
+	for(auto _ : state)
+	{
+		ComplexKey key;
+		for(size_t w = 0; w < key.words.size(); w++)
+		{
+			key.words[w] = rnd() & 1;
+		}
+
+		cache.add(key, i);
+		i++;
+	}
+}
+BENCHMARK_REGISTER_F(LRUCacheBenchmark, AddRandomComplexKey)->RangeMultiplier(8)->Range(1, 0x100000)->ArgName("cache-size");
+
+BENCHMARK_DEFINE_F(LRUCacheBenchmark, GetComplexKeyCacheHit)
+(benchmark::State &state)
+{
+	sw::LRUCache<ComplexKey, size_t> cache(size);
+	FastRnd rnd;
+
+	for(size_t i = 0; i < size; i++)
+	{
+		ComplexKey key;
+		for(size_t w = 0; w < key.words.size(); w++)
+		{
+			key.words[w] = (1U << w);
+		}
+		cache.add(key, i);
+	}
+
+	for(auto _ : state)
+	{
+		auto i = rnd() & size;
+
+		ComplexKey key;
+		for(size_t w = 0; w < key.words.size(); w++)
+		{
+			key.words[w] = i & (1U << w);
+		}
+		cache.query(key);
+	}
+}
+BENCHMARK_REGISTER_F(LRUCacheBenchmark, GetComplexKeyCacheHit)->RangeMultiplier(8)->Range(1, 0x100000)->ArgName("cache-size");
+
+BENCHMARK_DEFINE_F(LRUCacheBenchmark, GetComplexKeyCacheMiss)
+(benchmark::State &state)
+{
+	sw::LRUCache<ComplexKey, size_t> cache(size);
+	FastRnd rnd;
+
+	for(size_t i = 0; i < size; i++)
+	{
+		ComplexKey key;
+		for(size_t w = 0; w < key.words.size(); w++)
+		{
+			key.words[w] = 8 + (1U << w);
+		}
+		cache.add(key, i);
+	}
+
+	for(auto _ : state)
+	{
+		auto i = rnd() & size;
+
+		ComplexKey key;
+		for(size_t w = 0; w < key.words.size(); w++)
+		{
+			key.words[w] = i & (1U << w);
+		}
+		cache.query(key);
+	}
+}
+BENCHMARK_REGISTER_F(LRUCacheBenchmark, GetComplexKeyCacheMiss)->RangeMultiplier(8)->Range(1, 0x100000)->ArgName("cache-size");
diff --git a/tests/SystemBenchmarks/main.cpp b/tests/SystemBenchmarks/main.cpp
new file mode 100644
index 0000000..9f307ab
--- /dev/null
+++ b/tests/SystemBenchmarks/main.cpp
@@ -0,0 +1,17 @@
+// Copyright 2020 The SwiftShader Authors. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//    http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "benchmark/benchmark.h"
+
+BENCHMARK_MAIN();
diff --git a/tests/SystemUnitTests/LRUCacheTests.cpp b/tests/SystemUnitTests/LRUCacheTests.cpp
new file mode 100644
index 0000000..c1dead5
--- /dev/null
+++ b/tests/SystemUnitTests/LRUCacheTests.cpp
@@ -0,0 +1,124 @@
+// Copyright 2020 The SwiftShader Authors. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//    http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "System/LRUCache.hpp"
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include <vector>
+
+using namespace sw;
+
+namespace {
+
+template<typename Cache>
+void checkRange(const Cache &cache, std::vector<std::pair<typename Cache::Key, typename Cache::Data>> list)
+{
+	size_t i = 0;
+	for(auto it : cache)
+	{
+		ASSERT_EQ(list[i].first, it.key());
+		ASSERT_EQ(list[i].second, it.data());
+		i++;
+	}
+	ASSERT_EQ(i, list.size());
+}
+
+}  // namespace
+
+////////////////////////////////////////////////////////////////////////////////
+// LRUCache
+////////////////////////////////////////////////////////////////////////////////
+TEST(LRUCache, Empty)
+{
+	LRUCache<std::string, std::string> cache(8);
+	ASSERT_EQ(cache.get(""), "");
+	ASSERT_EQ(cache.get("123"), "");
+	for(auto ignored : cache)
+	{
+		(void)ignored;
+		FAIL() << "Should not loop on empty cache";
+	}
+}
+
+TEST(LRUCache, AddNoEviction)
+{
+	LRUCache<std::string, std::string> cache(4);
+
+	cache.add("1", "one");
+	cache.add("2", "two");
+	cache.add("3", "three");
+	cache.add("4", "four");
+
+	ASSERT_EQ(cache.get("1"), "one");
+	ASSERT_EQ(cache.get("2"), "two");
+	ASSERT_EQ(cache.get("3"), "three");
+	ASSERT_EQ(cache.get("4"), "four");
+
+	checkRange(cache, {
+	                      { "4", "four" },
+	                      { "3", "three" },
+	                      { "2", "two" },
+	                      { "1", "one" },
+	                  });
+}
+
+TEST(LRUCache, AddWithEviction)
+{
+	LRUCache<std::string, std::string> cache(4);
+
+	cache.add("1", "one");
+	cache.add("2", "two");
+	cache.add("3", "three");
+	cache.add("4", "four");
+	cache.add("5", "five");
+	cache.add("6", "six");
+
+	ASSERT_EQ(cache.get("1"), "");
+	ASSERT_EQ(cache.get("2"), "");
+	ASSERT_EQ(cache.get("3"), "three");
+	ASSERT_EQ(cache.get("4"), "four");
+	ASSERT_EQ(cache.get("5"), "five");
+	ASSERT_EQ(cache.get("6"), "six");
+
+	checkRange(cache, {
+	                      { "6", "six" },
+	                      { "5", "five" },
+	                      { "4", "four" },
+	                      { "3", "three" },
+	                  });
+}
+
+TEST(LRUCache, Reordering)
+{
+	LRUCache<std::string, std::string> cache(4);
+
+	// Fill
+	cache.add("1", "one");
+	cache.add("2", "two");
+	cache.add("3", "three");
+	cache.add("4", "four");
+
+	// Push 2 then 4 to most recent
+	cache.add("2", "two");
+	cache.add("4", "four");
+
+	checkRange(cache, {
+	                      { "4", "four" },
+	                      { "2", "two" },
+	                      { "3", "three" },
+	                      { "1", "one" },
+	                  });
+}
\ No newline at end of file