Skip to content

Appendix: Implementing Database level garbage collection for Blobs

Article

Benchmark Source

server.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>

#define BACKLOG 100

void panic(int code, int lineNum) {
    if (code == 0) {
        return;
    }

    char error_msg[64];
    snprintf(error_msg, sizeof(error_msg), "Panic %d @ %d\n", code, lineNum);
    __attribute__((unused)) ssize_t bytes_written = write(STDOUT_FILENO, error_msg, strlen(error_msg));
    exit(code);
}

int main() {
    int server_fd, new_socket;
    struct sockaddr_in address;
    int addrlen = sizeof(address);

    // Create socket
    if ((server_fd = socket(AF_INET, SOCK_STREAM, 0)) == 0) {
        panic(255, 31);
    }

    address.sin_family = AF_INET;
    address.sin_addr.s_addr = INADDR_ANY;
    address.sin_port = htons(8080);

    // Bind socket to address and port
    if (bind(server_fd, (struct sockaddr *)&address, sizeof(address)) < 0) {
        panic(1, 44);
    }

    // Listen for incoming connections
    if (listen(server_fd, BACKLOG) < 0) {
        panic(1, 56);
    }

    while (1) {
        // Accept incoming connection
        if ((new_socket = accept(server_fd, (struct sockaddr *)&address, (socklen_t*)&addrlen)) < 0) {
            panic(1, 69);
        }

        // Send HTTP response
        const char *response = "HTTP/1.1 200 OK\r\n\r\nHello, World!";
        ssize_t send_result = send(new_socket, response, strlen(response), 0);
        if (send_result < 0) {
            panic(1, 93);
        }

        // Shutdown and close the socket
        shutdown(new_socket, SHUT_RDWR);
        close(new_socket);
    }

    return 0;
}
server.wat
(module
    (type (;0;) (func (param i32) (result i32)))
    (type (;1;) (func (param i32 i32) (result i32)))
    (type (;2;) (func (param i32 i32 i32) (result i32)))
    (type (;3;) (func (param i32 i32 i32 i32) (result i32)))
    (type (;4;) (func (param i32 i32 i32 i32 i32) (result i32)))
    (type (;5;) (func))
    (type (;6;) (func (param i32)))
    (type (;7;) (func (param i32 i32)))
    (import "wasix_32v1" "fd_write" (func (;0;) (type 3)))
    (import "wasix_32v1" "fd_close" (func (;1;) (type 0)))
    (import "wasix_32v1" "sock_open"      (func (;2;) (type 3)))
    (import "wasix_32v1" "sock_bind"      (func (;3;) (type 1)))
    (import "wasix_32v1" "sock_listen"    (func (;4;) (type 1)))
    (import "wasix_32v1" "sock_accept_v2" (func (;5;) (type 3)))
    (import "wasix_32v1" "sock_send"      (func (;6;) (type 4)))
    (import "wasix_32v1" "sock_status"    (func (;7;) (type 1)))
    (import "wasix_32v1" "proc_exit"      (func (;8;) (type 6)))
    (import "wasix_32v1" "sock_shutdown"  (func (;9;) (type 1)))

    ;; main()
    (func (;10;) (type 5) (local i32 i32)
        ;; Create socket using sock_open
        i32.const 1    ;; PF_NET
        i32.const 1    ;; SOCK_STREAM
        i32.const 0    ;; Protocol
        i32.const 255  ;; result pointer
        call 2         ;; sock_open()

        ;; panic on error @
        i32.const 31
        call 11

        ;; Load the socket descriptor from memory
        i32.const 255
        i32.load
        local.set 0

        ;; Bind socket to address and port
        local.get 0   ;; Socket file descriptor
        i32.const 48  ;; Address of the sockaddr_in structure
        call 3        ;; sock_bind()

        ;; panic on error @
        i32.const 44
        call 11

        local.get 0
        call 13 ;; sock_status_print()
        drop

        ;; Listen for incoming connections
        local.get 0     ;; Socket file descriptor
        i32.const 100   ;; Backlog (maximum pending connections)
        call 4          ;; sock_listen()

        ;; panic on error @
        i32.const 56
        call 11

        local.get 0
        call 13 ;; sock_status_print()
        drop

        (loop
            local.get 0    ;; Listening socket file descriptor
            i32.const 0    ;; Desired file descriptor flags (default)
            i32.const 64   ;; result pointer: new socket
            i32.const 160  ;; result pointer: remote address
            call 5         ;; sock_accept_v2()

            ;; panic on error @
            i32.const 69
            call 11

            ;; Load the new socket descriptor from memory
            i32.const 64
            i32.load
            local.set 1

            ;; Send response to the client
            local.get 1    ;; socket
            i32.const 80   ;; iovs
            i32.const 1    ;; iovs_len
            i32.const 0    ;; No additional flags
            i32.const 160  ;; ptr: remote address
            call 6         ;; sock_send()

            ;; panic on error @
            i32.const 93
            call 11

            ;; Shutdown the socket
            local.get 1 ;; socket
            i32.const 2 ;; how: SHUT_RDWR
            call 9      ;; sock_shutdown()
            drop

            ;; Close the fd
            local.get 1 ;; socket
            call 1      ;; fd_close()
            drop

            br 0
        )
    )

    ;; panicOnError(code: i32, lineNum: i32)
    (func (;11;) (type 7) (param i32 i32)
        local.get 0
        i32.const 0
        i32.eq
        if
            return
        end

        ;; overwrite string encoding param.0
        i32.const 14
        local.get 0
        i32.const 100
        i32.div_u
        i32.const 2
        call 12
        i32.store8

        i32.const 15
        local.get 0
        i32.const 10
        i32.div_u
        i32.const 1
        call 12
        i32.store8

        i32.const 16
        local.get 0
        i32.const 0
        call 12
        i32.store8

        ;; overwrite string encoding param.1
        i32.const 20
        local.get 1
        i32.const 100
        i32.div_u
        i32.const 2
        call 12
        i32.store8

        i32.const 21
        local.get 1
        i32.const 10
        i32.div_u
        i32.const 1
        call 12
        i32.store8

        i32.const 22
        local.get 1
        i32.const 0
        call 12
        i32.store8

        ;; write to buffer
        i32.const 1   ;; std.io file descriptor
        i32.const 0   ;; iovs
        i32.const 1   ;; iovs_len
        i32.const 255 ;; nwritten
        call 0        ;; fd_write
        drop

        local.get 0
        call 8
    )

    ;; digitToChar(num: i32, place: i32)
    (func (;12;) (type 1) (param i32 i32) (result i32) (local i32)
        local.get 0
        i32.const 10
        i32.rem_u
        local.set 2

        local.get 1
        i32.const 0
        i32.ne
        if
            local.get 2
            i32.const 0
            i32.eq
            if
                i32.const 95
                return
            end
        end

        local.get 2
        i32.const 48
        i32.add
        return
    )

    ;; sock_status_print(fd: i32)
    (func (;13;) (type 0) (param i32) (result i32) (local i32)
        local.get 0    ;; socket
        i32.const 255  ;; ptr: status
        call 7         ;; sock_status()

        ;; panic on error @
        i32.const 185
        call 11

        i32.const 255
        i32.load
        local.set 1

        ;; overwrite string encoding
        i32.const 145
        local.get 1
        i32.const 100
        i32.div_u
        i32.const 2
        call 12
        i32.store8

        i32.const 146
        local.get 1
        i32.const 10
        i32.div_u
        i32.const 1
        call 12
        i32.store8

        i32.const 147
        local.get 1
        i32.const 0
        call 12
        i32.store8

        ;; write to buffer
        i32.const 1    ;; std.io file descriptor
        i32.const 124  ;; iovs
        i32.const 1    ;; iovs_len
        i32.const 255  ;; nwritten
        call 0         ;; fd_write
        drop

        local.get 1
        return
    )

    ;; sock_wait_opened(fd: i32)
    (func (;14;) (type 6) (param i32)
        block
            loop
                local.get 0
                call 13 ;; sock_status_print()

                i32.const 0
                i32.ne
                if
                    br 2
                end
                br 0
            end
        end
    )

    (memory (;0;) 1)
    (export "memory" (memory 0))
    (export "_start" (func 10))

    (data (i32.const 0) "\08\00\00\00\10\00\00\00")
    (data (i32.const 8) "Panic ??? @ ???\0a")

    ;; sockaddr_in
    (data (i32.const 48) "\01\00")                   ;; sin_family: AF_INET = 0x0001
    (data (i32.const 50) "\90\1f")                   ;; sin_port:      8080 = 0x1F90
    (data (i32.const 52) "\00\00\00\00")             ;; sin_addr:INADDR_ANY = 0.0.0.0
    (data (i32.const 56) "\00\00\00\00\00\00\00\00") ;; sin_zero = char[8] padding for sockaddr compatibility

    ;; http response
    (data (i32.const 80) "\58\00\00\00\24\00\00\00")
    (data (i32.const 88) "HTTP/1.1 200 OK\0d\0a\0d\0aHello, World!")

    (data (i32.const 124) "\84\00\00\00\14\00\00\00")
    (data (i32.const 132) "Sock Status: ???\0d\0a")

    ;; global: remote sock_addr
    (data (i32.const 160) "\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00")

    ;; stack: offset.255
)

