Michael Kaminsky, BrdgAI/CMU David G. Andersen, CMU Anuj … · while(errno == EAGAIN) libgotcha...

47
Lightweight Preemptible Functions Sol Boucher, Carnegie Mellon University Joint work with: Anuj Kalia, Microsoft Research David G. Andersen, CMU Michael Kaminsky, BrdgAI/CMU

Transcript of Michael Kaminsky, BrdgAI/CMU David G. Andersen, CMU Anuj … · while(errno == EAGAIN) libgotcha...

Page 1: Michael Kaminsky, BrdgAI/CMU David G. Andersen, CMU Anuj … · while(errno == EAGAIN) libgotcha microbenchmarks 41 Symbol access Time w/o libgotcha Time w/ libgotcha Function call

Lightweight Preemptible FunctionsSol Boucher, Carnegie Mellon University

Joint work with:Anuj Kalia, Microsoft Research

David G. Andersen, CMUMichael Kaminsky, BrdgAI/CMU

Page 2: Michael Kaminsky, BrdgAI/CMU David G. Andersen, CMU Anuj … · while(errno == EAGAIN) libgotcha microbenchmarks 41 Symbol access Time w/o libgotcha Time w/ libgotcha Function call

Why?● Bound resource use● Balance load of different tasks● Meet a deadline (e.g., real time)

Light∙weight (adj.): Low overhead, cheapPre∙empt∙i∙ble (adj.): Able to be stopped

2

Run a preemptible function (PF) Do something else importanttime

Page 3: Michael Kaminsky, BrdgAI/CMU David G. Andersen, CMU Anuj … · while(errno == EAGAIN) libgotcha microbenchmarks 41 Symbol access Time w/o libgotcha Time w/ libgotcha Function call

Desiderata

● Retain programmer’s control over the CPU

● Be able to interrupt arbitrary unmodified code

● Introduce minimal overhead in the common case

● Support cancellation

● Maintain compatibility with the existing systems stack

3

Page 4: Michael Kaminsky, BrdgAI/CMU David G. Andersen, CMU Anuj … · while(errno == EAGAIN) libgotcha microbenchmarks 41 Symbol access Time w/o libgotcha Time w/ libgotcha Function call

Agenda● Why contemporary approaches are insufficient

○ Futures○ Threads○ Processes

● Function calls with timeouts● Backwards compatibility● Preemptive userland threading

4

Page 5: Michael Kaminsky, BrdgAI/CMU David G. Andersen, CMU Anuj … · while(errno == EAGAIN) libgotcha microbenchmarks 41 Symbol access Time w/o libgotcha Time w/ libgotcha Function call

Problem: calling a function cedes control

5

Run a preemptible function (PF) Do something else importanttime

func()

Page 6: Michael Kaminsky, BrdgAI/CMU David G. Andersen, CMU Anuj … · while(errno == EAGAIN) libgotcha microbenchmarks 41 Symbol access Time w/o libgotcha Time w/ libgotcha Function call

Two approaches to multitasking

cooperative vs. preemptive≈

lightweightness vs. generality

6

Page 7: Michael Kaminsky, BrdgAI/CMU David G. Andersen, CMU Anuj … · while(errno == EAGAIN) libgotcha microbenchmarks 41 Symbol access Time w/o libgotcha Time w/ libgotcha Function call

Agenda● Why contemporary approaches are insufficient

○ Futures○ Threads○ Processes

● Function calls with timeouts● Backwards compatibility● Preemptive userland threading

7

Page 8: Michael Kaminsky, BrdgAI/CMU David G. Andersen, CMU Anuj … · while(errno == EAGAIN) libgotcha microbenchmarks 41 Symbol access Time w/o libgotcha Time w/ libgotcha Function call

Problem: futures are cooperative

future: lightweight userland thread scheduled by the language runtime

One future can depend on another’s result at a yield point

func()

8

PNG

Page 9: Michael Kaminsky, BrdgAI/CMU David G. Andersen, CMU Anuj … · while(errno == EAGAIN) libgotcha microbenchmarks 41 Symbol access Time w/o libgotcha Time w/ libgotcha Function call

Agenda● Why contemporary approaches are insufficient

