Skip to content

Commit 4335d68

Browse files
committed
chunked eval
1 parent fbacee3 commit 4335d68

File tree

2 files changed

+171
-49
lines changed

2 files changed

+171
-49
lines changed
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
/*
2+
Invocation:
3+
Invocation; note that the number of processes spawned is four times
4+
the number of cores -- this helps in two ways:
5+
1. Keeping cores busy while I/O operations are in flight
6+
2. Since the amount of time needed for the jobs is *not* balanced
7+
this minimizes the "tail latency" for the very last job to finish
8+
(on one core) by making the job size smaller.
9+
*/
10+
# see pkgs/top-level/nohydra
11+
{
12+
checkMeta,
13+
includeBroken ? true,
14+
path,
15+
systems,
16+
localSystem,
17+
myChunk,
18+
numChunks,
19+
attrPathFile,
20+
}: let
21+
pkgs = import <nixpkgs> {
22+
system = localSystem;
23+
};
24+
inherit (pkgs) lib;
25+
26+
attrPaths = builtins.fromJSON (builtins.readFile attrPathFile);
27+
chunkSize = (lib.length attrPaths) / numChunks;
28+
myPaths = let
29+
dropped = lib.drop (chunkSize * myChunk) attrPaths;
30+
in
31+
if myChunk == numChunks - 1
32+
then dropped
33+
else lib.take chunkSize dropped;
34+
35+
unfiltered = import (path + "/pkgs/top-level/release-outpaths.nix") {
36+
inherit
37+
checkMeta
38+
path
39+
includeBroken
40+
systems
41+
;
42+
};
43+
44+
f = i: m: a:
45+
lib.mapAttrs (
46+
name: values:
47+
if a ? ${name}
48+
then
49+
if lib.any (value: lib.length value <= i + 1) values
50+
then a.${name}
51+
else f (i + 1) values a.${name}
52+
else null
53+
) (lib.groupBy (a: lib.elemAt a i) m);
54+
55+
filtered = f 0 myPaths unfiltered;
56+
57+
recurseEverywhere = val:
58+
if lib.isDerivation val || !(lib.isAttrs val)
59+
then val
60+
else (builtins.mapAttrs (_: v: recurseEverywhere v) val) // {recurseForDerivations = true;};
61+
in
62+
recurseEverywhere filtered

nixpkgs_review/review.py

Lines changed: 109 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
from .github import GithubClient
1818
from .nix import Attr, nix_build, nix_eval, nix_shell
1919
from .report import Report
20-
from .utils import System, current_system, info, sh, system_order_key, warn
20+
from .utils import ROOT, System, current_system, info, sh, system_order_key, warn
2121

