Skip to content

Commit a73cf44

Browse files
committed
Reduce false sharing between pathInfoCache and Store
`perf c2c` shows a lot of cacheline conflicts between purely read-only Store methods (like `parseStorePath()`) and the Sync classes. So allocate pathInfoCache separately to avoid that.
1 parent 5ae1b5f commit a73cf44

File tree

7 files changed

+37
-63
lines changed

7 files changed

+37
-63
lines changed

src/libstore/binary-cache-store.cc

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -125,11 +125,8 @@ void BinaryCacheStore::writeNarInfo(ref<NarInfo> narInfo)
125125

126126
upsertFile(narInfoFile, narInfo->to_string(*this), "text/x-nix-narinfo");
127127

128-
{
129-
auto state_(state.lock());
130-
state_->pathInfoCache.upsert(
131-
std::string(narInfo->path.to_string()), PathInfoCacheValue{.value = std::shared_ptr<NarInfo>(narInfo)});
132-
}
128+
pathInfoCache->lock()->upsert(
129+
std::string(narInfo->path.to_string()), PathInfoCacheValue{.value = std::shared_ptr<NarInfo>(narInfo)});
133130

134131
if (diskCache)
135132
diskCache->upsertNarInfo(

src/libstore/include/nix/store/store-api.hh

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -310,14 +310,11 @@ protected:
310310
}
311311
};
312312

313-
struct State
314-
{
315-
LRUCache<std::string, PathInfoCacheValue> pathInfoCache;
316-
};
317-
318313
void invalidatePathInfoCacheFor(const StorePath & path);
319314

320-
SharedSync<State> state;
315+
// Note: this is a `ref` to avoid false sharing with immutable
316+
// bits of `Store`.
317+
ref<SharedSync<LRUCache<std::string, PathInfoCacheValue>>> pathInfoCache;
321318

322319
std::shared_ptr<NarInfoDiskCache> diskCache;
323320

@@ -860,7 +857,7 @@ public:
860857
*/
861858
void clearPathInfoCache()
862859
{
863-
state.lock()->pathInfoCache.clear();
860+
pathInfoCache->lock()->clear();
864861
}
865862

866863
/**

src/libstore/local-store.cc

Lines changed: 3 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -716,12 +716,8 @@ uint64_t LocalStore::addValidPath(State & state, const ValidPathInfo & info, boo
716716
}
717717
}
718718

719-
{
720-
auto state_(Store::state.lock());
721-
state_->pathInfoCache.upsert(
722-
std::string(info.path.to_string()),
723-
PathInfoCacheValue{.value = std::make_shared<const ValidPathInfo>(info)});
724-
}
719+
pathInfoCache->lock()->upsert(
720+
std::string(info.path.to_string()), PathInfoCacheValue{.value = std::make_shared<const ValidPathInfo>(info)});
725721

726722
return id;
727723
}
@@ -1023,10 +1019,7 @@ void LocalStore::invalidatePath(State & state, const StorePath & path)
10231019
/* Note that the foreign key constraints on the Refs table take
10241020
care of deleting the references entries for `path'. */
10251021

1026-
{
1027-
auto state_(Store::state.lock());
1028-
state_->pathInfoCache.erase(std::string(path.to_string()));
1029-
}
1022+
pathInfoCache->lock()->erase(std::string(path.to_string()));
10301023
}
10311024

10321025
const PublicKeys & LocalStore::getPublicKeys()

src/libstore/remote-store.cc

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -764,10 +764,7 @@ void RemoteStore::collectGarbage(const GCOptions & options, GCResults & results)
764764
results.bytesFreed = readLongLong(conn->from);
765765
readLongLong(conn->from); // obsolete
766766

767-
{
768-
auto state_(Store::state.lock());
769-
state_->pathInfoCache.clear();
770-
}
767+
pathInfoCache->lock()->clear();
771768
}
772769

773770
void RemoteStore::optimiseStore()

src/libstore/store-api.cc

Lines changed: 22 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -306,7 +306,7 @@ StringSet Store::Config::getDefaultSystemFeatures()
306306
Store::Store(const Store::Config & config)
307307
: StoreDirConfig{config}
308308
, config{config}
309-
, state({(size_t) config.pathInfoCacheSize})
309+
, pathInfoCache(make_ref<decltype(pathInfoCache)::element_type>((size_t) config.pathInfoCacheSize))
310310
{
311311
assertLibStoreInitialized();
312312
}
@@ -326,7 +326,7 @@ bool Store::PathInfoCacheValue::isKnownNow()
326326

327327
void Store::invalidatePathInfoCacheFor(const StorePath & path)
328328
{
329-
state.lock()->pathInfoCache.erase(path.to_string());
329+
pathInfoCache->lock()->erase(path.to_string());
330330
}
331331