Other Benchmarks

Raw Results

WebAssembly (wasmer runtime)

wasmer server.wasm --net
Running 10s test @ http://127.0.0.1:8080
  12 threads and 400 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency    26.55ms  128.64ms   1.77s    95.69%
    Req/Sec     1.99k     1.30k   29.42k    83.84%
  236457 requests in 10.10s, 8.12MB read
  Socket errors: connect 0, read 236457, write 0, timeout 37
Requests/sec:  23413.55
Transfer/sec:    823.16KB

Running 10s test @ http://127.0.0.1:8080
  12 threads and 400 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency    10.13ms   73.05ms   1.76s    98.36%
    Req/Sec     1.92k     1.18k    6.46k    65.69%
  226805 requests in 10.07s, 7.79MB read
  Socket errors: connect 0, read 226805, write 0, timeout 12
Requests/sec:  22533.68
Transfer/sec:    792.23KB

Running 10s test @ http://127.0.0.1:8080
  12 threads and 400 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency    13.71ms   83.90ms   1.77s    97.23%
    Req/Sec     2.03k     1.34k   18.36k    72.21%
  236314 requests in 10.10s, 8.11MB read
  Socket errors: connect 0, read 236315, write 0, timeout 19
Requests/sec:  23397.43
Transfer/sec:    822.60KB