○ Futures (cooperative not preemptive)○ Threads○ Processes

● Function calls with timeouts● Backwards compatibility● Preemptive userland threading

9

Page 10: Michael Kaminsky, BrdgAI/CMU David G. Andersen, CMU Anuj … · while(errno == EAGAIN) libgotcha microbenchmarks 41 Symbol access Time w/o libgotcha Time w/ libgotcha Function call

// Problem

buffer = decode(&img);

time_sensitive_task();

Alternative: kernel threading

10

// Tempting approach

pthread_create(&tid, NULL,

decode, &img);

usleep(TIMEOUT);

time_sensitive_task();

pthread_join(&tid, &buffer);

Page 11: Michael Kaminsky, BrdgAI/CMU David G. Andersen, CMU Anuj … · while(errno == EAGAIN) libgotcha microbenchmarks 41 Symbol access Time w/o libgotcha Time w/ libgotcha Function call

Run a preemptible function (PF) Do something else important

Problem: SLAs and graceful degradation

11

SLA

time

Page 12: Michael Kaminsky, BrdgAI/CMU David G. Andersen, CMU Anuj … · while(errno == EAGAIN) libgotcha microbenchmarks 41 Symbol access Time w/o libgotcha Time w/ libgotcha Function call

Call to malloc()

Observation: cancellation is hard

12

Process

Thread PF Thread ����⏱ CANCELLED

Page 13: Michael Kaminsky, BrdgAI/CMU David G. Andersen, CMU Anuj … · while(errno == EAGAIN) libgotcha microbenchmarks 41 Symbol access Time w/o libgotcha Time w/ libgotcha Function call

Agenda● Why contemporary approaches are insufficient

○ Futures (cooperative not preemptive)○ Threads (poor ergonomics, no cancellation)○ Processes

● Function calls with timeouts● Backwards compatibility● Preemptive userland threading

13

Page 14: Michael Kaminsky, BrdgAI/CMU David G. Andersen, CMU Anuj … · while(errno == EAGAIN) libgotcha microbenchmarks 41 Symbol access Time w/o libgotcha Time w/ libgotcha Function call

Problem: object ownership and lifetime

14

Process PF Process

Shared objectPointer ☐

CANCELLED

Page 15: Michael Kaminsky, BrdgAI/CMU David G. Andersen, CMU Anuj … · while(errno == EAGAIN) libgotcha microbenchmarks 41 Symbol access Time w/o libgotcha Time w/ libgotcha Function call

Agenda● Why contemporary approaches are insufficient

○ Futures (cooperative not preemptive)○ Threads (poor ergonomics, no cancellation) (sacrifice programmer control)○ Processes (poor performance and ergonomics)

● Function calls with timeouts● Backwards compatibility● Preemptive userland threading

15

}

Page 16: Michael Kaminsky, BrdgAI/CMU David G. Andersen, CMU Anuj … · while(errno == EAGAIN) libgotcha microbenchmarks 41 Symbol access Time w/o libgotcha Time w/ libgotcha Function call

Idea: function calls with timeouts

● Retain programmer’s control over the CPU

● Be able to interrupt arbitrary unmodified code

● Introduce minimal overhead in the common case

● Support cancellation

● Maintain compatibility with the existing systems stack

16

Page 17: Michael Kaminsky, BrdgAI/CMU David G. Andersen, CMU Anuj … · while(errno == EAGAIN) libgotcha microbenchmarks 41 Symbol access Time w/o libgotcha Time w/ libgotcha Function call

● Why contemporary approaches are insufficient● Function calls with timeouts● Backwards compatibility● Preemptive userland threading

Agenda

17

Page 18: Michael Kaminsky, BrdgAI/CMU David G. Andersen, CMU Anuj … · while(errno == EAGAIN) libgotcha microbenchmarks 41 Symbol access Time w/o libgotcha Time w/ libgotcha Function call

lightweight preemptible function: function invoked with a timeout

● Faster than spawning a process or thread

● Runs on the caller’s thread

A new application primitive

18

Page 19: Michael Kaminsky, BrdgAI/CMU David G. Andersen, CMU Anuj … · while(errno == EAGAIN) libgotcha microbenchmarks 41 Symbol access Time w/o libgotcha Time w/ libgotcha Function call

