Skip to content

Multithreaded evaluator #125

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 119 commits into
base: detsys-main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
119 commits
Select commit Hold shift + click to select a range
6760b39
EvalState: Make the parse/eval caches thread-safe
edolstra Jan 4, 2024
d3854d1
LRUCache: Mark size() as const
edolstra Jan 4, 2024
945cd69
Sync: Add support for shared locks
edolstra Jan 4, 2024
5f3b1a3
WIP
edolstra Feb 4, 2024
9ddca98
WIP3
edolstra Mar 12, 2024
d133aca
WIP4
edolstra May 23, 2024
1a55754
Disable some blackhole tests for now
edolstra May 24, 2024
d623dfb
WIP working
edolstra May 28, 2024
a9e3594
Better hash
edolstra May 29, 2024
b63a132
Symbol table concurrency hack
edolstra May 29, 2024
76f822f
Hacks
edolstra May 29, 2024
6a85af7
Fix failures due to value reuse
edolstra May 31, 2024
6eafc52
Revive the Boehm GC alloc cache
edolstra May 31, 2024
f018a55
Make RegexCache thread-safe
edolstra May 31, 2024
ec8593d
Add some stats
edolstra Jun 3, 2024
105dea5
Cleanup
edolstra Jun 3, 2024
27fb652
Make EvalState::srcToStore thread-safe
edolstra Jun 4, 2024
d990974
PosixSourceAccessor: Use SharedSync
edolstra Jun 5, 2024
eba54f5
FileParseCache, FileEvalCache: Use read lock
edolstra Jun 5, 2024
a25a5b7
Add getOptional()
edolstra Jun 6, 2024
ca11328
EvalState: Add importResolutionCache
edolstra Jun 6, 2024
c2c01d8
Make fileEvalCache insertion more efficient
edolstra Jun 6, 2024
9b88021
Ensure that files are parsed/evaluated only once
edolstra Jun 6, 2024
708e0e8
Small optimization
edolstra Jun 6, 2024
cc38822
SymbolStr: Remove std::string conversion
edolstra Jun 6, 2024
424e01e
Use a contiguous arena for storing symbols
edolstra Jun 6, 2024
c663076
Executor: Randomize the work queue
edolstra Jun 6, 2024
adcc351
Provide std::hash<SourcePath>
edolstra Jun 6, 2024
3988faf
Provide std::hash<Symbol>
edolstra Jun 6, 2024
a70ec9e
Remove unused #include
edolstra Jun 6, 2024
0cd29fe
Split the symbol table into domains
edolstra Jun 6, 2024
0c87ead
Fix --disable-gc build
edolstra Jun 7, 2024
33f50ae
Don't use finishValue() for thunks
edolstra Jun 7, 2024
5e87cf4
Remove debug statement
edolstra Jun 7, 2024
400a670
Specify memory order
edolstra Jun 7, 2024
5c6eb1a
Split the PosixSourceAccessor lstat cache
edolstra Jun 7, 2024
3353f9a
nix search: Restore output
edolstra Jun 13, 2024
9b814c4
Make the max-call-depth check thread-local
edolstra Jun 13, 2024
fd5c32b
Move code
edolstra Jun 13, 2024
1bdf907
nix flake show: Make multi-threaded
edolstra Jun 14, 2024
3cc1319
Disable some failing tests for now
edolstra Jun 14, 2024
f6cbd6a
Merge remote-tracking branch 'origin/master' into multithreaded-eval
edolstra Jun 18, 2024
6103246
Cleanups
edolstra Jun 19, 2024
576a03e
Re-enable assertNoSymlinks()
edolstra Jun 19, 2024
52bd994
Formatting
edolstra Jun 19, 2024
b713591
Merge remote-tracking branch 'origin/master' into multithreaded-eval
edolstra Jun 19, 2024
fcbdc9f
Merge remote-tracking branch 'origin/master' into multithreaded-eval
edolstra Jul 3, 2024
997af66
Make the default GC_INITIAL_HEAP_SIZE a lot bigger
edolstra Jul 5, 2024
d3397d7
Merge remote-tracking branch 'origin/master' into multithreaded-eval
edolstra Jul 11, 2024
2b4c36f
Remove obsolete comment
edolstra Jul 11, 2024
d131a02
Merge remote-tracking branch 'origin/master' into multithreaded-eval
edolstra Jul 12, 2024
4482fe4
Merge remote-tracking branch 'origin/master' into multithreaded-eval
edolstra Jul 26, 2024
8cf80c9
nix repl: Remove unnecessary call to evalString
edolstra Jul 26, 2024
67ff326
Remove FIXME
edolstra Jul 26, 2024
c8c9500
Merge remote-tracking branch 'origin/master' into multithreaded-eval
edolstra Aug 5, 2024
dd44b26
Merge remote-tracking branch 'origin/master' into multithreaded-eval
edolstra Aug 12, 2024
9102baf
Make AllowListSourceAccessor thread-safe
edolstra Aug 12, 2024
998a289
Make positionToDocComment thread-safe
edolstra Aug 12, 2024
5310b0f
callFunction(): Use correct environment in error messages
edolstra Aug 13, 2024
839aec2
callFunction(): Create the primop app chain safely
edolstra Aug 13, 2024
4f90786
Debug
edolstra Aug 13, 2024
6357885
Move perf counters into EvalState
edolstra Aug 13, 2024
4086c1c
Debug
edolstra Aug 13, 2024
a6d8217
Remove "SPURIOUS" message
edolstra Aug 13, 2024
ea4e981
Fix formatting
edolstra Aug 13, 2024
d36ea2e
Fix meson build
edolstra Aug 13, 2024
114d1a0
finishAll(): Propagate an arbitrary exception
edolstra Aug 14, 2024
f947b63
nix flake show: Make sure the visit() closure is still alive in case …
edolstra Aug 14, 2024
ceeb648
Introduce ValueType::nFailed
edolstra Aug 14, 2024
8b7d5b4
Make 'nix search --json' thread-safe
edolstra Aug 15, 2024
8020c0c
Merge remote-tracking branch 'origin/master' into multithreaded-eval
edolstra Aug 22, 2024
fee4e0f
Merge remote-tracking branch 'origin/master' into multithreaded-eval
edolstra Mar 11, 2025
8cf2af1
Formatting
edolstra Mar 11, 2025
d300e13
Use MAP_NORESERVE to avoid mmap failure on low-memory systems
edolstra Mar 11, 2025
8b374fd
Add eval-cores setting
edolstra Mar 12, 2025
c4ecaeb
Remove configure.ac
edolstra Mar 12, 2025
53356cf
Re-enable some tests
edolstra Mar 12, 2025
aa8025e
Tweak wording
edolstra Mar 12, 2025
250562c
Fix warning
edolstra Mar 12, 2025
50cf042
Fix UndefinedBehaviorSanitizer error passing a null Env
edolstra Mar 12, 2025
53f6b24
Merge remote-tracking branch 'origin/master' into multithreaded-eval
edolstra Mar 17, 2025
e6caa9d
Merge remote-tracking branch 'origin/master' into multithreaded-eval
edolstra Mar 31, 2025
ce255bd
Merge remote-tracking branch 'detsys/multithreaded-eval' into sync-2.29
edolstra May 18, 2025
33b8a1e
Merge remote-tracking branch 'detsys/detsys-main' into multithreaded-…
edolstra Jun 3, 2025
825a2af
GitSourceAccessor: Make thread-safe
edolstra Jun 4, 2025
02eb215
nix flake check: Make multi-threaded
edolstra Jun 4, 2025
9d6bfdb
Prevent double copy of nixpkgs source tree
edolstra Jun 5, 2025
9ac8710
RemoteStore: Increase default maxConnections
edolstra Jun 5, 2025
ccf88cb
Merge remote-tracking branch 'detsys/detsys-main' into multithreaded-…
edolstra Jun 17, 2025
3746889
Move code out of header
edolstra Jun 18, 2025
fbb59f8
Move Executor into EvalState
edolstra Jun 18, 2025
509db3d
Make printValueAsJSON() multi-threaded
edolstra Jun 23, 2025
f649f50
Run flake-regressions with eval-cores = 24
edolstra Jun 23, 2025
5421f5c
Merge remote-tracking branch 'detsys/detsys-main' into multithreaded-…
edolstra Jun 23, 2025
0e4ed54
Fix macOS build
edolstra Jun 23, 2025
a66d268
Merge remote-tracking branch 'detsys/detsys-main' into multithreaded-…
edolstra Jun 30, 2025
dbc9423
nix flake prefetch-inputs: Add
edolstra Jun 24, 2025
9a52b2f
flake-regressions: Use prefetching
edolstra Jun 30, 2025
1626e65
Move FlakeCommand into a header, allow separate registration of subco…
edolstra Jun 26, 2025
7ec889d
Don't set $PARALLEL with eval-cores
edolstra Jun 30, 2025
59a2818
EvalState: Make the counters atomic
edolstra Jul 3, 2025
7156b63
Merge remote-tracking branch 'detsys/detsys-main' into multithreaded-…
edolstra Jul 3, 2025
a66991a
Restore mkBlackhole() for testing
edolstra Jul 3, 2025
08bf730
Re-enable some tests
edolstra Jul 3, 2025
aedaffd
Drop NIX_SHOW_THREAD_STATS
edolstra Jul 3, 2025
1b88cfc
Implement SymbolTable::dump()
edolstra Jul 3, 2025
2a245fc
Remove debug lines
edolstra Jul 3, 2025
529e500
Merge remote-tracking branch 'detsys/detsys-main' into multithreaded-…
edolstra Jul 10, 2025
a1c5533
SymbolTable: Use boost::concurrent_flat_set
edolstra Jul 10, 2025
47f8b6e
Update multithreaded eval to work with the 16-byte Value representation
edolstra Jul 11, 2025
88cd30d
Formatting
edolstra Jul 14, 2025
fcd49c0
Merge remote-tracking branch 'detsys/detsys-main' into multithreaded-…
edolstra Jul 14, 2025
0c09c4c
Remove debug line
edolstra Jul 14, 2025
e0779b6
Remove unnecessary diff
edolstra Jul 14, 2025
84cfa2b
PosixSourceAccessor: Use concurrent_flat_map
edolstra Jul 15, 2025
fe99dcf
Remove unused variable
edolstra Jul 15, 2025
d90ec7d
showHelp(): Simplify
edolstra Jul 15, 2025
a61c4ec
Remove unused Pending struct
edolstra Jul 15, 2025
914baff
Clean up CallDepth merge issue
edolstra Jul 15, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 14 additions & 2 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,7 @@ jobs:
nix_config:
- "lazy-trees = true"
- "lazy-trees = false"
- "eval-cores = 24"
glob:
- "[0-d]*"
- "[e-l]*"
Expand All @@ -144,26 +145,37 @@ jobs:
with:
repository: DeterminateSystems/flake-regressions
path: flake-regressions
ref: prefetch
- name: Checkout flake-regressions-data
uses: actions/checkout@v4
with:
repository: DeterminateSystems/flake-regressions-data
path: flake-regressions/tests
- uses: DeterminateSystems/determinate-nix-action@main
- uses: DeterminateSystems/flakehub-cache-action@main
- env:
PARALLEL: "-P 50%"
- name: Run flake regression tests
env:
PARALLEL: ${{ !contains(matrix.nix_config, 'eval-cores') && '-P 50%' || '-P 1' }}
FLAKE_REGRESSION_GLOB: ${{ matrix.glob }}
NIX_CONFIG: ${{ matrix.nix_config }}
PREFETCH: "1"
GC_INITIAL_HEAP_SIZE: "32G"
run: |
set -x
echo "PARALLEL: $PARALLEL"
echo "NIX_CONFIG: $NIX_CONFIG"
if [ ! -z "${NSC_CACHE_PATH:-}" ]; then
mkdir -p "${NSC_CACHE_PATH}/nix/xdg-cache"
export XDG_CACHE_HOME="${NSC_CACHE_PATH}/nix/xdg-cache"
fi
nix build -L --out-link ./new-nix
export PATH=$(pwd)/new-nix/bin:$PATH