332332
std::map<std::string, std::optional<StorePath>> Store::queryStaticPartialDerivationOutputMap(const StorePath & path)
@@ -448,22 +448,18 @@ void Store::querySubstitutablePathInfos(const StorePathCAMap & paths, Substituta
448448

449449
bool Store::isValidPath(const StorePath & storePath)
450450
{
451-
{
452-
auto state_(state.lock());
453-
auto res = state_->pathInfoCache.get(storePath.to_string());
454-
if (res && res->isKnownNow()) {
455-
stats.narInfoReadAverted++;
456-
return res->didExist();
457-
}
451+
auto res = pathInfoCache->lock()->get(storePath.to_string());
452+
if (res && res->isKnownNow()) {
453+
stats.narInfoReadAverted++;
454+
return res->didExist();
458455
}
459456

460457
if (diskCache) {
461458
auto res = diskCache->lookupNarInfo(
462459
config.getReference().render(/*FIXME withParams=*/false), std::string(storePath.hashPart()));
463460
if (res.first != NarInfoDiskCache::oUnknown) {
464461
stats.narInfoReadAverted++;
465-
auto state_(state.lock());
466-
state_->pathInfoCache.upsert(
462+
pathInfoCache->lock()->upsert(
467463
storePath.to_string(),
468464
res.first == NarInfoDiskCache::oInvalid ? PathInfoCacheValue{}
469465
: PathInfoCacheValue{.value = res.second});
@@ -518,30 +514,25 @@ std::optional<std::shared_ptr<const ValidPathInfo>> Store::queryPathInfoFromClie
518514
{
519515
auto hashPart = std::string(storePath.hashPart());
520516

521-
{
522-
auto res = state.lock()->pathInfoCache.get(storePath.to_string());
523-
if (res && res->isKnownNow()) {
524-
stats.narInfoReadAverted++;
525-
if (res->didExist())
526-
return std::make_optional(res->value);
527-
else
528-
return std::make_optional(nullptr);
529-
}
517+
auto res = pathInfoCache->lock()->get(storePath.to_string());
518+
if (res && res->isKnownNow()) {
519+
stats.narInfoReadAverted++;
520+
if (res->didExist())
521+
return std::make_optional(res->value);
522+
else
523+
return std::make_optional(nullptr);
530524
}
531525

532526
if (diskCache) {
533527
auto res = diskCache->lookupNarInfo(config.getReference().render(/*FIXME withParams=*/false), hashPart);
534528
if (res.first != NarInfoDiskCache::oUnknown) {
535529
stats.narInfoReadAverted++;
536-
{
537-
auto state_(state.lock());
538-
state_->pathInfoCache.upsert(
539-
storePath.to_string(),
540-
res.first == NarInfoDiskCache::oInvalid ? PathInfoCacheValue{}
541-
: PathInfoCacheValue{.value = res.second});
542-
if (res.first == NarInfoDiskCache::oInvalid || !goodStorePath(storePath, res.second->path))
543-
return std::make_optional(nullptr);
544-
}
530+
pathInfoCache->lock()->upsert(
531+
storePath.to_string(),
532+
res.first == NarInfoDiskCache::oInvalid ? PathInfoCacheValue{}
533+
: PathInfoCacheValue{.value = res.second});
534+
if (res.first == NarInfoDiskCache::oInvalid || !goodStorePath(storePath, res.second->path))
535+
return std::make_optional(nullptr);
545536
assert(res.second);
546537
return std::make_optional(res.second);
547538
}
@@ -577,10 +568,7 @@ void Store::queryPathInfo(const StorePath & storePath, Callback<ref<const ValidP
577568
if (diskCache)
578569
diskCache->upsertNarInfo(config.getReference().render(/*FIXME withParams=*/false), hashPart, info);
579570

580-
{
581-
auto state_(state.lock());
582-
state_->pathInfoCache.upsert(storePath.to_string(), PathInfoCacheValue{.value = info});
583-
}
571+
pathInfoCache->lock()->upsert(storePath.to_string(), PathInfoCacheValue{.value = info});
584572

585573
if (!info || !goodStorePath(storePath, info->path)) {
586574
stats.narInfoMissing++;
@@ -803,10 +791,7 @@ StorePathSet Store::exportReferences(const StorePathSet & storePaths, const Stor
803791

804792
const Store::Stats & Store::getStats()
805793
{
806-
{
807-
auto state_(state.readLock());
808-
stats.pathInfoCacheSize = state_->pathInfoCache.size();
809-
}
794+
stats.pathInfoCacheSize = pathInfoCache->readLock()->size();
810795
return stats;
811796
}
812797

src/libutil/include/nix/util/ref.hh

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,9 @@ private:
1818
std::shared_ptr<T> p;
1919

2020
public:
21+
22+
using element_type = T;
23+
2124
explicit ref(const std::shared_ptr<T> & p)
2225
: p(p)
2326
{

src/libutil/include/nix/util/sync.hh

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,8 @@ private:
3636

3737
public:
3838

39+
using element_type = T;
40+
3941
SyncBase() {}
4042

4143
SyncBase(const T & data)

0 commit comments

Comments
 (0)