Skip to content

Commit 0fc0ff8

Browse files
committed
bindings: add crate and Go module with Service inter-op mechanism
A Service looks like a bidirectional channel, but is one which is synchronously polled to send and receive framed message code and data payloads. It's intended as a fundamental building block for building bidirectional in-process streaming services that interoperate between Go and Rust -- an interface which is a heck of a lot faster than gRPC. It achieves zero-copy semantics by pushing Go []byte buffers down into CGO invocations, and surfacing Rust-owned []byte arenas and Frame payloads. There are some rules callers need to adhere to, such as not scribbling "sent" memory until Poll() is called, and not referencing Rust-owned memory returned by Poll() after _another_ Poll() call is made. It amortizes the CGO overhead by vectorizing dispatch. On the Rust side, some common functions are provided to monomorphize appropriate bindings for an implementation of a Service trait. Lies, damn lies, and benchmarks: This shows a variety of stride patterns (number of sent messages per Poll() call), some of which are deliberately worst case. It's comparing the actual "upper case" service with a no-op service that has the same setup & moving parts, but avoids the actual CGO invocation itself. Comparative benchmarks with equivalent "naive" CGO and "pure" Go Services are included. These are also zero-copy / return owned memory. $ go test ./go/bindings/ -bench='.' -benchtime 3s goos: linux goarch: amd64 pkg: github.com/estuary/flow/go/bindings BenchmarkUpperService/cgo-1-24 43781984 79.1 ns/op BenchmarkUpperService/noop-1-24 270172464 13.3 ns/op BenchmarkUpperService/cgo-3-24 48714016 72.2 ns/op BenchmarkUpperService/noop-3-24 368625391 9.70 ns/op BenchmarkUpperService/cgo-4-24 91550871 36.8 ns/op BenchmarkUpperService/noop-4-24 363729646 10.1 ns/op BenchmarkUpperService/cgo-11-24 77817141 45.2 ns/op BenchmarkUpperService/noop-11-24 369495175 9.76 ns/op BenchmarkUpperService/cgo-15-24 83527456 42.2 ns/op BenchmarkUpperService/noop-15-24 382336641 9.84 ns/op BenchmarkUpperService/cgo-17-24 135539218 27.0 ns/op BenchmarkUpperService/noop-17-24 423105061 8.66 ns/op BenchmarkUpperService/cgo-31-24 100000000 32.6 ns/op BenchmarkUpperService/noop-31-24 406740273 9.10 ns/op BenchmarkUpperService/cgo-32-24 151226794 23.7 ns/op BenchmarkUpperService/noop-32-24 423966495 8.67 ns/op BenchmarkUpperService/cgo-63-24 129356660 27.7 ns/op BenchmarkUpperService/noop-63-24 426944920 8.82 ns/op BenchmarkUpperService/cgo-137-24 150434529 23.9 ns/op BenchmarkUpperService/noop-137-24 441497529 8.39 ns/op BenchmarkUpperService/cgo-426-24 155104615 23.3 ns/op BenchmarkUpperService/noop-426-24 452368488 8.30 ns/op BenchmarkUpperServiceNaive-24 34605169 104 ns/op BenchmarkUpperServiceGo-24 278482276 12.8 ns/op PASS ok github.com/estuary/flow/go/bindings 111.130s
1 parent 4a30be1 commit 0fc0ff8

File tree

11 files changed

+1066
-0
lines changed

11 files changed

+1066
-0
lines changed

Cargo.lock

Lines changed: 35 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/bindings/Cargo.toml

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
[package]
2+
name = "bindings"
3+
version = "0.0.0"
4+
authors = ["Estuary Technologies, Inc"]
5+
edition = "2018"
6+
7+
[lib]
8+
crate_type = ["staticlib"]
9+
10+
[dependencies]
11+
12+
[build-dependencies]
13+
cbindgen = "*"

crates/bindings/build.rs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
extern crate cbindgen;
2+
3+
use std::env;
4+
use std::path::PathBuf;
5+
6+
fn main() {
7+
let crate_dir = env::var("CARGO_MANIFEST_DIR").unwrap();
8+
9+
let config = cbindgen::Config::from_file(PathBuf::from(&crate_dir).join("cbindgen.toml"))
10+
.expect("failed to parse cbindgen config");
11+
12+
cbindgen::Builder::new()
13+
.with_crate(crate_dir)
14+
.with_config(config)
15+
.generate()
16+
.expect("Unable to generate bindings")
17+
.write_to_file("flow_bindings.h");
18+
}

crates/bindings/cbindgen.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
language = "C"

crates/bindings/flow_bindings.h

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
#include <stdarg.h>
2+
#include <stdbool.h>
3+
#include <stdint.h>
4+
#include <stdlib.h>
5+
6+
/**
7+
* Opaque pointer for a Service instance in the ABI.
8+
*/
9+
typedef struct {
10+
uint8_t _private[0];
11+
} ServiceImpl;
12+
13+
/**
14+
* Output frame produced by a Service.
15+
*/
16+
typedef struct {
17+
/**
18+
* Service-defined response code.
19+
*/
20+
uint32_t code;
21+
/**
22+
* Begin data offset into the Channel arena.
23+
*/
24+
uint32_t begin;
25+
/**
26+
* End data offset into the Channel arena.
27+
*/
28+
uint32_t end;
29+
} Out;
30+
31+
/**
32+
* Channel is shared between CGO and Rust, and holds details
33+
* about the language interconnect.
34+
*/
35+
typedef struct {
36+
ServiceImpl *svc_impl;
37+
uint8_t *arena_ptr;
38+
uintptr_t arena_len;
39+
uintptr_t arena_cap;
40+
Out *out_ptr;
41+
uintptr_t out_len;
42+
uintptr_t out_cap;
43+
uint8_t *err_ptr;
44+
uintptr_t err_len;
45+
uintptr_t err_cap;
46+
} Channel;
47+
48+
/**
49+
* Input frame produced from CGO, which is a single service invocation.
50+
* 16 bytes, or 1/4 of a typical cache line.
51+
*/
52+
typedef struct {
53+
const uint8_t *data_ptr;
54+
uint32_t data_len;
55+
uint32_t code;
56+
} In1;
57+
58+
/**
59+
* Four invocations, composed into one struct.
60+
* 64 bytes, or one typical cache line.
61+
*/
62+
typedef struct {
63+
In1 in0;
64+
In1 in1;
65+
In1 in2;
66+
In1 in3;
67+
} In4;
68+
69+
/**
70+
* Sixteen invocations, composed into one struct.
71+
* 256 bytes, or four typical cache lines.
72+
*/
73+
typedef struct {
74+
In4 in0;
75+
In4 in1;
76+
In4 in2;
77+
In4 in3;
78+
} In16;
79+
80+
Channel *upper_case_create(void);
81+
82+
void upper_case_invoke1(Channel *ch, In1 i);
83+
84+
void upper_case_invoke4(Channel *ch, In4 i);
85+
86+
void upper_case_invoke16(Channel *ch, In16 i);
87+
88+
void upper_case_drop(Channel *ch);
89+
90+
ServiceImpl *create_upper_case_naive(void);
91+
92+
uint32_t upper_case_naive(ServiceImpl *svc,
93+
uint32_t _code,
94+
const uint8_t *in_ptr,
95+
uint32_t in_len,
96+
const uint8_t **out_ptr,
97+
uint32_t *out_len);

crates/bindings/src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
pub mod service;
2+
mod upper_case;

0 commit comments

Comments
 (0)