lightweight preemptible function: function invoked with a timeout

● Interrupts at 10–100s microseconds granularity

● Pauses on timeout for low overhead and flexibility to resume

A new application primitive

19

Page 20: Michael Kaminsky, BrdgAI/CMU David G. Andersen, CMU Anuj … · while(errno == EAGAIN) libgotcha microbenchmarks 41 Symbol access Time w/o libgotcha Time w/ libgotcha Function call

lightweight preemptible function: function invoked with a timeout

● Preemptible code is a normal function or closure

● Invoked via wrapper like pthread_create(), but synchronous

A new application primitive

20

Page 21: Michael Kaminsky, BrdgAI/CMU David G. Andersen, CMU Anuj … · while(errno == EAGAIN) libgotcha microbenchmarks 41 Symbol access Time w/o libgotcha Time w/ libgotcha Function call

funcstate = launch(func, 400 /*us*/, NULL);

The interface: launch() and resume()

if(!funcstate.is_complete) {

work_queue.push(funcstate);

}

// ...

funcstate = work_queue.pop();

resume(&funcstate, 200 /*us*/);

21

Page 22: Michael Kaminsky, BrdgAI/CMU David G. Andersen, CMU Anuj … · while(errno == EAGAIN) libgotcha microbenchmarks 41 Symbol access Time w/o libgotcha Time w/ libgotcha Function call

The interface: cancel()

funcstate = launch(func, 400 /*us*/, NULL);

if(!funcstate.is_complete) {

work_queue.push(funcstate);

}

// ...

funcstate = work_queue.pop();

cancel(&funcstate);

22

Page 23: Michael Kaminsky, BrdgAI/CMU David G. Andersen, CMU Anuj … · while(errno == EAGAIN) libgotcha microbenchmarks 41 Symbol access Time w/o libgotcha Time w/ libgotcha Function call

// counter == ?!

counter = 0;

funcstate = launch(λa. ++counter, 1, NULL);

++counter;

if(!funcstate.is_complete) {

resume(&funcstate, TO_COMPLETION);

}

assert(counter == 2);

Concurrency: explicit sharing

23

Page 24: Michael Kaminsky, BrdgAI/CMU David G. Andersen, CMU Anuj … · while(errno == EAGAIN) libgotcha microbenchmarks 41 Symbol access Time w/o libgotcha Time w/ libgotcha Function call

error[E0503]: cannot use `counter` because it was mutably borrowed

13 | funcstate = launch(λa. ++counter, 1, NULL); | --- ------- borrow occurs due to use | | of `counter` in closure | | | borrow of `counter` occurs here14 | ++counter; | ^^^^^^^^^ use of borrowed `counter`

Concurrency: existing protections work (e.g., Rust)

24

Page 25: Michael Kaminsky, BrdgAI/CMU David G. Andersen, CMU Anuj … · while(errno == EAGAIN) libgotcha microbenchmarks 41 Symbol access Time w/o libgotcha Time w/ libgotcha Function call

libinger: library implementing LPFs, currently supports C and Rust programs

25

Page 26: Michael Kaminsky, BrdgAI/CMU David G. Andersen, CMU Anuj … · while(errno == EAGAIN) libgotcha microbenchmarks 41 Symbol access Time w/o libgotcha Time w/ libgotcha Function call

Implementation: execution stack

funcstate = launch(func, TO_COMPLETION, NULL);

26

Caller’s stack:

...

launch()

Preemptible function’s stack:

[stub]

func()[caller]

Page 27: Michael Kaminsky, BrdgAI/CMU David G. Andersen, CMU Anuj … · while(errno == EAGAIN) libgotcha microbenchmarks 41 Symbol access Time w/o libgotcha Time w/ libgotcha Function call

Implementation: timer signal

funcstate = launch(func, TIMEOUT, NULL);

27

Caller’s stack:

...

launch()

Preemptible function’s stack:

[stub]

func()[caller]

handler()resume()

Timeout?

Page 28: Michael Kaminsky, BrdgAI/CMU David G. Andersen, CMU Anuj … · while(errno == EAGAIN) libgotcha microbenchmarks 41 Symbol access Time w/o libgotcha Time w/ libgotcha Function call

funcstate = launch(func, TIMEOUT, NULL);

cancel(&funcstate);

Implementation: cleanup

28

Preemptible function’s stack:

[stub]

func()

handler()

Page 29: Michael Kaminsky, BrdgAI/CMU David G. Andersen, CMU Anuj … · while(errno == EAGAIN) libgotcha microbenchmarks 41 Symbol access Time w/o libgotcha Time w/ libgotcha Function call

launch() timeout!

Preemption mechanism

29

t

Timeout?

Page 30: Michael Kaminsky, BrdgAI/CMU David G. Andersen, CMU Anuj … · while(errno == EAGAIN) libgotcha microbenchmarks 41 Symbol access Time w/o libgotcha Time w/ libgotcha Function call

libinger microbenchmarks

30

Operation Cost (μs)

launch() ≈ 5

resume() ≈ 5

cancel() ≈ 4800*

pthread_create() ≈ 30

fork() ≈ 200* This operation is not typically on the critical path.

Page 31: Michael Kaminsky, BrdgAI/CMU David G. Andersen, CMU Anuj … · while(errno == EAGAIN) libgotcha microbenchmarks 41 Symbol access Time w/o libgotcha Time w/ libgotcha Function call

libinger cancels runaway image decoding quickly

31

10

Page 32: Michael Kaminsky, BrdgAI/CMU David G. Andersen, CMU Anuj … · while(errno == EAGAIN) libgotcha microbenchmarks 41 Symbol access Time w/o libgotcha Time w/ libgotcha Function call

● Why contemporary approaches are insufficient● Function calls with timeouts● Backwards compatibility● Preemptive userland threading

Agenda

32

Page 33: Michael Kaminsky, BrdgAI/CMU David G. Andersen, CMU Anuj … · while(errno == EAGAIN) libgotcha microbenchmarks 41 Symbol access Time w/o libgotcha Time w/ libgotcha Function call

Signal handlers cannot call non-reentrant code

The rest of the program interrupts a preemptible function

The rest of the program cannot call non-reentrant code?!

Problem: non-reentrancy

33

ProgramPreemptible function

Preemptible function

Calls to strtok()

Page 34: Michael Kaminsky, BrdgAI/CMU David G. Andersen, CMU Anuj … · while(errno == EAGAIN) libgotcha microbenchmarks 41 Symbol access Time w/o libgotcha Time w/ libgotcha Function call

Can reuse each library copy once function runs to completion

Approach 1: library copying

34

ProgramPreemptible function

Preemptible function

strtok()

strtok()

About the Author~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

libc.so

About the Author~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

libc.so

About the Author~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

libc.so

Page 35: Michael Kaminsky, BrdgAI/CMU David G. Andersen, CMU Anuj … · while(errno == EAGAIN) libgotcha microbenchmarks 41 Symbol access Time w/o libgotcha Time w/ libgotcha Function call

Dynamic symbol binding

35

Executable

k = strtok(“k:v”, “:”);

Global Offset Table (GOT)

...

0x900dc0de

...

About the Author~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

libc

?

Page 36: Michael Kaminsky, BrdgAI/CMU David G. Andersen, CMU Anuj … · while(errno == EAGAIN) libgotcha microbenchmarks 41 Symbol access Time w/o libgotcha Time w/ libgotcha Function call

libgotcha: runtime implementing selective relinking for linked programs

36

Page 37: Michael Kaminsky, BrdgAI/CMU David G. Andersen, CMU Anuj … · while(errno == EAGAIN) libgotcha microbenchmarks 41 Symbol access Time w/o libgotcha Time w/ libgotcha Function call

1. Copy the library for each LPF2. Create an SGOT for each LPF3. Point GOT entries at libgotcha

Selective relinking

37

Executable

k = strtok("k:v", ":");

About the Author~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

libcGlobal Offset Table (GOT)

...

0x900dc0de

...

About the Author~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

libc

About the Author~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

libgotcha

0xc00010ff

SGOT————————

Page 38: Michael Kaminsky, BrdgAI/CMU David G. Andersen, CMU Anuj … · while(errno == EAGAIN) libgotcha microbenchmarks 41 Symbol access Time w/o libgotcha Time w/ libgotcha Function call

