mirror of
https://github.com/joleuger/vuinputd.git
synced 2026-07-17 16:36:03 +00:00
First microbenchmark conducted
This commit is contained in:
parent
e3094f898b
commit
67db5f1885
5 changed files with 50 additions and 11 deletions
|
|
@ -58,6 +58,8 @@ sequenceDiagram
|
|||
Kernel->>Game: 10. send input data via eventX device
|
||||
```
|
||||
|
||||
> **Performance note:**
|
||||
> While `vuinputd` adds an extra userspace round trip via CUSE, the measured overhead is in the range of **tens of microseconds per event** in a simple integration test. This is several orders of magnitude smaller than typical sources of input latency such as frame rendering, compositor delays, scheduling jitter, or network latency. In practice, the additional cost is negligible for interactive and latency-sensitive applications, including gaming. More detailed benchmarks can be found in `TESTS.md`.
|
||||
|
||||
---
|
||||
|
||||
|
|
|
|||
|
|
@ -7,6 +7,36 @@ Install bubblewrap:
|
|||
|
||||
Run with `cargo test -p vuinputd-tests --features "requires-privileges requires-uinput requires-bwrap"`.
|
||||
|
||||
|
||||
|
||||
## Performance tests
|
||||
|
||||
Using CUSE introduces an additional round trip between kernel and userspace, which inevitably adds overhead compared to direct uinput access. To estimate the order of magnitude of this overhead, the `vuinputd-tests` include a simple integration test that emits two input events: once using direct uinput access and once via `vuinputd` v0.3.
|
||||
|
||||
The test measures the elapsed time between emitting an event and receiving it again, using `CLOCK_MONOTONIC`. The measured latencies were:
|
||||
|
||||
* **First call:** 16 µs (direct uinput) vs. 90 µs (vuinputd)
|
||||
* **Second call:** 7 µs (direct uinput) vs. 68 µs (vuinputd)
|
||||
|
||||
As expected, `vuinputd` introduces a clearly measurable performance penalty due to the additional userspace round trip. However, even in this best-case microbenchmark, the absolute overhead remains well below 0.1 ms per event.
|
||||
|
||||
In practical terms, this level of overhead is negligible for real-world usage. For latency-sensitive applications such as gaming, tens of microseconds are several orders of magnitude smaller than typical sources of latency such as frame rendering time, compositor delays, scheduling jitter, or network latency. Even a single rendered frame at 60 Hz already accounts for roughly 16.6 ms, making the additional cost introduced by `vuinputd` effectively unobservable to the user.
|
||||
|
||||
It is important to note that this benchmark is intentionally minimal and primarily intended to provide a rough sense of scale. It does not model realistic workloads, higher event rates, or concurrent inputs. More comprehensive benchmarks are required to assess behavior under load and contention. Nevertheless, these results demonstrate that the architectural approach taken by `vuinputd` is sound and does not introduce prohibitive latency by design.
|
||||
As long as more realistic benchmarks confirm similar behavior under load, `vuinputd` can be considered suitable even for interactive and latency-sensitive use cases.
|
||||
|
||||
Detailed results:
|
||||
|
||||
`integration_tests.rs#test_keyboard_in_container_with_uinput`:
|
||||
```
|
||||
{"events":[{"tv_sec":3133476,"tv_nsec":947503794,"duration_usec":16,"type_":1,"code":57,"value":1,"send_and_receive_match":true},{"tv_sec":3133476,"tv_nsec":947520555,"duration_usec":7,"type_":1,"code":57,"value":0,"send_and_receive_match":true}]}
|
||||
```
|
||||
|
||||
`integration_tests.rs#test_keyboard_in_container_with_vuinput`:
|
||||
```
|
||||
Event log: {"events":[{"tv_sec":3133303,"tv_nsec":796108454,"duration_usec":90,"type_":1,"code":57,"value":1,"send_and_receive_match":true},{"tv_sec":3133303,"tv_nsec":796198973,"duration_usec":68,"type_":1,"code":57,"value":0,"send_and_receive_match":true}]}
|
||||
```
|
||||
|
||||
## Manual end-to-end tests
|
||||
|
||||
| vuinputd | host | input type | app that creates device | app that reads device | working | Notes |
|
||||
|
|
|
|||
|
|
@ -280,7 +280,7 @@ unsafe fn set_standard_keyboard_keys(fd: i32) -> Result<(), std::io::Error> {
|
|||
#[command(author, version, about)]
|
||||
struct Args {
|
||||
/// Use IPC
|
||||
#[arg(long, default_value_t = false)]
|
||||
#[arg(long)]
|
||||
ipc: bool,
|
||||
|
||||
/// Device path (with /dev/)
|
||||
|
|
@ -335,7 +335,7 @@ fn emit_read_and_log(
|
|||
let input_event_recv = read_event(&read_from).unwrap();
|
||||
let _syn_recv = read_event(&read_from).unwrap();
|
||||
let (time_recv_sec, time_recv_nsec) = monotonic_time();
|
||||
let duration_nsec =
|
||||
let duration_usec =
|
||||
(time_recv_sec - time_sent_sec) * 1_000_000 + (time_recv_nsec - time_sent_nsec) / 1000;
|
||||
let send_and_receive_match = input_event_recv.type_ == ev_type
|
||||
&& input_event_recv.code == code
|
||||
|
|
@ -343,8 +343,8 @@ fn emit_read_and_log(
|
|||
|
||||
Ok(LoggedInputEvent {
|
||||
tv_sec: time_sent_sec,
|
||||
tv_usec: time_sent_nsec,
|
||||
duration_nsec: duration_nsec,
|
||||
tv_nsec: time_sent_nsec,
|
||||
duration_usec: duration_usec,
|
||||
type_: ev_type,
|
||||
code: code,
|
||||
value: val,
|
||||
|
|
|
|||
|
|
@ -8,9 +8,9 @@ use serde::{Deserialize, Serialize};
|
|||
pub struct LoggedInputEvent {
|
||||
pub tv_sec: i64,
|
||||
|
||||
pub tv_usec: i64,
|
||||
pub tv_nsec: i64,
|
||||
|
||||
pub duration_nsec: i64,
|
||||
pub duration_usec: i64,
|
||||
|
||||
pub type_: u16,
|
||||
|
||||
|
|
|
|||
|
|
@ -99,14 +99,18 @@ fn test_keyboard_on_host() {
|
|||
fn test_keyboard_in_container_with_uinput() {
|
||||
let test_keyboard = env!("CARGO_BIN_EXE_test-keyboard");
|
||||
|
||||
let out = bwrap::BwrapBuilder::new()
|
||||
let (builder, _ipc) = bwrap::BwrapBuilder::new()
|
||||
.unshare_net()
|
||||
.ro_bind("/", "/")
|
||||
.tmpfs("/tmp")
|
||||
.dev_bind("/dev/uinput", "/dev/uinput")
|
||||
.dev_bind("/dev/input", "/dev/input")
|
||||
.die_with_parent()
|
||||
.command(test_keyboard, &[])
|
||||
.with_ipc()
|
||||
.expect("failed to create IPC");
|
||||
|
||||
let out = builder
|
||||
.command(test_keyboard, &["--ipc"])
|
||||
.run()
|
||||
.unwrap_or_else(|e| panic!("failed to run bwrap!: {e}"));
|
||||
|
||||
|
|
@ -122,14 +126,13 @@ fn test_keyboard_in_container_with_uinput() {
|
|||
feature = "requires-uinput",
|
||||
feature = "requires-bwrap"
|
||||
))]
|
||||
//#[ignore]
|
||||
#[test]
|
||||
fn test_keyboard_in_container_with_vuinput() {
|
||||
run_vuinputd::ensure_vuinputd_running();
|
||||
|
||||
let test_keyboard = env!("CARGO_BIN_EXE_test-keyboard");
|
||||
|
||||
let out = bwrap::BwrapBuilder::new()
|
||||
let (builder, _ipc) = bwrap::BwrapBuilder::new()
|
||||
.unshare_net()
|
||||
.ro_bind("/", "/")
|
||||
.tmpfs("/tmp")
|
||||
|
|
@ -139,7 +142,11 @@ fn test_keyboard_in_container_with_vuinput() {
|
|||
.tmpfs("/run")
|
||||
.dev_bind("/dev/vuinput-test", "/dev/uinput")
|
||||
.die_with_parent()
|
||||
.command(test_keyboard, &[])
|
||||
.with_ipc()
|
||||
.expect("failed to create IPC");
|
||||
|
||||
let out = builder
|
||||
.command(test_keyboard, &["--ipc"])
|
||||
.run()
|
||||
.unwrap_or_else(|e| panic!("failed to run bwrap!: {e}"));
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue