Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
[submodule "third_party/c_library_v2"]
path = third_party/c_library_v2
url = https://github.com/mavlink/c_library_v2.git
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 6 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,12 @@ tokio-util = { version = "0.7", features = ["codec"] }
[features]
default = ["std"]
std = []
# Enables the C reference (c_library_v2) decode benchmark arm. Requires the
# third_party/c_library_v2 git submodule and a C compiler.
bench-c-reference = []

[build-dependencies]
cc = "1.2"

[dev-dependencies]
anyhow = "1.0"
Expand Down
75 changes: 75 additions & 0 deletions benches/bench.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,58 @@ use rand::{prelude::StdRng, SeedableRng};
use tokio_stream::StreamExt;
use tokio_util::codec::{Decoder, FramedRead};

#[cfg(feature = "bench-c-reference")]
mod c_reference {
use std::{ffi::c_int, mem::MaybeUninit, ptr};

pub struct BenchState(*mut std::ffi::c_void);

extern "C" {
fn mavlink_codec_bench_state_new() -> *mut std::ffi::c_void;
fn mavlink_codec_bench_state_reset(state: *mut std::ffi::c_void);
fn mavlink_codec_bench_state_free(state: *mut std::ffi::c_void);
fn mavlink_codec_bench_state_decode(
state: *mut std::ffi::c_void,
data: *const u8,
len: usize,
) -> c_int;
fn mavlink_codec_bench_state_last_message(
state: *const std::ffi::c_void,
) -> *const std::ffi::c_void;
}

impl BenchState {
pub fn new() -> Self {
let state = unsafe { mavlink_codec_bench_state_new() };
assert!(!state.is_null());
Self(state)
}

pub fn reset(&mut self) {
unsafe { mavlink_codec_bench_state_reset(self.0) };
}

pub fn decode(&mut self, data: &[u8]) -> c_int {
unsafe { mavlink_codec_bench_state_decode(self.0, data.as_ptr(), data.len()) }
}

pub fn last_message_word(&self) -> u64 {
let msg = unsafe { mavlink_codec_bench_state_last_message(self.0) };
let mut word = MaybeUninit::<u64>::uninit();
unsafe {
ptr::copy_nonoverlapping(msg.cast(), word.as_mut_ptr(), 1);
word.assume_init()
}
}
}

impl Drop for BenchState {
fn drop(&mut self) {
unsafe { mavlink_codec_bench_state_free(self.0) };
}
}
}

fn add_random_v2_message(buf: &mut Vec<u8>, rng: &mut StdRng) {
use rand::Rng;

Expand Down Expand Up @@ -159,6 +211,29 @@ fn benchmark_decode(c: &mut Criterion) {
);
},
);

#[cfg(feature = "bench-c-reference")]
group.bench_with_input(
BenchmarkId::new("c_library_v2", messages_count),
messages_count,
|b, &messages_count| {
let buf = buf.clone();

b.iter_batched(
|| {
let mut state = c_reference::BenchState::new();
state.reset();
state
},
|mut state| {
let count = state.decode(&buf);
black_box(count);
black_box(state.last_message_word());
},
criterion::BatchSize::SmallInput,
);
},
);
}

group.finish();
Expand Down
51 changes: 51 additions & 0 deletions benches/c_shim/shim.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
#include <stddef.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>

#include <ardupilotmega/mavlink.h>

struct mavlink_codec_bench_state {
mavlink_message_t rxmsg;
mavlink_status_t status;
mavlink_message_t r_message;
mavlink_status_t r_status;
mavlink_message_t last_message;
};

struct mavlink_codec_bench_state *mavlink_codec_bench_state_new(void) {
struct mavlink_codec_bench_state *state =
calloc(1, sizeof(struct mavlink_codec_bench_state));
return state;
}

void mavlink_codec_bench_state_reset(struct mavlink_codec_bench_state *state) {
memset(&state->rxmsg, 0, sizeof(state->rxmsg));
memset(&state->status, 0, sizeof(state->status));
}

void mavlink_codec_bench_state_free(struct mavlink_codec_bench_state *state) {
free(state);
}

/* Decodes `len` bytes one char at a time and returns the number of complete
* framed messages. Each completed frame is copied into `last_message` so the
* compiler cannot elide message materialization. */
int mavlink_codec_bench_state_decode(struct mavlink_codec_bench_state *state,
const uint8_t *data, size_t len) {
int count = 0;
for (size_t i = 0; i < len; i++) {
if (mavlink_frame_char_buffer(&state->rxmsg, &state->status, data[i],
&state->r_message,
&state->r_status) == MAVLINK_FRAMING_OK) {
state->last_message = state->r_message;
count++;
}
}
return count;
}

const mavlink_message_t *
mavlink_codec_bench_state_last_message(const struct mavlink_codec_bench_state *state) {
return &state->last_message;
}
27 changes: 27 additions & 0 deletions build.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
fn main() {
println!("cargo:rerun-if-changed=build.rs");

// The C reference shim is only compiled for the opt-in benchmark arm so that normal
// builds need neither a C compiler nor the c_library_v2 submodule.
if std::env::var_os("CARGO_FEATURE_BENCH_C_REFERENCE").is_none() {
return;
}

let lib_root = std::path::Path::new("third_party/c_library_v2");
assert!(
lib_root.join("ardupilotmega/mavlink.h").exists(),
"the `bench-c-reference` feature requires the c_library_v2 submodule; run \
`git submodule update --init third_party/c_library_v2`"
);

println!("cargo:rerun-if-changed=benches/c_shim/shim.c");

cc::Build::new()
.file("benches/c_shim/shim.c")
.include(lib_root)
.warnings(false)
.flag_if_supported("-Wno-address-of-packed-member")
.flag_if_supported("-Wno-unused-parameter")
.flag_if_supported("-Wno-unused-function")
.compile("mavlink_c_shim");
}
1 change: 1 addition & 0 deletions third_party/c_library_v2
Submodule c_library_v2 added at e5697c
Loading