nix config show lazy-trees
nix config show eval-cores
lscpu
nproc

if ! flake-regressions/eval-all.sh; then
echo "Some failed, trying again"
printf "\n\n\n\n\n\n\n\n"
Expand Down
2 changes: 2 additions & 0 deletions src/libexpr-c/nix_api_value.cc
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,8 @@ ValueType nix_get_type(nix_c_context * context, const nix_value * value)
switch (v.type()) {
case nThunk:
return NIX_TYPE_THUNK;
case nFailed:
return NIX_TYPE_FAILED;
case nInt:
return NIX_TYPE_INT;
case nFloat:
Expand Down
3 changes: 2 additions & 1 deletion src/libexpr-c/nix_api_value.h
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,8 @@ typedef enum {
NIX_TYPE_ATTRS,
NIX_TYPE_LIST,
NIX_TYPE_FUNCTION,
NIX_TYPE_EXTERNAL
NIX_TYPE_EXTERNAL,
NIX_TYPE_FAILED,
} ValueType;

// forward declarations
Expand Down
7 changes: 4 additions & 3 deletions src/libexpr-tests/value/print.cc
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ using namespace testing;
struct ValuePrintingTests : LibExprTest
{
template<class... A>
void test(Value v, std::string_view expected, A... args)
void test(Value & v, std::string_view expected, A... args)
{
std::stringstream out;
v.print(state, out, args...);
Expand Down Expand Up @@ -730,9 +730,10 @@ TEST_F(ValuePrintingTests, ansiColorsAttrsElided)
vThree.mkInt(3);

builder.insert(state.symbols.create("three"), &vThree);
vAttrs.mkAttrs(builder.finish());
Value vAttrs2;
vAttrs2.mkAttrs(builder.finish());

test(vAttrs,
test(vAttrs2,
"{ one = " ANSI_CYAN "1" ANSI_NORMAL "; " ANSI_FAINT "«2 attributes elided»" ANSI_NORMAL " }",
PrintOptions {
.ansiColors = true,
Expand Down
1 change: 0 additions & 1 deletion src/libexpr-tests/value/value.cc
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ TEST_F(ValueTest, unsetValue)
{
Value unsetValue;
ASSERT_EQ(false, unsetValue.isValid());
ASSERT_EQ(nThunk, unsetValue.type(true));
ASSERT_DEATH(unsetValue.type(), "");
}

Expand Down
51 changes: 42 additions & 9 deletions src/libexpr/eval-gc.cc
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,40 @@ static void * oomHandler(size_t requested)
throw std::bad_alloc();
}

static size_t getFreeMem()
{
/* On Linux, use the `MemAvailable` or `MemFree` fields from
/proc/cpuinfo. */
# ifdef __linux__
{
std::unordered_map<std::string, std::string> fields;
for (auto & line :
tokenizeString<std::vector<std::string>>(readFile(std::filesystem::path("/proc/meminfo")), "\n")) {
auto colon = line.find(':');
if (colon == line.npos)
continue;
fields.emplace(line.substr(0, colon), trim(line.substr(colon + 1)));
}

auto i = fields.find("MemAvailable");
if (i == fields.end())
i = fields.find("MemFree");
if (i != fields.end()) {
auto kb = tokenizeString<std::vector<std::string>>(i->second, " ");
if (kb.size() == 2 && kb[1] == "kB")
return string2Int<size_t>(kb[0]).value_or(0) * 1024;
}
}
# endif

/* On non-Linux systems, conservatively assume that 25% of memory is free. */
long pageSize = sysconf(_SC_PAGESIZE);
long pages = sysconf(_SC_PHYS_PAGES);
if (pageSize != -1)
return (pageSize * pages) / 4;
return 0;
}

static inline void initGCReal()
{
/* Initialise the Boehm garbage collector. */
Expand All @@ -53,6 +87,8 @@ static inline void initGCReal()

GC_INIT();

GC_allow_register_threads();

/* Register valid displacements in case we are using alignment niches
for storing the type information. This way tagged pointers are considered
to be valid, even when they are not aligned. */
Expand All @@ -62,8 +98,8 @@ static inline void initGCReal()

GC_set_oom_fn(oomHandler);

/* Set the initial heap size to something fairly big (25% of
physical RAM, up to a maximum of 384 MiB) so that in most cases
/* Set the initial heap size to something fairly big (80% of
free RAM, up to a maximum of 8 GiB) so that in most cases
we don't need to garbage collect at all. (Collection has a
fairly significant overhead.) The heap size can be overridden
through libgc's GC_INITIAL_HEAP_SIZE environment variable. We
Expand All @@ -74,13 +110,10 @@ static inline void initGCReal()
if (!getEnv("GC_INITIAL_HEAP_SIZE")) {
size_t size = 32 * 1024 * 1024;
# if HAVE_SYSCONF && defined(_SC_PAGESIZE) && defined(_SC_PHYS_PAGES)
size_t maxSize = 384 * 1024 * 1024;
long pageSize = sysconf(_SC_PAGESIZE);
long pages = sysconf(_SC_PHYS_PAGES);
if (pageSize != -1)
size = (pageSize * pages) / 4; // 25% of RAM
if (size > maxSize)
size = maxSize;
size_t maxSize = 8ULL * 1024 * 1024 * 1024;
auto free = getFreeMem();
debug("free memory is %d bytes", free);
size = std::min((size_t) (free * 0.8), maxSize);
# endif
debug("setting initial heap size to %1% bytes", size);
GC_expand_hp(size);
Expand Down
Loading
Loading