2222
# keep up to date with `supportedPlatforms`
2323
# https://github.com/NixOS/ofborg/blob/cf2c6712bd7342406e799110e7cd465aa250cdca/ofborg/src/outpaths.nix#L12
@@ -201,8 +201,10 @@ def build_commit(
201201
# TODO: nix-eval-jobs ?
202202
base_packages: dict[System, list[Package]] = list_packages(
203203
self.builddir.nix_path,
204-
self.systems,
205-
self.allow,
204+
systems=self.systems,
205+
local_system=self.local_system,
206+
allow=self.allow,
207+
nixpkgs_path=str(self.builddir.worktree_dir),
206208
n_threads=self.num_parallel_evals,
207209
)
208210

@@ -216,8 +218,10 @@ def build_commit(
216218
# TODO: nix-eval-jobs ?
217219
merged_packages: dict[System, list[Package]] = list_packages(
218220
self.builddir.nix_path,
219-
self.systems,
220-
self.allow,
221+
systems=self.systems,
222+
local_system=self.local_system,
223+
allow=self.allow,
224+
nixpkgs_path=str(self.builddir.worktree_dir),
221225
n_threads=self.num_parallel_evals,
222226
check_meta=True,
223227
)
@@ -427,68 +431,124 @@ def parse_packages_xml(stdout: IO[str]) -> list[Package]:
427431

428432

429433
def _list_packages_system(
430-
system: System,
431-
nix_path: str,
434+
chunk_id: int,
435+
num_chunks: int,
436+
systems: set[System],
437+
local_system: System,
438+
nixpkgs_path: str,
439+
paths_json_filename: str,
440+
paths_filename: str,
432441
allow: AllowedFeatures,
433442
check_meta: bool = False,
434-
) -> list[Package]:
435-
cmd = [
443+
) -> list[str]:
444+
cmd: list[str] = [
436445
"nix-env",
437-
"--extra-experimental-features",
438-
"" if allow.url_literals else "no-url-literals",
439-
"--option",
440-
"system",
441-
system,
442-
"-f",
443-
"<nixpkgs>",
444-
"--nix-path",
445-
nix_path,
446446
"-qaP",
447-
"--xml",
447+
"--no-name",
448448
"--out-path",
449449
"--show-trace",
450-
"--allow-import-from-derivation"
451-
if allow.ifd
452-
else "--no-allow-import-from-derivation",
453450
]
454-
if check_meta:
455-
cmd.append("--meta")
451+
cmd.extend(["-f", str(ROOT.joinpath("nix/parallel-eval.nix"))])
452+
453+
cmd.extend(
454+
["--arg", "systems", f"[{", ".join([f'"{system}"' for system in systems])}]"]
455+
)
456+
cmd.extend(["--arg", "checkMeta", "true"])
457+
cmd.extend(["--arg", "includeBroken", "true"])
458+
cmd.extend(["--argstr", "localSystem", local_system])
459+
cmd.extend(["--arg", "attrPathFile", paths_json_filename])
460+
cmd.extend(["--arg", "path", nixpkgs_path])
461+
cmd.extend(["--arg", "numChunks", str(num_chunks)])
462+
cmd.extend(["--arg", "myChunk", str(chunk_id)])
463+
464+
# cmd.extend([">", paths_filename])
465+
456466
info("$ " + " ".join(cmd))
457-
with tempfile.NamedTemporaryFile(mode="w") as tmp:
458-
res = subprocess.run(cmd, stdout=tmp)
459-
if res.returncode != 0:
460-
raise NixpkgsReviewError(
461-
f"Failed to list packages: nix-env failed with exit code {res.returncode}"
462-
)
463-
tmp.flush()
464-
with open(tmp.name, encoding="utf-8") as f:
465-
return parse_packages_xml(f)
467+
res = subprocess.run(
468+
cmd,
469+
check=True,
470+
stdout=subprocess.PIPE,
471+
text=True,
472+
)
473+
if res.returncode != 0:
474+
raise NixpkgsReviewError(
475+
f"Failed to list packages: nix-env failed with exit code {res.returncode}"
476+
)
477+
results: list[str] = []
478+
for line in res.stdout.split("\n"):
479+
# <package_path>.<system> (python312Packages.numpy.x86_64-linux)
480+
results.append(line.split()[0].strip())
481+
482+
return results
466483

467484

468485
def list_packages(
469486
nix_path: str,
470487
systems: set[System],
488+
local_system: System,
471489
allow: AllowedFeatures,
490+
nixpkgs_path: str,
472491
n_threads: int,
473492
check_meta: bool = False,
474493
) -> dict[System, list[Package]]:
475-
results: dict[System, list[Package]] = {}
476-
with concurrent.futures.ThreadPoolExecutor(max_workers=n_threads) as executor:
477-
future_to_system = {
478-
executor.submit(
479-
_list_packages_system,
480-
system=system,
481-
nix_path=nix_path,
482-
allow=allow,
483-
check_meta=check_meta,
484-
): system
485-
for system in systems
486-
}
487-
for future in concurrent.futures.as_completed(future_to_system):
488-
system = future_to_system[future]
489-
results[system] = future.result()
494+
with tempfile.TemporaryDirectory() as temp_dir:
495+
paths_json_filename: str = os.path.join(temp_dir, "paths.json")
496+
with open(paths_json_filename, mode="w") as paths_json:
497+
subprocess.run(
498+
args=[
499+
"nix-instantiate",
500+
"--eval",
501+
"--strict",
502+
"--json",
503+
"--arg",
504+
"enableWarnings",
505+
"false",
506+
f"{nixpkgs_path}/pkgs/top-level/release-attrpaths-superset.nix",
507+
"-A",
508+
"paths",
509+
],
510+
stdout=paths_json,
511+
stderr=subprocess.DEVNULL,
512+
check=True,
513+
)
490514

491-
return results
515+
paths_filename: str = os.path.join(temp_dir, "paths")
516+
num_chunks: int = 4 * n_threads
517+
results: dict[System, list[Package]] = {system: [] for system in systems}
518+
with concurrent.futures.ThreadPoolExecutor(max_workers=n_threads) as executor:
519+
futures = [
520+
executor.submit(
521+
_list_packages_system,
522+
chunk_id=chunk_id,
523+
systems=systems,
524+
# nix_path=nix_path,
525+
local_system=local_system,
526+
nixpkgs_path=nixpkgs_path,
527+
paths_json_filename=paths_json_filename,
528+
paths_filename=paths_filename,
529+
num_chunks=num_chunks,
530+
allow=allow,
531+
check_meta=check_meta,
532+
)
533+
for chunk_id in range(num_chunks)
534+
]
535+
for future in concurrent.futures.as_completed(futures):
536+
for result in future.result():
537+
# result = "python312Packages.numpy.x86_64-linux"
538+
539+
# ["python312Packages", "numpy", "x86_64-linux"]
540+
splitted_result: list[str] = result.split(".")
541+
542+
# "x86_64-linux"
543+
system = splitted_result.pop()
544+
545+
# "python312Packages.numpy"
546+
path: str = ".".join(splitted_result)
547+
548+
# TODO: create a Package object
549+
results[system].append(path)
550+
551+
return results
492552

493553

494554
def package_attrs(

0 commit comments

Comments
 (0)