Skip to content

Commit 27a2b98

Browse files
Cargo zigbuild (#78)
Switch allocator and libc
1 parent 1a3453c commit 27a2b98

File tree

15 files changed

+166
-114
lines changed

15 files changed

+166
-114
lines changed

.cargo/config.toml

+23
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,26 @@
1+
[target.aarch64-unknown-linux-musl]
2+
rustflags = [
3+
"-Zpre-link-args=-target aarch64-linux-musl",
4+
"-Ctarget-feature=+lse,+crt-static",
5+
"-Ctarget-cpu=neoverse-n1",
6+
]
7+
linker = "./zigcc"
8+
ar = "zig ar"
9+
10+
[target.x86_64-unknown-linux-musl]
11+
rustflags = [
12+
"-Zpre-link-args=-target x86_64-linux-musl",
13+
"-Ctarget-feature=+crt-static",
14+
]
15+
linker = "./zigcc"
16+
ar = "zig ar"
17+
118
[unstable]
219
build-std = ["core", "compiler_builtins", "alloc", "std", "panic_abort"]
320
build-std-features = ["panic_immediate_abort"]
21+
22+
[env]
23+
CC_aarch64_unknown_linux_musl = "zig cc -target aarch64-linux-musl"
24+
CXX_aarch64_unknown_linux_musl = "zig cc -target aarch64-linux-musl"
25+
CC_x86_64_unknown_linux_musl = "zig cc -target x86_64-linux-musl"
26+
CXX_x86_64_unknown_linux_musl = "zig cc -target x86_64-linux-musl"

.github/workflows/build.yml

+1-4
Original file line numberDiff line numberDiff line change
@@ -79,15 +79,12 @@ jobs:
7979
CARGO_TARGET_AARCH64_UNKNOWN_LINUX_MUSL_RUNNER: qemu-aarch64
8080
run: |
8181
sudo apt-get install -y \
82-
libc6-arm64-cross \
83-
libc6-dev-arm64-cross \
84-
crossbuild-essential-arm64 \
8582
qemu-system-arm \
8683
qemu-efi-aarch64 \
8784
qemu-utils \
8885
qemu-user
8986
90-
make CURRENT_TARGET=aarch64-unknown-linux-gnu test-ci
87+
make CURRENT_TARGET=aarch64-unknown-linux-musl test-ci
9188
- name: Build Linux binaries
9289
if: inputs.release && inputs.platform == 'linux'
9390
run: |

CHANGELOG.md

+4-39
Original file line numberDiff line numberDiff line change
@@ -1,39 +1,4 @@
1-
* Over 2x faster JSON parsing & stringify:
2-
3-
Size 2580:
4-
parse: 49.333µs vs. 89.792µs
5-
stringify: 31µs vs. 92.375µs
6-
Size 51701:
7-
parse: 494.458µs vs. 1.160125ms
8-
stringify: 427.791µs vs. 1.097625ms
9-
Size 517001:
10-
parse: 4.932875ms vs. 11.911375ms
11-
stringify: 3.925875ms vs. 10.853125ms
12-
Size 5170901:
13-
parse: 56.0855ms vs. 126.783833ms
14-
stringify: 38.671083ms vs. 107.312875ms
15-
Size 51718901:
16-
parse: 731.7205ms vs. 1.285825541s
17-
stringify: 395.82225ms vs. 1.39267225s
18-
Size 517288901:
19-
parse: 6.886183416s vs. 14.985707583s
20-
stringify: 3.957781167s vs. 10.885577917s
21-
22-
* 7x faster integer and float toString():
23-
24-
Benchmark 1: target/release/llrt
25-
Time (mean ± σ): 1.568 s ± 0.016 s [User: 1.555 s, System: 0.007 s]
26-
Range (min … max): 1.558 s … 1.610 s 10 runs
27-
28-
Benchmark 2: target/release/llrt-next
29-
Time (mean ± σ): 205.1 ms ± 3.1 ms [User: 196.9 ms, System: 2.2 ms]
30-
Range (min … max): 200.0 ms … 213.1 ms 14 runs
31-
32-
Summary
33-
target/release/llrt-next ran
34-
7.65 ± 0.14 times faster than target/release/llrt
35-
36-
* Improved logging:
37-
* LLRT now supports [advanced logging controls](https://aws.amazon.com/blogs/compute/introducing-advanced-logging-controls-for-aws-lambda-functions/) for AWS Lambda
38-
* `requestId` is now captured and outputted with logging
39-
* Console has some performance improvements by reusing String and avoiding allocations
1+
* Use MiMalloc allocator for improved performance.
2+
* Switch libc from gnu to musl
3+
* Bug fixes
4+
* Dependency upgrades

Cargo.lock

+12-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

+4-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "llrt"
3-
version = "0.1.4-beta"
3+
version = "0.1.5-beta"
44
edition = "2021"
55

66
[features]
@@ -19,6 +19,7 @@ md-5 = { version = "0.10.6", features = ["asm"] }
1919
rquickjs = { git = "https://github.com/DelSkayn/rquickjs", rev = "60696e88dfb903d8f5cd81b2667fb3f64f9e9f67", features = [
2020
"full-async",
2121
"parallel",
22+
"rust-alloc",
2223
], default-features = false }
2324
tokio = { version = "1", features = ["full"] }
2425
tracing = { version = "0.1.40", features = ["log"] }
@@ -66,10 +67,12 @@ tokio-rustls = { version = "0.25.0", features = [
6667
"ring",
6768
], default-features = false }
6869
ring = "0.17.7"
70+
libmimalloc-sys = "0.1.35"
6971

7072
[build-dependencies]
7173
rquickjs = { git = "https://github.com/DelSkayn/rquickjs", rev = "60696e88dfb903d8f5cd81b2667fb3f64f9e9f67", features = [
7274
"full-async",
75+
"rust-alloc",
7376
], default-features = false }
7477
relative-path = "1.9.0"
7578
tokio = { version = "1", features = ["full"] }

Makefile

+10-35
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,16 @@
1-
TARGET_linux_x86_64 = x86_64-unknown-linux-gnu
2-
TARGET_linux_arm64 = aarch64-unknown-linux-gnu
1+
TARGET_linux_x86_64 = x86_64-unknown-linux-musl
2+
TARGET_linux_arm64 = aarch64-unknown-linux-musl
33
TARGET_darwin_x86_64 = x86_64-apple-darwin
44
TARGET_darwin_arm64 = aarch64-apple-darwin
55
RUST_VERSION = nightly
66
TOOLCHAIN = +$(RUST_VERSION)
77
BUILD_ARG = $(TOOLCHAIN) build -r
88
BUILD_DIR = ./target/release
99
BUNDLE_DIR = bundle
10-
ZSTD_LIB_ARGS = -j lib-nomt CC="$(CURDIR)/zigcc -s -O3 -flto" AR="zig ar" UNAME=Linux ZSTD_LIB_COMPRESSION=0 ZSTD_LIB_DICTBUILDER=0
10+
ZSTD_LIB_ARGS = -j lib-nomt UNAME=Linux ZSTD_LIB_COMPRESSION=0 ZSTD_LIB_DICTBUILDER=0 AR="zig ar"
11+
ZSTD_LIB_CC_ARGS = -s -O3 -flto
12+
ZSTD_LIB_CC_arm64 = CC="zig cc -target aarch64-linux-musl $(ZSTD_LIB_CC_ARGS)"
13+
ZSTD_LIB_CC_x64 = CC="zig cc -target aarch64-linux-musl $(ZSTD_LIB_CC_ARGS)"
1114

1215
TS_SOURCES = $(wildcard src/js/*.ts) $(wildcard src/js/@llrt/*.ts) $(wildcard tests/*.ts)
1316
STD_JS_FILE = $(BUNDLE_DIR)/@llrt/std.js
@@ -27,29 +30,8 @@ else
2730
ARCH := $(shell uname -m)
2831
endif
2932

30-
ifeq ($(DETECTED_OS),darwin)
31-
export AR = $(CURDIR)/zigar
32-
export CC_aarch64_unknown_linux_gnu = $(CURDIR)/zigcc
33-
export CCX_aarch64_unknown_linux_gnu = $(CURDIR)/zigcc
34-
export CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_LINKER = $(CURDIR)/zigcc
35-
export CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_RUSTFLAGS = -Ctarget-feature=+crt-static,+lse -Ctarget-cpu=neoverse-n1
36-
37-
export CC_x86_64_unknown_linux_gnu = $(CURDIR)/zigcc
38-
export CXX_x86_64_unknown_linux_gnu = $(CURDIR)/zigcc
39-
export CARGO_TARGET_X86_64_UNKNOWN_LINUX_GNU_LINKER = $(CURDIR)/zigcc
40-
else ifeq ($(DETECTED_OS),linux)
41-
export CARGO_TARGET_X86_64_UNKNOWN_LINUX_GNU_RUSTFLAGS = -Ctarget-feature=+crt-static
42-
export CARGO_TARGET_X86_64_UNKNOWN_LINUX_GNU_LINKER = x86_64-linux-gnu-gcc
43-
44-
export CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_RUSTFLAGS = -Clink-arg=-Wl,--allow-multiple-definition -Ctarget-feature=+crt-static,+lse -Ctarget-cpu=neoverse-n1
45-
export CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_LINKER = aarch64-linux-gnu-gcc
46-
47-
endif
48-
4933
CURRENT_TARGET ?= $(TARGET_$(DETECTED_OS)_$(ARCH))
5034

51-
export COMPILE_TARGET = $(CURRENT_TARGET)
52-
5335
lambda-all: libs $(RELEASE_ZIPS)
5436
release-all: | lambda-all llrt-linux-x64.zip llrt-linux-arm64.zip llrt-darwin-x64.zip llrt-darwin-arm64.zip
5537
release: llrt-$(DETECTED_OS)-$(ARCH).zip
@@ -73,8 +55,8 @@ llrt-linux-arm64.zip: | clean-js js
7355
zip -j $@ target/$(TARGET_linux_arm64)/release/llrt
7456

7557
define release_template
76-
release-${1}: js
77-
COMPILE_TARGET=$$(TARGET_linux_$$(RELEASE_ARCH_NAME_${1})) cargo $$(BUILD_ARG) --target $$(TARGET_linux_$$(RELEASE_ARCH_NAME_${1})) --features lambda -vv
58+
release-${1}: | clean-js js
59+
cargo $$(BUILD_ARG) --target $$(TARGET_linux_$$(RELEASE_ARCH_NAME_${1})) --features lambda -vv
7860
./pack target/$$(TARGET_linux_$$(RELEASE_ARCH_NAME_${1}))/release/llrt target/$$(TARGET_linux_$$(RELEASE_ARCH_NAME_${1}))/release/bootstrap
7961
@rm -rf llrt-lambda-${1}.zip
8062
zip -j llrt-lambda-${1}.zip target/$$(TARGET_linux_$$(RELEASE_ARCH_NAME_${1}))/release/bootstrap
@@ -132,13 +114,6 @@ run: export _HANDLER = index.handler
132114
run: | clean-js js
133115
cargo run -r -vv
134116

135-
run-release: export _HANDLER = fixtures/local.handler
136-
run-release: js
137-
cargo build
138-
time target/release/llrt
139-
time target/release/llrt
140-
time target/release/llrt
141-
142117
run-ssr: export AWS_LAMBDA_RUNTIME_API = localhost:3000
143118
run-ssr: export TABLE_NAME=quickjs-table
144119
run-ssr: export AWS_REGION = us-east-1
@@ -175,13 +150,13 @@ lib/zstd.h:
175150
lib/arm64/libzstd.a:
176151
mkdir -p $(dir $@)
177152
rm -f zstd/lib/-.o
178-
cd zstd/lib && make clean && COMPILE_TARGET="aarch64-unknown-linux-musl" make $(ZSTD_LIB_ARGS)
153+
cd zstd/lib && make clean && make $(ZSTD_LIB_ARGS) $(ZSTD_LIB_CC_arm64)
179154
cp zstd/lib/libzstd.a $@
180155

181156
lib/x64/libzstd.a:
182157
mkdir -p $(dir $@)
183158
rm -f zstd/lib/-.o
184-
cd zstd/lib && make clean && COMPILE_TARGET="x86_64-unknown-linux-musl" make $(ZSTD_LIB_ARGS)
159+
cd zstd/lib && make clean && make $(ZSTD_LIB_ARGS) $(ZSTD_LIB_CC_x64)
185160
cp zstd/lib/libzstd.a $@
186161

187162
bench:

README.md

+11-10
Original file line numberDiff line numberDiff line change
@@ -178,6 +178,11 @@ B) Without the JIT overhead, LLRT conserves both CPU and memory resources that c
178178

179179
## Building from source
180180

181+
Clone code and cd to directory
182+
183+
git clone git@github.com:awslabs/llrt.git --recursive
184+
cd llrt
185+
181186
Install rust
182187

183188
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | bash -s -- -y
@@ -189,17 +194,9 @@ Install dependencies
189194
brew install zig make zstd node corepack
190195

191196
# Ubuntu
192-
sudo apt -y install make zstd gcc \
193-
libc6-arm64-cross \
194-
libc6-dev-arm64-cross \
195-
crossbuild-essential-arm64
197+
sudo apt -y install make zstd
196198
sudo snap install zig --classic --beta
197199

198-
Clone code and cd to directory
199-
200-
git clone git@github.com:awslabs/llrt.git --recursive
201-
cd llrt
202-
203200
Install Node.js packages
204201

205202
corepack enable
@@ -209,12 +206,16 @@ Install generate libs and setup rust targets & toolchains
209206

210207
make stdlib && make libs
211208

212-
Build release
209+
Build release for Lambda
213210

214211
make release-arm64
215212
# or for x86, use
216213
make release-x86
217214

215+
Optionally build for your local machine (Mac or Linux)
216+
217+
make release
218+
218219
You should now have a `llrt-lambda-arm64.zip` or `llrt-lambda-x86.zip`. You can manually upload this as a Lambda layer or use it via your Infrastructure-as-code pipeline
219220

220221
## Running Lambda emulator

example/functions/src/ssr.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -147,7 +147,8 @@ const assetResponse = async (path: string) => {
147147
};
148148

149149
export const handler = async (event: any) => {
150-
const { method, path: eventPath } = event?.requestContext?.http || {};
150+
const { method = "GET", path: eventPath = "/" } =
151+
event?.requestContext?.http || {};
151152

152153
try {
153154
const reqSegments: string[] = (eventPath as string)

src/allocator.rs

+44
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
use libmimalloc_sys as ffi;
2+
3+
use core::alloc::{GlobalAlloc, Layout};
4+
use core::ffi::c_void;
5+
use core::sync::atomic::AtomicUsize;
6+
use ffi::*;
7+
use std::sync::atomic::Ordering;
8+
9+
pub static USED_MEM: AtomicUsize = AtomicUsize::new(0);
10+
11+
pub struct TrackingMiMalloc;
12+
13+
unsafe impl GlobalAlloc for TrackingMiMalloc {
14+
#[inline]
15+
unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
16+
let size = layout.size();
17+
USED_MEM.fetch_add(size, Ordering::Relaxed);
18+
mi_malloc_aligned(size, layout.align()) as *mut u8
19+
}
20+
21+
#[inline]
22+
unsafe fn alloc_zeroed(&self, layout: Layout) -> *mut u8 {
23+
let size = layout.size();
24+
USED_MEM.fetch_add(size, Ordering::Relaxed);
25+
mi_zalloc_aligned(size, layout.align()) as *mut u8
26+
}
27+
28+
#[inline]
29+
unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) {
30+
USED_MEM.fetch_sub(layout.size(), Ordering::Relaxed);
31+
mi_free(ptr as *mut c_void);
32+
}
33+
34+
#[inline]
35+
unsafe fn realloc(&self, ptr: *mut u8, layout: Layout, new_size: usize) -> *mut u8 {
36+
let size = layout.size();
37+
if new_size > size {
38+
USED_MEM.fetch_add(new_size - size, Ordering::Relaxed);
39+
} else {
40+
USED_MEM.fetch_sub(size - new_size, Ordering::Relaxed);
41+
}
42+
mi_realloc_aligned(ptr as *mut c_void, new_size, layout.align()) as *mut u8
43+
}
44+
}

src/js/@llrt/runtime.ts

+1
Original file line numberDiff line numberDiff line change
@@ -198,6 +198,7 @@ const startProcessEvents = async (
198198
}
199199
iterations++;
200200
}
201+
global.__gc();
201202
}
202203
};
203204

src/main.c

+20-1
Original file line numberDiff line numberDiff line change
@@ -298,8 +298,27 @@ int main(int argc, char *argv[])
298298
char startTimeStr[16];
299299
sprintf(startTimeStr, "%lu", startTime);
300300

301+
char *memorySizeStr = getenv("AWS_LAMBDA_FUNCTION_MEMORY_SIZE");
302+
int memorySize = memorySizeStr ? atoi(memorySizeStr) : 128;
303+
double memoryFactor = 0.8;
304+
if (memorySize > 512)
305+
{
306+
memoryFactor = 0.9;
307+
}
308+
if (memorySize > 1024)
309+
{
310+
memoryFactor = 0.92;
311+
}
312+
if (memorySize > 2048)
313+
{
314+
memoryFactor = 0.95;
315+
}
316+
317+
char mimallocReserveMemoryMb[16];
318+
sprintf(mimallocReserveMemoryMb, "%iMiB", (int)(memorySize * memoryFactor));
319+
301320
setenv("_START_TIME", startTimeStr, false);
302-
setenv("MIMALLOC_RESERVE_OS_MEMORY", "120m", false);
321+
setenv("MIMALLOC_RESERVE_OS_MEMORY", mimallocReserveMemoryMb, false);
303322
setenv("MIMALLOC_LIMIT_OS_ALLOC", "1", false);
304323

305324
logInfo("Starting app\n");

0 commit comments

Comments
 (0)