Skip to content

Commit c9d8e2e

Browse files
committed
perf(alsa): inline and improve branch predictions
1 parent 3c592d9 commit c9d8e2e

File tree

1 file changed

+49
-34
lines changed

1 file changed

+49
-34
lines changed

src/host/alsa/mod.rs

Lines changed: 49 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -688,7 +688,7 @@ fn input_stream_worker(
688688
delay_frames,
689689
stream_type,
690690
} => {
691-
assert_eq!(
691+
debug_assert_eq!(
692692
stream_type,
693693
StreamType::Input,
694694
"expected input stream, but polling descriptors indicated output",
@@ -746,7 +746,7 @@ fn output_stream_worker(
746746
delay_frames,
747747
stream_type,
748748
} => {
749-
assert_eq!(
749+
debug_assert_eq!(
750750
stream_type,
751751
StreamType::Output,
752752
"expected output stream, but polling descriptors indicated input",
@@ -875,11 +875,17 @@ fn process_input(
875875
stream.channel.io_bytes().readi(buffer)?;
876876
let data = buffer.as_mut_ptr() as *mut ();
877877
let data = unsafe { Data::from_parts(data, stream.period_samples, stream.sample_format) };
878-
let callback = stream_timestamp(&status, stream.creation_instant)?;
878+
let callback = match stream.creation_instant {
879+
None => stream_timestamp_hardware(&status)?,
880+
Some(creation) => stream_timestamp_fallback(creation)?,
881+
};
879882
let delay_duration = frames_to_duration(delay_frames, stream.conf.sample_rate);
880883
let capture = callback
881884
.sub(delay_duration)
882-
.expect("`capture` is earlier than representation supported by `StreamInstant`");
885+
.ok_or_else(|| BackendSpecificError {
886+
description: "`capture` is earlier than representation supported by `StreamInstant`"
887+
.to_string(),
888+
})?;
883889
let timestamp = crate::InputStreamTimestamp { callback, capture };
884890
let info = crate::InputCallbackInfo { timestamp };
885891
data_callback(&data, &info);
@@ -904,11 +910,17 @@ fn process_output(
904910
let data = buffer.as_mut_ptr() as *mut ();
905911
let mut data =
906912
unsafe { Data::from_parts(data, stream.period_samples, stream.sample_format) };
907-
let callback = stream_timestamp(&status, stream.creation_instant)?;
913+
let callback = match stream.creation_instant {
914+
None => stream_timestamp_hardware(&status)?,
915+
Some(creation) => stream_timestamp_fallback(creation)?,
916+
};
908917
let delay_duration = frames_to_duration(delay_frames, stream.conf.sample_rate);
909918
let playback = callback
910919
.add(delay_duration)
911-
.expect("`playback` occurs beyond representation supported by `StreamInstant`");
920+
.ok_or_else(|| BackendSpecificError {
921+
description: "`playback` occurs beyond representation supported by `StreamInstant`"
922+
.to_string(),
923+
})?;
912924
let timestamp = crate::OutputStreamTimestamp { callback, playback };
913925
let info = crate::OutputCallbackInfo { timestamp };
914926
data_callback(&mut data, &info);
@@ -948,42 +960,43 @@ fn process_output(
948960
Ok(())
949961
}
950962

951-
// Use the elapsed duration since the start of the stream.
963+
// Use hardware timestamps from ALSA.
952964
//
953-
// This ensures positive values that are compatible with our `StreamInstant` representation.
954-
fn stream_timestamp(
965+
// This ensures accurate timestamps based on actual hardware timing.
966+
#[inline]
967+
fn stream_timestamp_hardware(
955968
status: &alsa::pcm::Status,
956-
creation_instant: Option<std::time::Instant>,
957969
) -> Result<crate::StreamInstant, BackendSpecificError> {
958-
match creation_instant {
959-
None => {
960-
let trigger_ts = status.get_trigger_htstamp();
961-
let ts = status.get_htstamp();
962-
let nanos = timespec_diff_nanos(ts, trigger_ts);
963-
if nanos < 0 {
964-
let description = format!(
965-
"get_htstamp `{}.{}` was earlier than get_trigger_htstamp `{}.{}`",
966-
ts.tv_sec, ts.tv_nsec, trigger_ts.tv_sec, trigger_ts.tv_nsec
967-
);
968-
return Err(BackendSpecificError { description });
969-
}
970-
Ok(crate::StreamInstant::from_nanos(nanos))
971-
}
972-
Some(creation) => {
973-
let now = std::time::Instant::now();
974-
let duration = now.duration_since(creation);
975-
crate::StreamInstant::from_nanos_i128(duration.as_nanos() as i128).ok_or(
976-
BackendSpecificError {
977-
description: "stream duration has exceeded `StreamInstant` representation"
978-
.to_string(),
979-
},
980-
)
981-
}
970+
let trigger_ts = status.get_trigger_htstamp();
971+
let ts = status.get_htstamp();
972+
let nanos = timespec_diff_nanos(ts, trigger_ts);
973+
if nanos < 0 {
974+
let description = format!(
975+
"get_htstamp `{}.{}` was earlier than get_trigger_htstamp `{}.{}`",
976+
ts.tv_sec, ts.tv_nsec, trigger_ts.tv_sec, trigger_ts.tv_nsec
977+
);
978+
return Err(BackendSpecificError { description });
982979
}
980+
Ok(crate::StreamInstant::from_nanos(nanos))
981+
}
982+
983+
// Use elapsed duration since stream creation as fallback when hardware timestamps are unavailable.
984+
//
985+
// This ensures positive values that are compatible with our `StreamInstant` representation.
986+
#[inline]
987+
fn stream_timestamp_fallback(
988+
creation: std::time::Instant,
989+
) -> Result<crate::StreamInstant, BackendSpecificError> {
990+
let now = std::time::Instant::now();
991+
let duration = now.duration_since(creation);
992+
crate::StreamInstant::from_nanos_i128(duration.as_nanos() as i128).ok_or(BackendSpecificError {
993+
description: "stream duration has exceeded `StreamInstant` representation".to_string(),
994+
})
983995
}
984996

985997
// Adapted from `timestamp2ns` here:
986998
// https://fossies.org/linux/alsa-lib/test/audio_time.c
999+
#[inline]
9871000
fn timespec_to_nanos(ts: libc::timespec) -> i64 {
9881001
let nanos = ts.tv_sec * 1_000_000_000 + ts.tv_nsec;
9891002
#[cfg(target_pointer_width = "64")]
@@ -994,11 +1007,13 @@ fn timespec_to_nanos(ts: libc::timespec) -> i64 {
9941007

9951008
// Adapted from `timediff` here:
9961009
// https://fossies.org/linux/alsa-lib/test/audio_time.c
1010+
#[inline]
9971011
fn timespec_diff_nanos(a: libc::timespec, b: libc::timespec) -> i64 {
9981012
timespec_to_nanos(a) - timespec_to_nanos(b)
9991013
}
10001014

10011015
// Convert the given duration in frames at the given sample rate to a `std::time::Duration`.
1016+
#[inline]
10021017
fn frames_to_duration(frames: usize, rate: crate::SampleRate) -> std::time::Duration {
10031018
let secsf = frames as f64 / rate.0 as f64;
10041019
let secs = secsf as u64;

0 commit comments

Comments
 (0)