WebAssembly (wasmer llvm)

wat2wasm server.wat -o server.wasm
wasmer create-exe --llvm server.wasm -o server.out
Running 10s test @ http://127.0.0.1:8080
  12 threads and 400 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency    20.35ms  117.18ms   1.77s    96.60%
    Req/Sec     2.51k     1.77k   34.89k    80.43%
  299600 requests in 10.10s, 10.29MB read
  Socket errors: connect 0, read 299600, write 0, timeout 39
Requests/sec:  29665.16
Transfer/sec:      1.02MB

Running 10s test @ http://127.0.0.1:8080
  12 threads and 400 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency    17.29ms  106.60ms   1.76s    97.06%
    Req/Sec     2.50k     1.29k   14.02k    77.21%
  301093 requests in 10.10s, 10.34MB read
  Socket errors: connect 0, read 301093, write 0, timeout 23
Requests/sec:  29811.75
Transfer/sec:      1.02MB

Running 10s test @ http://127.0.0.1:8080
  12 threads and 400 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency    22.95ms  123.38ms   1.77s    96.22%
    Req/Sec     2.47k     1.64k   36.88k    84.55%
  296337 requests in 10.10s, 10.17MB read
  Socket errors: connect 0, read 296337, write 0, timeout 30
Requests/sec:  29339.53
Transfer/sec:      1.01MB

Winter.js (wasmer runtime)

Running 10s test @ http://127.0.0.1:8080
  12 threads and 400 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency    55.14ms    8.91ms 165.95ms   93.45%
    Req/Sec   396.56    138.06   680.00     62.94%
  47648 requests in 10.05s, 5.47MB read
Requests/sec:   4740.14
Transfer/sec:    556.95KB

Running 10s test @ http://127.0.0.1:8080
  12 threads and 400 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency    50.93ms    6.07ms 215.99ms   84.28%
    Req/Sec   200.83    106.82   686.00     68.49%
  24136 requests in 10.04s, 2.77MB read
Requests/sec:   2403.87
Transfer/sec:    282.71KB

Running 10s test @ http://127.0.0.1:8080
  12 threads and 400 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency    52.97ms    9.61ms 251.05ms   90.47%
    Req/Sec   112.27     79.62   400.00     65.18%
  13092 requests in 10.04s, 1.50MB read
Requests/sec:   1304.06
Transfer/sec:    153.45KB

Node

Running 10s test @ http://127.0.0.1:8080
  12 threads and 400 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency    45.82ms  177.25ms   1.99s    95.62%
    Req/Sec     2.88k   835.90    11.15k    90.74%
  325385 requests in 10.03s, 53.37MB read
  Socket errors: connect 0, read 0, write 0, timeout 43
Requests/sec:  32447.16
Transfer/sec:      5.32MB

Running 10s test @ http://127.0.0.1:8080
  12 threads and 400 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency    45.24ms  171.77ms   2.00s    95.86%
    Req/Sec     2.49k   816.56    11.11k    90.42%
  280164 requests in 10.06s, 45.96MB read
  Socket errors: connect 0, read 0, write 0, timeout 64
Requests/sec:  27851.09
Transfer/sec:      4.57MB

Running 10s test @ http://127.0.0.1:8080
  12 threads and 400 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency    43.95ms  168.63ms   1.99s    95.99%
    Req/Sec     2.51k     0.88k   17.13k    90.40%
  283702 requests in 10.06s, 46.54MB read
  Socket errors: connect 0, read 0, write 0, timeout 60
Requests/sec:  28193.25
Transfer/sec:      4.62MB

Bun

Running 10s test @ http://127.0.0.1:8080
  12 threads and 400 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency     3.21ms    1.11ms  40.54ms   97.62%
    Req/Sec    10.44k   699.00    16.63k    85.83%
  1254427 requests in 10.06s, 143.56MB read
Requests/sec: 124727.81
Transfer/sec:     14.27MB

Running 10s test @ http://127.0.0.1:8080
  12 threads and 400 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency     3.48ms  362.15us  11.16ms   80.94%
    Req/Sec     9.52k   506.77    18.89k    94.37%
  1143770 requests in 10.06s, 130.89MB read
Requests/sec: 113749.38
Transfer/sec:     13.02MB

Running 10s test @ http://127.0.0.1:8080
  12 threads and 400 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency     3.14ms  382.54us   8.30ms   84.57%
    Req/Sec    10.57k   786.85    20.00k    97.35%
  1268543 requests in 10.02s, 145.17MB read
Requests/sec: 126545.76
Transfer/sec:     14.48MB

C (gcc -O0)

Running 10s test @ http://127.0.0.1:8080
  12 threads and 400 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency    21.26ms  117.42ms   1.77s    95.95%
    Req/Sec     4.73k     2.65k   16.46k    72.86%
  562425 requests in 10.10s, 17.16MB read
  Socket errors: connect 0, read 562425, write 0, timeout 36
Requests/sec:  55685.12
Transfer/sec:      1.70MB

Running 10s test @ http://127.0.0.1:8080
  12 threads and 400 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency    27.25ms  143.89ms   1.77s    95.87%
    Req/Sec     4.85k     2.46k   17.69k    66.83%
  579112 requests in 10.10s, 17.67MB read
  Socket errors: connect 0, read 579112, write 0, timeout 35
Requests/sec:  57339.04
Transfer/sec:      1.75MB

Running 10s test @ http://127.0.0.1:8080
  12 threads and 400 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency    26.16ms  124.31ms   1.77s    95.02%
    Req/Sec     4.84k     2.82k   34.01k    75.42%
  574902 requests in 10.10s, 17.55MB read
  Socket errors: connect 0, read 574902, write 0, timeout 32
Requests/sec:  56920.29
Transfer/sec:      1.74MB

C (gcc -O3)

Running 10s test @ http://127.0.0.1:8080
  12 threads and 400 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency    18.98ms  115.87ms   1.76s    96.51%
    Req/Sec     5.00k     2.96k   29.10k    74.96%
  594236 requests in 10.10s, 18.13MB read
  Socket errors: connect 0, read 594236, write 0, timeout 28
Requests/sec:  58834.45
Transfer/sec:      1.80MB

Running 10s test @ http://127.0.0.1:8080
  12 threads and 400 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency    20.99ms  119.09ms   1.77s    96.18%
    Req/Sec     5.00k     2.87k   24.91k    74.08%
  594872 requests in 10.10s, 18.15MB read
  Socket errors: connect 0, read 594872, write 0, timeout 43
Requests/sec:  58899.79
Transfer/sec:      1.80MB

Running 10s test @ http://127.0.0.1:8080
  12 threads and 400 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency    21.32ms  125.58ms   1.77s    96.48%
    Req/Sec     5.15k     3.13k   18.28k    71.01%
  597622 requests in 10.10s, 18.24MB read
  Socket errors: connect 0, read 597622, write 0, timeout 47
Requests/sec:  59175.08
Transfer/sec:      1.81MB