mirror of
https://github.com/nolen777/eagle0.git
synced 2026-07-28 21:35:42 +00:00
Compare commits
1
Commits
c6336979c8
...
raw_ptr
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c1d134a073 |
+19
-11
@@ -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(); }
|
||||
|
||||
+29
-31
@@ -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(
|
||||
|
||||
+7
-10
@@ -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,
|
||||
|
||||
+2
-2
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user