Skip to content

Commit a583a2b

Browse files
committed
Run the daemon worker on the same CPU as the client
On a system with multiple CPUs, running Nix operations through the daemon is significantly slower than "direct" mode: $ NIX_REMOTE= nix-instantiate '<nixos>' -A system real 0m0.974s user 0m0.875s sys 0m0.088s $ NIX_REMOTE=daemon nix-instantiate '<nixos>' -A system real 0m2.118s user 0m1.463s sys 0m0.218s The main reason seems to be that the client and the worker get moved to a different CPU after every call to the worker. This patch adds a hack to lock them to the same CPU. With this, the overhead of going through the daemon is very small: $ NIX_REMOTE=daemon nix-instantiate '<nixos>' -A system real 0m1.074s user 0m0.809s sys 0m0.098s
1 parent 263d668 commit a583a2b

File tree

9 files changed

+92
-4
lines changed

9 files changed

+92
-4
lines changed

configure.ac

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,10 @@ AC_CHECK_HEADERS([sys/mount.h], [], [],
127127
AC_CHECK_FUNCS([lutimes])
128128

129129

130+
# Check for sched_setaffinity.
131+
AC_CHECK_FUNCS([sched_setaffinity])
132+
133+
130134
# Check whether the store optimiser can optimise symlinks.
131135
AC_MSG_CHECKING([whether it is possible to create a link to a symlink])
132136
ln -s bla tmp_link

src/libstore/build.cc

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
#include "local-store.hh"
88
#include "util.hh"
99
#include "archive.hh"
10+
#include "affinity.hh"
1011

1112
#include <map>
1213
#include <sstream>
@@ -366,6 +367,8 @@ void Goal::trace(const format & f)
366367
/* Common initialisation performed in child processes. */
367368
static void commonChildInit(Pipe & logPipe)
368369
{
370+
restoreAffinity();
371+
369372
/* Put the child in a separate session (and thus a separate
370373
process group) so that it has no controlling terminal (meaning
371374
that e.g. ssh cannot open /dev/tty) and it doesn't receive
@@ -568,6 +571,7 @@ static void runSetuidHelper(const string & command,
568571
args.push_back(0);
569572

570573
restoreSIGPIPE();
574+
restoreAffinity();
571575

572576
execve(program.c_str(), (char * *) &args[0], 0);
573577
throw SysError(format("executing `%1%'") % program);

src/libstore/local-store.cc

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
#include "pathlocks.hh"
66
#include "worker-protocol.hh"
77
#include "derivations.hh"
8+
#include "affinity.hh"
89

910
#include <iostream>
1011
#include <algorithm>
@@ -1021,6 +1022,7 @@ void LocalStore::startSubstituter(const Path & substituter, RunningSubstituter &
10211022

10221023
case 0: /* child */
10231024
try {
1025+
restoreAffinity();
10241026
if (dup2(toPipe.readSide, STDIN_FILENO) == -1)
10251027
throw SysError("dupping stdin");
10261028
if (dup2(fromPipe.writeSide, STDOUT_FILENO) == -1)

src/libstore/remote-store.cc

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
#include "remote-store.hh"
44
#include "worker-protocol.hh"
55
#include "archive.hh"
6+
#include "affinity.hh"
67
#include "globals.hh"
78

89
#include <sys/types.h>
@@ -15,7 +16,6 @@
1516
#include <unistd.h>
1617
#include <cstring>
1718

18-
1919
namespace nix {
2020

2121

@@ -71,8 +71,19 @@ void RemoteStore::openConnection(bool reserveSpace)
7171
if (GET_PROTOCOL_MAJOR(daemonVersion) != GET_PROTOCOL_MAJOR(PROTOCOL_VERSION))
7272
throw Error("Nix daemon protocol version not supported");
7373
writeInt(PROTOCOL_VERSION, to);
74+
75+
if (GET_PROTOCOL_MINOR(daemonVersion) >= 14) {
76+
int cpu = lockToCurrentCPU();
77+
if (cpu != -1) {
78+
writeInt(1, to);
79+
writeInt(cpu, to);
80+
} else
81+
writeInt(0, to);
82+
}
83+
7484
if (GET_PROTOCOL_MINOR(daemonVersion) >= 11)
7585
writeInt(reserveSpace, to);
86+
7687
processStderr();
7788
}
7889
catch (Error & e) {

src/libstore/worker-protocol.hh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ namespace nix {
66
#define WORKER_MAGIC_1 0x6e697863
77
#define WORKER_MAGIC_2 0x6478696f
88

9-
#define PROTOCOL_VERSION 0x10d
9+
#define PROTOCOL_VERSION 0x10e
1010
#define GET_PROTOCOL_MAJOR(x) ((x) & 0xff00)
1111
#define GET_PROTOCOL_MINOR(x) ((x) & 0x00ff)
1212

src/libutil/Makefile.am

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
pkglib_LTLIBRARIES = libutil.la
22

33
libutil_la_SOURCES = util.cc hash.cc serialise.cc \
4-
archive.cc xml-writer.cc
4+
archive.cc xml-writer.cc affinity.cc
55

66
libutil_la_LIBADD = ../boost/format/libformat.la
77

88
pkginclude_HEADERS = util.hh hash.hh serialise.hh \
9-
archive.hh xml-writer.hh types.hh
9+
archive.hh xml-writer.hh types.hh affinity.hh
1010

1111
if !HAVE_OPENSSL
1212
libutil_la_SOURCES += \

src/libutil/affinity.cc

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
#include "types.hh"
2+
#include "util.hh"
3+
#include "affinity.hh"
4+
5+
#if HAVE_SCHED_H
6+
#include <sched.h>
7+
#endif
8+
9+
namespace nix {
10+
11+
12+
static bool didSaveAffinity = false;
13+
static cpu_set_t savedAffinity;
14+
15+
16+
void setAffinityTo(int cpu)
17+
{
18+
#if HAVE_SCHED_SETAFFINITY
19+
if (sched_getaffinity(0, sizeof(cpu_set_t), &savedAffinity) == -1) return;
20+
didSaveAffinity = true;
21+
printMsg(lvlDebug, format("locking this thread to CPU %1%") % cpu);
22+
cpu_set_t newAffinity;
23+
CPU_ZERO(&newAffinity);
24+
CPU_SET(cpu, &newAffinity);
25+
if (sched_setaffinity(0, sizeof(cpu_set_t), &newAffinity) == -1)
26+
printMsg(lvlError, format("failed to lock thread to CPU %1%") % cpu);
27+
#endif
28+
}
29+
30+
31+
int lockToCurrentCPU()
32+
{
33+
#if HAVE_SCHED_SETAFFINITY
34+
if (getEnv("NIX_AFFINITY_HACK", "1") == "1") {
35+
int cpu = sched_getcpu();
36+
if (cpu != -1) setAffinityTo(cpu);
37+
return cpu;
38+
}
39+
#endif
40+
return -1;
41+
}
42+
43+
44+
void restoreAffinity()
45+
{
46+
#if HAVE_SCHED_SETAFFINITY
47+
if (!didSaveAffinity) return;
48+
if (sched_setaffinity(0, sizeof(cpu_set_t), &savedAffinity) == -1)
49+
printMsg(lvlError, "failed to restore affinity %1%");
50+
#endif
51+
}
52+
53+
54+
}

src/libutil/affinity.hh

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
#pragma once
2+
3+
namespace nix {
4+
5+
void setAffinityTo(int cpu);
6+
int lockToCurrentCPU();
7+
void restoreAffinity();
8+
9+
}

src/nix-daemon/nix-daemon.cc

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
#include "serialise.hh"
55
#include "worker-protocol.hh"
66
#include "archive.hh"
7+
#include "affinity.hh"
78
#include "globals.hh"
89

910
#include <cstring>
@@ -671,6 +672,9 @@ static void processConnection(bool trusted)
671672
to.flush();
672673
unsigned int clientVersion = readInt(from);
673674

675+
if (GET_PROTOCOL_MINOR(clientVersion) >= 14 && readInt(from))
676+
setAffinityTo(readInt(from));
677+
674678
bool reserveSpace = true;
675679
if (GET_PROTOCOL_MINOR(clientVersion) >= 11)
676680
reserveSpace = readInt(from) != 0;

0 commit comments

Comments
 (0)