libset: full set of all a program’s libraries

Libsets and cancellation

38

ProgramPreemptible function

Preemptible function

Calls to strtok()

About the Author~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

libc.so

About the Author~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

libc.so

Page 39: Michael Kaminsky, BrdgAI/CMU David G. Andersen, CMU Anuj … · while(errno == EAGAIN) libgotcha microbenchmarks 41 Symbol access Time w/o libgotcha Time w/ libgotcha Function call

Approach 2: uncopyable functionsCopying doesn’t work for everything…

void *malloc(size_t size) {

PREEMPTION_ENABLED = false;

void *mem = /* Call the real malloc(). */;

check_for_timeout();

PREEMPTION_ENABLED = true;

return mem;

}39

Page 40: Michael Kaminsky, BrdgAI/CMU David G. Andersen, CMU Anuj … · while(errno == EAGAIN) libgotcha microbenchmarks 41 Symbol access Time w/o libgotcha Time w/ libgotcha Function call

“Approach 3”: blocking syscalls

int open(const char *filename) {

syscall(SYS_open, filename);

}

struct sigaction sa = {};

sa.sa_flags = SA_RESTART;

40

while(errno == EAGAIN)

Page 41: Michael Kaminsky, BrdgAI/CMU David G. Andersen, CMU Anuj … · while(errno == EAGAIN) libgotcha microbenchmarks 41 Symbol access Time w/o libgotcha Time w/ libgotcha Function call

libgotcha microbenchmarks

41

Symbol access Time w/o libgotcha Time w/ libgotcha

Function call ≈ 2 ns ≈ 14 ns

Global variable ≈ 0 ns ≈ 3500* ns

Baseline End-to-end time w/o libgotcha

gettimeofday() ≈ 19 ns (65% overhead)

getpid() ≈ 44 ns (30% overhead)

* Exported global variables have become rare.

Page 42: Michael Kaminsky, BrdgAI/CMU David G. Andersen, CMU Anuj … · while(errno == EAGAIN) libgotcha microbenchmarks 41 Symbol access Time w/o libgotcha Time w/ libgotcha Function call

● Why contemporary approaches are insufficient● Function calls with timeouts● Backwards compatibility● Preemptive userland threading

Agenda

42

Page 43: Michael Kaminsky, BrdgAI/CMU David G. Andersen, CMU Anuj … · while(errno == EAGAIN) libgotcha microbenchmarks 41 Symbol access Time w/o libgotcha Time w/ libgotcha Function call

libturquoise: preemptive version of the Rust Tokio userland thread pool

43

Page 44: Michael Kaminsky, BrdgAI/CMU David G. Andersen, CMU Anuj … · while(errno == EAGAIN) libgotcha microbenchmarks 41 Symbol access Time w/o libgotcha Time w/ libgotcha Function call

2 classes:Short: 500 μsLong: 50 ms

Vary % long in mix

Measure short only

hyper latency benchmark: experimental setup

compute-bound request

response

44

Page 45: Michael Kaminsky, BrdgAI/CMU David G. Andersen, CMU Anuj … · while(errno == EAGAIN) libgotcha microbenchmarks 41 Symbol access Time w/o libgotcha Time w/ libgotcha Function call

hyper latency benchmarks: results

45

No code changes! Head-of-line blockingSho

rt la

tenc

y (m

s)

% long requests % long requests

Median 99% tail

Preemptive

Cooperative

Preemptive

Cooperative

.

.

.

.

.

.

.

Page 46: Michael Kaminsky, BrdgAI/CMU David G. Andersen, CMU Anuj … · while(errno == EAGAIN) libgotcha microbenchmarks 41 Symbol access Time w/o libgotcha Time w/ libgotcha Function call

Summary

lightweight preemptible function: function invoked with a timeout

● Synchronous preemption abstraction● Supports resuming and cancellation● Interoperable with legacy software● Exciting systems applications

46

Page 47: Michael Kaminsky, BrdgAI/CMU David G. Andersen, CMU Anuj … · while(errno == EAGAIN) libgotcha microbenchmarks 41 Symbol access Time w/o libgotcha Time w/ libgotcha Function call

Thank you!Reach me at [email protected]

47