Skip to content

Commit 0c342d0

Browse files
committed
implement BufferedEntrophySource to reduce syscalls
1 parent c255a8c commit 0c342d0

File tree

5 files changed

+91
-11
lines changed

5 files changed

+91
-11
lines changed

src/workerd/server/BUILD.bazel

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ wd_cc_binary(
7171
"//src/pyodide:pyodide_extra_capnp",
7272
"//src/rust/cxx-integration",
7373
"//src/workerd/util:autogate",
74+
"//src/workerd/util:entropy-source",
7475
"//src/workerd/util:perfetto",
7576
"@capnp-cpp//src/capnp:capnpc",
7677
],

src/workerd/server/workerd.c++

Lines changed: 2 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,9 @@
1616
#include <workerd/server/workerd-capnp-schema.embed.h>
1717
#include <workerd/server/workerd.capnp.h>
1818
#include <workerd/util/autogate.h>
19+
#include <workerd/util/entropy-source.h>
1920

2021
#include <fcntl.h>
21-
#include <openssl/rand.h>
2222
#include <sys/stat.h>
2323

2424
#include <capnp/dynamic.h>
@@ -94,15 +94,6 @@ __lsan_default_suppressions() {
9494
}
9595
#endif
9696

97-
// =======================================================================================
98-
99-
class EntropySourceImpl: public kj::EntropySource {
100-
public:
101-
void generate(kj::ArrayPtr<kj::byte> buffer) override {
102-
KJ_ASSERT(RAND_bytes(buffer.begin(), buffer.size()) == 1);
103-
}
104-
};
105-
10697
// =======================================================================================
10798
// Some generic CLI helpers so that we can throw exceptions rather than return
10899
// kj::MainBuilder::Validity. Honestly I do not know how people put up with patterns like
@@ -1475,7 +1466,7 @@ class CliMain final: public SchemaFileImpl::ErrorReporter {
14751466
kj::Own<kj::Filesystem> fs = kj::newDiskFilesystem();
14761467
kj::AsyncIoContext io = kj::setupAsyncIo();
14771468
NetworkWithLoopback network{io.provider->getNetwork(), *io.provider};
1478-
EntropySourceImpl entropySource;
1469+
BufferedEntropySource entropySource;
14791470

14801471
kj::Vector<kj::Path> importPath;
14811472
capnp::SchemaParser schemaParser;

src/workerd/util/BUILD.bazel

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,20 @@ wd_cc_library(
147147
],
148148
)
149149

150+
wd_cc_library(
151+
name = "entropy-source",
152+
srcs = ["entropy-source.c++"],
153+
hdrs = ["entropy-source.h"],
154+
implementation_deps = [
155+
"@ssl",
156+
],
157+
visibility = ["//visibility:public"],
158+
deps = [
159+
"@capnp-cpp//src/kj",
160+
"@capnp-cpp//src/kj/compat:kj-http",
161+
],
162+
)
163+
150164
wd_cc_library(
151165
name = "test",
152166
hdrs = ["test.h"],
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
// Copyright (c) 2017-2022 Cloudflare, Inc.
2+
// Licensed under the Apache 2.0 license found in the LICENSE file or at:
3+
// https://opensource.org/licenses/Apache-2.0
4+
5+
#include "entropy-source.h"
6+
7+
namespace workerd {
8+
9+
void BufferedEntropySource::generate(kj::ArrayPtr<kj::byte> buffer) {
10+
thread_local BufferState state;
11+
12+
size_t offset = 0;
13+
while (offset < buffer.size()) {
14+
if (state.position >= BUFFER_SIZE) {
15+
refillBuffer(state);
16+
}
17+
18+
size_t available = BUFFER_SIZE - state.position;
19+
size_t toCopy = kj::min(available, buffer.size() - offset);
20+
memcpy(buffer.begin() + offset, state.buffer + state.position, toCopy);
21+
state.position += toCopy;
22+
offset += toCopy;
23+
}
24+
}
25+
26+
void BufferedEntropySource::refillBuffer(BufferState& state) {
27+
KJ_ASSERT(
28+
RAND_bytes(state.buffer, BUFFER_SIZE) == 1, "RAND_bytes failed to generate random data");
29+
state.position = 0;
30+
}
31+
32+
} // namespace workerd

src/workerd/util/entropy-source.h

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
// Copyright (c) 2017-2022 Cloudflare, Inc.
2+
// Licensed under the Apache 2.0 license found in the LICENSE file or at:
3+
// https://opensource.org/licenses/Apache-2.0
4+
5+
#pragma once
6+
7+
#include <openssl/rand.h>
8+
9+
#include <kj/common.h>
10+
#include <kj/compat/http.h>
11+
#include <kj/debug.h>
12+
13+
#include <cstring>
14+
15+
namespace workerd {
16+
17+
// Buffered entropy source that maintains a thread-local pool of random bytes to reduce
18+
// RAND_bytes syscall overhead. This is especially beneficial on macOS where RAND_bytes
19+
// calls are expensive.
20+
//
21+
// The buffer is refilled in bulk (4KB at a time) when exhausted, amortizing syscall
22+
// overhead.
23+
class BufferedEntropySource final: public kj::EntropySource {
24+
public:
25+
BufferedEntropySource() = default;
26+
~BufferedEntropySource() noexcept(false) = default;
27+
KJ_DISALLOW_COPY_AND_MOVE(BufferedEntropySource);
28+
29+
void generate(kj::ArrayPtr<kj::byte> buffer) override;
30+
31+
private:
32+
static constexpr size_t BUFFER_SIZE = 4096;
33+
34+
struct BufferState {
35+
alignas(64) kj::byte buffer[BUFFER_SIZE]; // Cache-line aligned for performance
36+
size_t position = BUFFER_SIZE; // Start at end to trigger initial fill
37+
};
38+
39+
static void refillBuffer(BufferState& state);
40+
};
41+
42+
} // namespace workerd

0 commit comments

Comments
 (0)