Compare commits

...
1 Commits
Author SHA1 Message Date
admin c1d134a073 use raw pointers instead of shared pointers 2025-07-29 07:16:31 -07:00
4 changed files with 57 additions and 54 deletions
@@ -117,13 +117,21 @@ auto ActionPointDistancesCache::GetMapId(const HexMap* map) -> MapId {
return MapId{.terrainTypesId = map->base_hash(), .modifierId = modifierId};
}
void ActionPointDistancesCache::ConsolidateThreadLocalCache_Racy() {
persistentCache.insert(std::begin(sharedDistances), std::end(sharedDistances));
sharedDistances.clear();
persistentCache.insert(std::begin(sharedCache), std::end(sharedCache));
sharedCache.clear();
// Clear the current thread's cache since persistent cache now has everything
tlsCache.clear();
}
ActionPointDistancesCache::~ActionPointDistancesCache() {
// We own the ActionPointDistances pointers in the persistent cache and in the shared cache.
for (const auto& [key, value] : persistentCache) { delete value; }
for (const auto& [key, value] : sharedCache) { delete value; }
}
auto ActionPointDistancesCache::GetRaw(
const HexMap* map,
const MapId& mapId,
@@ -142,7 +150,7 @@ auto ActionPointDistancesCache::GetRaw(
#endif
// Return directly from persistent cache without TLS insertion
// This avoids the overhead of thread-local storage operations on hot path
return persistentIt->second.rawPtr;
return persistentIt->second;
}
#if CACHE_STATS_LOGGING_
@@ -155,7 +163,7 @@ auto ActionPointDistancesCache::GetRaw(
cacheStats.localHits++;
MaybePrintCacheStats();
#endif
return localIt->second.rawPtr; // Raw pointer - zero overhead access!
return localIt->second; // Raw pointer - zero overhead access!
}
#if CACHE_STATS_LOGGING_
@@ -163,19 +171,19 @@ auto ActionPointDistancesCache::GetRaw(
#endif
// Check shared cache before expensive ice-clearing operation
shared_ptr<ActionPointDistances> sharedResult;
if (sharedDistances.if_contains(cacheKey, [&sharedResult](const auto& kv) {
const ActionPointDistances* sharedResult;
if (sharedCache.if_contains(cacheKey, [&sharedResult](const auto& kv) {
sharedResult = kv.second;
})) {
#if CACHE_STATS_LOGGING_
cacheStats.sharedAccesses++;
#endif
// Cache hit in shared cache - store in thread-local cache and return
tlsCache.emplace(cacheKey, CacheEntry(sharedResult));
tlsCache.emplace(cacheKey, sharedResult);
#if CACHE_STATS_LOGGING_
MaybePrintCacheStats();
#endif
return sharedResult.get();
return sharedResult;
}
// Cache miss in both caches - need to create ice-cleared map for pathfinding computation
@@ -212,14 +220,14 @@ auto ActionPointDistancesCache::GetRaw(
auto result = creationResult.apd;
// Store in shared cache
sharedDistances.lazy_emplace_l(
sharedCache.lazy_emplace_l(
cacheKey,
[](const auto& kv) { /* already checked above */ },
[=](const auto& ctor) { ctor(cacheKey, result); });
// Cache result locally for future lookups by this thread
// Store both shared_ptr and raw pointer for hybrid access
tlsCache.emplace(cacheKey, CacheEntry(result));
tlsCache.emplace(cacheKey, result);
// Prevent unbounded cache growth - limit to reasonable size
if (tlsCache.size() > 100) {
@@ -233,7 +241,7 @@ auto ActionPointDistancesCache::GetRaw(
tlsCache.erase(tlsCache.begin(), it);
}
return result.get();
return result;
}
void ActionPointDistancesCache::ClearThreadLocalCache() { tlsCache.clear(); }
@@ -57,36 +57,6 @@ struct FullCacheKeyHash {
class ActionPointDistancesCache {
private:
struct CacheEntry {
shared_ptr<ActionPointDistances> sharedPtr;
const ActionPointDistances* rawPtr;
explicit CacheEntry(shared_ptr<ActionPointDistances> ptr)
: sharedPtr(std::move(ptr)),
rawPtr(sharedPtr.get()) {}
};
// Tier 1: persistent map. This is NOT safe to write to while reads may be happening.
using PersistentMap = gtl::flat_hash_map<FullCacheKey, CacheEntry, FullCacheKeyHash>;
PersistentMap persistentCache;
using APDMap = gtl::parallel_flat_hash_map<
FullCacheKey,
shared_ptr<ActionPointDistances>,
FullCacheKeyHash,
std::equal_to<FullCacheKey>,
std::allocator<std::pair<const FullCacheKey, shared_ptr<ActionPointDistances>>>,
6,
std::mutex>;
APDMap sharedDistances;
using TLSCache = gtl::flat_hash_map<FullCacheKey, CacheEntry, FullCacheKeyHash>;
static thread_local TLSCache tlsCache;
// Epoch system removed - TLS cache uses size-based eviction instead
// Helper to build cache key
static auto MakeCacheKey(
const MapId& mapId,
@@ -94,13 +64,41 @@ private:
bool includeBravingWater,
int braveWaterActionPointCost) -> FullCacheKey;
// Tier 1: persistent map. This is NOT safe to write to while reads may be happening. Owns the
// ActionPointDistances pointers, so it must be cleared.
using PersistentMap =
gtl::flat_hash_map<FullCacheKey, const ActionPointDistances*, FullCacheKeyHash>;
PersistentMap persistentCache{};
// Tier 2: thread-local cache. This stores items that are not yet in the persistent cache.
// Does NOT own the ActionPointDistances objects; they are also present in the shared cache.
using TLSCache =
gtl::flat_hash_map<FullCacheKey, const ActionPointDistances*, FullCacheKeyHash>;
static thread_local TLSCache tlsCache;
// Tier 3: shared cache. This is thread-safe and can be accessed concurrently. Does own the
// ActionPointDistances objects, so it must be cleared. Can be consolidated into the persistent
// cache when no reads are happening.
using APDMap = gtl::parallel_flat_hash_map<
FullCacheKey,
const ActionPointDistances*,
FullCacheKeyHash,
std::equal_to<FullCacheKey>,
std::allocator<std::pair<const FullCacheKey, const ActionPointDistances*>>,
6,
std::mutex>;
APDMap sharedCache{};
public:
explicit ActionPointDistancesCache() {
// Pre-size persistent cache to reduce hash collisions
// Estimate: ~12 entries from pre-fetching + ~50-100 entries during gameplay
persistentCache.reserve(128);
}
~ActionPointDistancesCache();
// Returns raw pointer for zero overhead access
// Lifetime guaranteed by shared cache ownership
auto GetRaw(
@@ -9,7 +9,7 @@
#include "src/main/cpp/net/eagle0/shardok/library/map/CoordsSet.hpp"
// Dynamic thread count based on hardware capabilities
static const int ASYNC_COUNT = []() {
static const int ASYNC_COUNT = [] {
const int cores = static_cast<int>(std::thread::hardware_concurrency());
// Use cores-2 to leave room for OS and other processes, minimum 4 threads
const int threadCount = std::max(4, cores - 4);
@@ -26,7 +26,7 @@ void FixedActionPointDistances::SetCacheDirectory(const string& newDir) {
static thread_local byte_vector _scratch;
FixedActionPointDistances::FixedActionPointDistances(const HexMap* map, int columnCount)
FixedActionPointDistances::FixedActionPointDistances(const int8_t columnCount)
: ActionPointDistances(columnCount) {}
auto FixedActionPointDistances::Create(
@@ -37,12 +37,9 @@ auto FixedActionPointDistances::Create(
bool includeBravingWater,
int braveWaterActionPointCost) -> CreationResult {
// Create the object using private constructor
auto apd = std::shared_ptr<FixedActionPointDistances>(
new FixedActionPointDistances(map, map->column_count()));
auto apd = new FixedActionPointDistances(map->column_count());
CreationResult result;
result.apd = apd;
result.loadedFromFile = false;
CreationResult result{.apd = apd, .loadedFromFile = false};
string path = "";
@@ -73,8 +70,8 @@ auto FixedActionPointDistances::Create(
for (int fromIndex = 0; fromIndex < indexCount; fromIndex++) {
apd->distances[fromIndex].insert(
apd->distances[fromIndex].end(),
&(ptr[0]),
&(ptr[indexCount]));
&ptr[0],
&ptr[indexCount]);
ptr += indexCount;
}
result.loadedFromFile = true;
@@ -97,7 +94,7 @@ auto FixedActionPointDistances::Create(
for (int i = 0; i < chunkSize; i++) {
const auto fromIndex = chunkStartIndex + i;
if (fromIndex >= indexCount) { continue; }
chunkVec.push_back(ActionPointDistances::GenerateDistances(
chunkVec.push_back(GenerateDistances(
fromIndex,
map,
includeBravingWater,
@@ -19,7 +19,7 @@ using BattalionTypeSPtr = std::shared_ptr<const BattalionType>;
class FixedActionPointDistances final : public ActionPointDistances {
public:
struct CreationResult {
std::shared_ptr<FixedActionPointDistances> apd;
const FixedActionPointDistances *apd;
bool loadedFromFile;
};
@@ -29,7 +29,7 @@ private:
inline static string cacheDirectory = "";
// Private constructor - use Create factory method instead
explicit FixedActionPointDistances(const HexMap *map, int columnCount);
explicit FixedActionPointDistances(int8_t columnCount);
public:
static void SetCacheDirectory(const string &newDir);