Add NavMeshTilesCache benchmarks

cherry-pick-74612b12
elsid 4 years ago
parent 52eccf1a2d
commit b9a40bc5fc
No known key found for this signature in database
GPG Key ID: B845CB9FEE18AB40

@ -34,6 +34,7 @@ option(BUILD_NIFTEST "Build nif file tester" ON)
option(BUILD_DOCS "Build documentation." OFF ) option(BUILD_DOCS "Build documentation." OFF )
option(BUILD_WITH_CODE_COVERAGE "Enable code coverage with gconv" OFF) option(BUILD_WITH_CODE_COVERAGE "Enable code coverage with gconv" OFF)
option(BUILD_UNITTESTS "Enable Unittests with Google C++ Unittest" OFF) option(BUILD_UNITTESTS "Enable Unittests with Google C++ Unittest" OFF)
option(BUILD_BENCHMARKS "Build benchmarks with Google Benchmark" OFF)
option(BULLET_USE_DOUBLES "Use double precision for Bullet" ON) option(BULLET_USE_DOUBLES "Use double precision for Bullet" ON)
set(OpenGL_GL_PREFERENCE LEGACY) # Use LEGACY as we use GL2; GLNVD is for GL3 and up. set(OpenGL_GL_PREFERENCE LEGACY) # Use LEGACY as we use GL2; GLNVD is for GL3 and up.
@ -549,6 +550,10 @@ if (BUILD_UNITTESTS)
add_subdirectory( apps/openmw_test_suite ) add_subdirectory( apps/openmw_test_suite )
endif() endif()
if (BUILD_BENCHMARKS)
add_subdirectory(apps/benchmarks)
endif()
if (WIN32) if (WIN32)
if (MSVC) if (MSVC)
if (OPENMW_MP_BUILD) if (OPENMW_MP_BUILD)

@ -0,0 +1,34 @@
cmake_minimum_required(VERSION 3.11)
set(BENCHMARK_ENABLE_TESTING OFF)
set(BENCHMARK_ENABLE_INSTALL OFF)
set(BENCHMARK_ENABLE_GTEST_TESTS OFF)
set(SAVED_CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}")
string(REPLACE "-Wsuggest-override" "" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}")
string(REPLACE "-Wundef" "" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}")
include(FetchContent)
FetchContent_Declare(benchmark
URL https://github.com/google/benchmark/archive/refs/tags/v1.5.2.zip
URL_HASH MD5=49395b757a7c4656d70f1328d93efd00
SOURCE_DIR fetched/benchmark
)
FetchContent_MakeAvailableExcludeFromAll(benchmark)
set(CMAKE_CXX_FLAGS "${SAVED_CMAKE_CXX_FLAGS}")
openmw_add_executable(openmw_detournavigator_navmeshtilescache_benchmark detournavigator/navmeshtilescache.cpp)
target_compile_options(openmw_detournavigator_navmeshtilescache_benchmark PRIVATE -Wall)
target_compile_features(openmw_detournavigator_navmeshtilescache_benchmark PRIVATE cxx_std_17)
target_link_libraries(openmw_detournavigator_navmeshtilescache_benchmark benchmark::benchmark components)
if (UNIX AND NOT APPLE)
target_link_libraries(openmw_detournavigator_navmeshtilescache_benchmark ${CMAKE_THREAD_LIBS_INIT})
endif()
if (MSVC)
if (CMAKE_CL_64)
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} /bigobj")
endif (CMAKE_CL_64)
endif (MSVC)

@ -0,0 +1,215 @@
#include <benchmark/benchmark.h>
#include <components/detournavigator/navmeshtilescache.hpp>
#include <algorithm>
#include <random>
#include <iostream>
namespace
{
using namespace DetourNavigator;
struct Key
{
osg::Vec3f mAgentHalfExtents;
TilePosition mTilePosition;
RecastMesh mRecastMesh;
std::vector<OffMeshConnection> mOffMeshConnections;
};
struct Item
{
Key mKey;
NavMeshData mValue;
};
template <typename Random>
TilePosition generateTilePosition(int max, Random& random)
{
std::uniform_int_distribution<int> distribution(0, max);
return TilePosition(distribution(random), distribution(random));
}
template <typename Random>
osg::Vec3f generateAgentHalfExtents(float min, float max, Random& random)
{
std::uniform_int_distribution<int> distribution(min, max);
return osg::Vec3f(distribution(random), distribution(random), distribution(random));
}
template <typename OutputIterator, typename Random>
void generateVertices(OutputIterator out, std::size_t number, Random& random)
{
std::uniform_real_distribution<float> distribution(0.0, 1.0);
std::generate_n(out, 3 * (number - number % 3), [&] { return distribution(random); });
}
template <typename OutputIterator, typename Random>
void generateIndices(OutputIterator out, int max, std::size_t number, Random& random)
{
std::uniform_int_distribution<int> distribution(0, max);
std::generate_n(out, number - number % 3, [&] { return distribution(random); });
}
AreaType toAreaType(int index)
{
switch (index)
{
case 0: return AreaType_null;
case 1: return AreaType_water;
case 2: return AreaType_door;
case 3: return AreaType_pathgrid;
case 4: return AreaType_ground;
}
return AreaType_null;
}
template <typename Random>
AreaType generateAreaType(Random& random)
{
std::uniform_int_distribution<int> distribution(0, 4);
return toAreaType(distribution(random));;
}
template <typename OutputIterator, typename Random>
void generateAreaTypes(OutputIterator out, std::size_t triangles, Random& random)
{
std::generate_n(out, triangles, [&] { return generateAreaType(random); });
}
template <typename OutputIterator, typename Random>
void generateWater(OutputIterator out, std::size_t count, Random& random)
{
std::uniform_real_distribution<btScalar> distribution(0.0, 1.0);
std::generate_n(out, count, [&] {
const btVector3 shift(distribution(random), distribution(random), distribution(random));
return RecastMesh::Water {1, btTransform(btMatrix3x3::getIdentity(), shift)};
});
}
template <typename OutputIterator, typename Random>
void generateOffMeshConnection(OutputIterator out, std::size_t count, Random& random)
{
std::uniform_real_distribution<btScalar> distribution(0.0, 1.0);
std::generate_n(out, count, [&] {
const osg::Vec3f start(distribution(random), distribution(random), distribution(random));
const osg::Vec3f end(distribution(random), distribution(random), distribution(random));
return OffMeshConnection {start, end, generateAreaType(random)};
});
}
template <class Random>
Key generateKey(std::size_t triangles, Random& random)
{
const osg::Vec3f agentHalfExtents = generateAgentHalfExtents(0.5, 1.5, random);
const TilePosition tilePosition = generateTilePosition(10000, random);
const std::size_t generation = std::uniform_int_distribution<std::size_t>(0, 100)(random);
const std::size_t revision = std::uniform_int_distribution<std::size_t>(0, 10000)(random);
std::vector<float> vertices;
generateVertices(std::back_inserter(vertices), triangles * 1.98, random);
std::vector<int> indices;
generateIndices(std::back_inserter(indices), static_cast<int>(vertices.size() / 3) - 1, vertices.size() * 1.53, random);
std::vector<AreaType> areaTypes;
generateAreaTypes(std::back_inserter(areaTypes), indices.size() / 3, random);
std::vector<RecastMesh::Water> water;
generateWater(std::back_inserter(water), 2, random);
const std::size_t trianglesPerChunk = 256;
RecastMesh recastMesh(generation, revision, std::move(indices), std::move(vertices),
std::move(areaTypes), std::move(water), trianglesPerChunk);
std::vector<OffMeshConnection> offMeshConnections;
generateOffMeshConnection(std::back_inserter(offMeshConnections), 300, random);
return Key {agentHalfExtents, tilePosition, std::move(recastMesh), std::move(offMeshConnections)};
}
constexpr std::size_t trianglesPerTile = 310;
template <typename OutputIterator, typename Random>
void generateKeys(OutputIterator out, std::size_t count, Random& random)
{
std::generate_n(out, count, [&] { return generateKey(trianglesPerTile, random); });
}
template <typename OutputIterator, typename Random>
void fillCache(OutputIterator out, Random& random, NavMeshTilesCache& cache)
{
std::size_t size = cache.getStats().mNavMeshCacheSize;
while (true)
{
Key key = generateKey(trianglesPerTile, random);
cache.set(key.mAgentHalfExtents, key.mTilePosition, key.mRecastMesh, key.mOffMeshConnections, NavMeshData());
*out++ = std::move(key);
const std::size_t newSize = cache.getStats().mNavMeshCacheSize;
if (size >= newSize)
break;
size = newSize;
}
}
template <std::size_t maxCacheSize, int hitPercentage>
void getFromFilledCache(benchmark::State& state)
{
NavMeshTilesCache cache(maxCacheSize);
std::minstd_rand random;
std::vector<Key> keys;
fillCache(std::back_inserter(keys), random, cache);
generateKeys(std::back_inserter(keys), keys.size() * (100 - hitPercentage) / 100, random);
std::size_t n = 0;
while (state.KeepRunning())
{
const auto& key = keys[n++ % keys.size()];
const auto result = cache.get(key.mAgentHalfExtents, key.mTilePosition, key.mRecastMesh, key.mOffMeshConnections);
benchmark::DoNotOptimize(result);
}
}
constexpr auto getFromFilledCache_1m_100hit = getFromFilledCache<1 * 1024 * 1024, 100>;
constexpr auto getFromFilledCache_4m_100hit = getFromFilledCache<4 * 1024 * 1024, 100>;
constexpr auto getFromFilledCache_16m_100hit = getFromFilledCache<16 * 1024 * 1024, 100>;
constexpr auto getFromFilledCache_64m_100hit = getFromFilledCache<64 * 1024 * 1024, 100>;
constexpr auto getFromFilledCache_1m_70hit = getFromFilledCache<1 * 1024 * 1024, 70>;
constexpr auto getFromFilledCache_4m_70hit = getFromFilledCache<4 * 1024 * 1024, 70>;
constexpr auto getFromFilledCache_16m_70hit = getFromFilledCache<16 * 1024 * 1024, 70>;
constexpr auto getFromFilledCache_64m_70hit = getFromFilledCache<64 * 1024 * 1024, 70>;
template <std::size_t maxCacheSize>
void setToBoundedNonEmptyCache(benchmark::State& state)
{
NavMeshTilesCache cache(maxCacheSize);
std::minstd_rand random;
std::vector<Key> keys;
fillCache(std::back_inserter(keys), random, cache);
generateKeys(std::back_inserter(keys), keys.size() * 2, random);
std::reverse(keys.begin(), keys.end());
std::size_t n = 0;
while (state.KeepRunning())
{
const auto& key = keys[n++ % keys.size()];
const auto result = cache.set(key.mAgentHalfExtents, key.mTilePosition, key.mRecastMesh, key.mOffMeshConnections, NavMeshData());
benchmark::DoNotOptimize(result);
}
}
constexpr auto setToBoundedNonEmptyCache_1m = setToBoundedNonEmptyCache<1 * 1024 * 1024>;
constexpr auto setToBoundedNonEmptyCache_4m = setToBoundedNonEmptyCache<4 * 1024 * 1024>;
constexpr auto setToBoundedNonEmptyCache_16m = setToBoundedNonEmptyCache<16 * 1024 * 1024>;
constexpr auto setToBoundedNonEmptyCache_64m = setToBoundedNonEmptyCache<64 * 1024 * 1024>;
} // namespace
BENCHMARK(getFromFilledCache_1m_100hit);
BENCHMARK(getFromFilledCache_4m_100hit);
BENCHMARK(getFromFilledCache_16m_100hit);
BENCHMARK(getFromFilledCache_64m_100hit);
BENCHMARK(getFromFilledCache_1m_70hit);
BENCHMARK(getFromFilledCache_4m_70hit);
BENCHMARK(getFromFilledCache_16m_70hit);
BENCHMARK(getFromFilledCache_64m_70hit);
BENCHMARK(setToBoundedNonEmptyCache_1m);
BENCHMARK(setToBoundedNonEmptyCache_4m);
BENCHMARK(setToBoundedNonEmptyCache_16m);
BENCHMARK(setToBoundedNonEmptyCache_64m);
BENCHMARK_MAIN();

@ -46,6 +46,9 @@ namespace DetourNavigator
ChunkyTriMesh(const std::vector<float>& verts, const std::vector<int>& tris, ChunkyTriMesh(const std::vector<float>& verts, const std::vector<int>& tris,
const std::vector<AreaType>& flags, const std::size_t trisPerChunk); const std::vector<AreaType>& flags, const std::size_t trisPerChunk);
ChunkyTriMesh(ChunkyTriMesh&&) = default;
ChunkyTriMesh& operator=(ChunkyTriMesh&&) = default;
ChunkyTriMesh(const ChunkyTriMesh&) = delete; ChunkyTriMesh(const ChunkyTriMesh&) = delete;
ChunkyTriMesh& operator=(const ChunkyTriMesh&) = delete; ChunkyTriMesh& operator=(const ChunkyTriMesh&) = delete;

@ -88,27 +88,27 @@ namespace DetourNavigator
return Value(*this, iterator); return Value(*this, iterator);
} }
void NavMeshTilesCache::reportStats(unsigned int frameNumber, osg::Stats& stats) const NavMeshTilesCache::Stats NavMeshTilesCache::getStats() const
{ {
std::size_t navMeshCacheSize = 0; Stats result;
std::size_t usedNavMeshTiles = 0;
std::size_t cachedNavMeshTiles = 0;
std::size_t hitCount = 0;
std::size_t getCount = 0;
{ {
const std::lock_guard<std::mutex> lock(mMutex); const std::lock_guard<std::mutex> lock(mMutex);
navMeshCacheSize = mUsedNavMeshDataSize; result.mNavMeshCacheSize = mUsedNavMeshDataSize;
usedNavMeshTiles = mBusyItems.size(); result.mUsedNavMeshTiles = mBusyItems.size();
cachedNavMeshTiles = mFreeItems.size(); result.mCachedNavMeshTiles = mFreeItems.size();
hitCount = mHitCount; result.mHitCount = mHitCount;
getCount = mGetCount; result.mGetCount = mGetCount;
} }
return result;
}
stats.setAttribute(frameNumber, "NavMesh CacheSize", navMeshCacheSize); void NavMeshTilesCache::reportStats(unsigned int frameNumber, osg::Stats& out) const
stats.setAttribute(frameNumber, "NavMesh UsedTiles", usedNavMeshTiles); {
stats.setAttribute(frameNumber, "NavMesh CachedTiles", cachedNavMeshTiles); const Stats stats = getStats();
stats.setAttribute(frameNumber, "NavMesh CacheHitRate", static_cast<double>(hitCount) / getCount * 100.0); out.setAttribute(frameNumber, "NavMesh CacheSize", stats.mNavMeshCacheSize);
out.setAttribute(frameNumber, "NavMesh UsedTiles", stats.mUsedNavMeshTiles);
out.setAttribute(frameNumber, "NavMesh CachedTiles", stats.mCachedNavMeshTiles);
out.setAttribute(frameNumber, "NavMesh CacheHitRate", static_cast<double>(stats.mHitCount) / stats.mGetCount * 100.0);
} }
void NavMeshTilesCache::removeLeastRecentlyUsed() void NavMeshTilesCache::removeLeastRecentlyUsed()

@ -188,6 +188,15 @@ namespace DetourNavigator
ItemIterator mIterator; ItemIterator mIterator;
}; };
struct Stats
{
std::size_t mNavMeshCacheSize;
std::size_t mUsedNavMeshTiles;
std::size_t mCachedNavMeshTiles;
std::size_t mHitCount;
std::size_t mGetCount;
};
NavMeshTilesCache(const std::size_t maxNavMeshDataSize); NavMeshTilesCache(const std::size_t maxNavMeshDataSize);
Value get(const osg::Vec3f& agentHalfExtents, const TilePosition& changedTile, Value get(const osg::Vec3f& agentHalfExtents, const TilePosition& changedTile,
@ -197,6 +206,8 @@ namespace DetourNavigator
const RecastMesh& recastMesh, const std::vector<OffMeshConnection>& offMeshConnections, const RecastMesh& recastMesh, const std::vector<OffMeshConnection>& offMeshConnections,
NavMeshData&& value); NavMeshData&& value);
Stats getStats() const;
void reportStats(unsigned int frameNumber, osg::Stats& stats) const; void reportStats(unsigned int frameNumber, osg::Stats& stats) const;
private: private:

Loading…
Cancel
Save