forked from Mirrorlandia_minetest/minetest
Try to benchmark common MapBlock usage
This commit is contained in:
parent
e5a6048eec
commit
2c2bc4a427
@ -2,6 +2,7 @@ set (BENCHMARK_SRCS
|
|||||||
${CMAKE_CURRENT_SOURCE_DIR}/benchmark.cpp
|
${CMAKE_CURRENT_SOURCE_DIR}/benchmark.cpp
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/benchmark_lighting.cpp
|
${CMAKE_CURRENT_SOURCE_DIR}/benchmark_lighting.cpp
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/benchmark_serialize.cpp
|
${CMAKE_CURRENT_SOURCE_DIR}/benchmark_serialize.cpp
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/benchmark_mapblock.cpp
|
||||||
PARENT_SCOPE)
|
PARENT_SCOPE)
|
||||||
|
|
||||||
set (BENCHMARK_CLIENT_SRCS
|
set (BENCHMARK_CLIENT_SRCS
|
||||||
|
185
src/benchmark/benchmark_mapblock.cpp
Normal file
185
src/benchmark/benchmark_mapblock.cpp
Normal file
@ -0,0 +1,185 @@
|
|||||||
|
/*
|
||||||
|
Minetest
|
||||||
|
Copyright (C) 2023 Minetest Authors
|
||||||
|
|
||||||
|
This program is free software; you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
the Free Software Foundation; either version 2.1 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU Lesser General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Lesser General Public License along
|
||||||
|
with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "benchmark_setup.h"
|
||||||
|
#include "mapblock.h"
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
typedef std::vector<MapBlock*> MBContainer;
|
||||||
|
|
||||||
|
static void allocateSome(MBContainer &vec, u32 n)
|
||||||
|
{
|
||||||
|
vec.reserve(vec.size() + n);
|
||||||
|
Map *map = reinterpret_cast<Map*>(0x1234);
|
||||||
|
for (u32 i = 0; i < n; i++) {
|
||||||
|
auto *mb = new MapBlock(map, {i & 0xff, 0, i >> 8}, nullptr);
|
||||||
|
vec.push_back(mb);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void freeSome(MBContainer &vec, u32 n)
|
||||||
|
{
|
||||||
|
// deallocate from end since that has no cost moving data inside the vector
|
||||||
|
u32 start_i = 0;
|
||||||
|
if (vec.size() > n)
|
||||||
|
start_i = vec.size() - n;
|
||||||
|
for (u32 i = start_i; i < vec.size(); i++)
|
||||||
|
delete vec[i];
|
||||||
|
vec.resize(start_i);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void freeAll(MBContainer &vec) { freeSome(vec, vec.size()); }
|
||||||
|
|
||||||
|
// usage patterns inspired by ClientMap::updateDrawList()
|
||||||
|
static void workOnMetadata(const MBContainer &vec)
|
||||||
|
{
|
||||||
|
for (MapBlock *block : vec) {
|
||||||
|
#ifndef SERVER
|
||||||
|
bool foo = !!block->mesh;
|
||||||
|
#else
|
||||||
|
bool foo = true;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (block->refGet() > 2)
|
||||||
|
block->refDrop();
|
||||||
|
|
||||||
|
v3s16 pos = block->getPos() * MAP_BLOCKSIZE;
|
||||||
|
if (foo)
|
||||||
|
pos += v3s16(MAP_BLOCKSIZE / 2);
|
||||||
|
|
||||||
|
if (pos.getDistanceFrom(v3s16(0)) > 30000)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
block->resetUsageTimer();
|
||||||
|
block->refGrab();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// usage patterns inspired by LBMManager::applyLBMs()
|
||||||
|
static u32 workOnNodes(const MBContainer &vec)
|
||||||
|
{
|
||||||
|
u32 foo = 0;
|
||||||
|
for (MapBlock *block : vec) {
|
||||||
|
block->resetUsageTimer();
|
||||||
|
|
||||||
|
if (block->isOrphan())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
v3s16 pos_of_block = block->getPosRelative();
|
||||||
|
v3s16 pos;
|
||||||
|
MapNode n;
|
||||||
|
for (pos.X = 0; pos.X < MAP_BLOCKSIZE; pos.X++) {
|
||||||
|
for (pos.Y = 0; pos.Y < MAP_BLOCKSIZE; pos.Y++) {
|
||||||
|
for (pos.Z = 0; pos.Z < MAP_BLOCKSIZE; pos.Z++) {
|
||||||
|
n = block->getNodeNoCheck(pos);
|
||||||
|
|
||||||
|
if (n.getContent() == CONTENT_AIR) {
|
||||||
|
auto p = pos + pos_of_block;
|
||||||
|
foo ^= p.X + p.Y + p.Z;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return foo;
|
||||||
|
}
|
||||||
|
|
||||||
|
// usage patterns inspired by ABMHandler::apply()
|
||||||
|
// touches both metadata and node data at the same time
|
||||||
|
static u32 workOnBoth(const MBContainer &vec)
|
||||||
|
{
|
||||||
|
int foo = 0;
|
||||||
|
for (MapBlock *block : vec) {
|
||||||
|
block->contents.clear();
|
||||||
|
block->contents_cached = false;
|
||||||
|
|
||||||
|
v3s16 p0;
|
||||||
|
for(p0.X=0; p0.X<MAP_BLOCKSIZE; p0.X++)
|
||||||
|
for(p0.Y=0; p0.Y<MAP_BLOCKSIZE; p0.Y++)
|
||||||
|
for(p0.Z=0; p0.Z<MAP_BLOCKSIZE; p0.Z++)
|
||||||
|
{
|
||||||
|
MapNode n = block->getNodeNoCheck(p0);
|
||||||
|
content_t c = n.getContent();
|
||||||
|
|
||||||
|
if (!block->contents_cached && !block->do_not_cache_contents) {
|
||||||
|
block->contents.insert(c);
|
||||||
|
if (block->contents.size() > 10) {
|
||||||
|
// Too many different nodes... don't try to cache
|
||||||
|
block->do_not_cache_contents = true;
|
||||||
|
block->contents.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
block->contents_cached = !block->do_not_cache_contents;
|
||||||
|
foo += block->contents.size();
|
||||||
|
}
|
||||||
|
return foo;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define BENCH1(_count) \
|
||||||
|
BENCHMARK_ADVANCED("allocate_" #_count)(Catch::Benchmark::Chronometer meter) { \
|
||||||
|
MBContainer vec; \
|
||||||
|
const u32 pcount = _count / meter.runs(); \
|
||||||
|
meter.measure([&] { \
|
||||||
|
allocateSome(vec, pcount); \
|
||||||
|
return vec.size(); \
|
||||||
|
}); \
|
||||||
|
freeAll(vec); \
|
||||||
|
}; \
|
||||||
|
BENCHMARK_ADVANCED("testCase1_" #_count)(Catch::Benchmark::Chronometer meter) { \
|
||||||
|
MBContainer vec; \
|
||||||
|
allocateSome(vec, _count); \
|
||||||
|
meter.measure([&] { \
|
||||||
|
workOnMetadata(vec); \
|
||||||
|
}); \
|
||||||
|
freeAll(vec); \
|
||||||
|
}; \
|
||||||
|
BENCHMARK_ADVANCED("testCase2_" #_count)(Catch::Benchmark::Chronometer meter) { \
|
||||||
|
MBContainer vec; \
|
||||||
|
allocateSome(vec, _count); \
|
||||||
|
meter.measure([&] { \
|
||||||
|
return workOnNodes(vec); \
|
||||||
|
}); \
|
||||||
|
freeAll(vec); \
|
||||||
|
}; \
|
||||||
|
BENCHMARK_ADVANCED("testCase3_" #_count)(Catch::Benchmark::Chronometer meter) { \
|
||||||
|
MBContainer vec; \
|
||||||
|
allocateSome(vec, _count); \
|
||||||
|
meter.measure([&] { \
|
||||||
|
return workOnBoth(vec); \
|
||||||
|
}); \
|
||||||
|
freeAll(vec); \
|
||||||
|
}; \
|
||||||
|
BENCHMARK_ADVANCED("free_" #_count)(Catch::Benchmark::Chronometer meter) { \
|
||||||
|
MBContainer vec; \
|
||||||
|
allocateSome(vec, _count); \
|
||||||
|
/* catch2 does multiple runs so we have to be careful to not dealloc too many */ \
|
||||||
|
const u32 pcount = _count / meter.runs(); \
|
||||||
|
meter.measure([&] { \
|
||||||
|
freeSome(vec, pcount); \
|
||||||
|
return vec.size(); \
|
||||||
|
}); \
|
||||||
|
freeAll(vec); \
|
||||||
|
};
|
||||||
|
|
||||||
|
TEST_CASE("benchmark_mapblock") {
|
||||||
|
BENCH1(900)
|
||||||
|
BENCH1(2200)
|
||||||
|
BENCH1(7500) // <- default client_mapblock_limit
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user