commit 788c4a8e12c437c3c2e0f74f16530f8a45c41999
parent 5d4e795de84dc9f0067a8e28a3c790e19f2b220b
Author: kernelkind <kernelkind@gmail.com>
Date: Tue, 24 Feb 2026 16:20:46 -0500
feat(perf): add profiling macros
Signed-off-by: kernelkind <kernelkind@gmail.com>
Diffstat:
15 files changed, 67 insertions(+), 27 deletions(-)
diff --git a/crates/notedeck/src/account/contacts.rs b/crates/notedeck/src/account/contacts.rs
@@ -69,6 +69,7 @@ impl Contacts {
}
}
+ #[profiling::function]
pub(super) fn poll_for_updates(&mut self, ndb: &Ndb, txn: &Transaction, sub: Subscription) {
let nks = ndb.poll_for_notes(sub, 1);
@@ -104,6 +105,7 @@ impl Contacts {
}
}
+#[profiling::function]
fn update_state(state: &mut ContactState, note: &Note, key: NoteKey) {
match state {
ContactState::Unreceived => {
diff --git a/crates/notedeck/src/account/mute.rs b/crates/notedeck/src/account/mute.rs
@@ -94,6 +94,7 @@ impl AccountMutedData {
muted
}
+ #[profiling::function]
pub(super) fn poll_for_updates(&mut self, ndb: &Ndb, txn: &Transaction, sub: Subscription) {
let nks = ndb.poll_for_notes(sub, 1);
diff --git a/crates/notedeck/src/app.rs b/crates/notedeck/src/app.rs
@@ -121,17 +121,21 @@ fn render_notedeck(notedeck: &mut Notedeck, ctx: &egui::Context) {
}
impl eframe::App for Notedeck {
+ #[profiling::function]
fn update(&mut self, ctx: &egui::Context, frame: &mut eframe::Frame) {
profiling::finish_frame!();
self.frame_history
.on_new_frame(ctx.input(|i| i.time), frame.info().cpu_usage);
- self.media_jobs.run_received(&mut self.job_pool, |id| {
- crate::run_media_job_pre_action(id, &mut self.img_cache.textures);
- });
- self.media_jobs.deliver_all_completed(|completed| {
- crate::deliver_completed_media_job(completed, &mut self.img_cache.textures)
- });
+ {
+ profiling::scope!("media jobs");
+ self.media_jobs.run_received(&mut self.job_pool, |id| {
+ crate::run_media_job_pre_action(id, &mut self.img_cache.textures);
+ });
+ self.media_jobs.deliver_all_completed(|completed| {
+ crate::deliver_completed_media_job(completed, &mut self.img_cache.textures)
+ });
+ }
self.nip05_cache.poll();
diff --git a/crates/notedeck/src/jobs/cache.rs b/crates/notedeck/src/jobs/cache.rs
@@ -39,6 +39,7 @@ where
}
}
+ #[profiling::function]
pub fn run_received(&mut self, pool: &mut JobPool, mut pre_action: impl FnMut(&JobId<K>)) {
for pkg in self.receive_new_jobs.try_iter() {
let id = &pkg.id;
@@ -68,6 +69,7 @@ where
}
}
+ #[profiling::function]
pub fn deliver_all_completed(&mut self, mut deliver_complete: impl FnMut(JobComplete<K, T>)) {
while let Some(res) = self.completed.pop() {
tracing::trace!("Got completed: {:?}", res.job_id);
@@ -82,6 +84,7 @@ where
}
}
+#[profiling::function]
fn run_received_job<K, T>(
job_run: JobRun<T>,
pool: &mut JobPool,
@@ -102,6 +105,7 @@ fn run_received_job<K, T>(
}
}
+#[profiling::function]
fn run_sync<F, K, T>(
job_pool: &mut JobPool,
send_new_jobs: Sender<JobPackage<K, T>>,
@@ -114,38 +118,42 @@ fn run_sync<F, K, T>(
T: Send + 'static,
{
let id_c = id.clone();
- let wrapped: Box<dyn FnOnce() + Send + 'static> = Box::new(move || {
- let res = run_job();
- match res {
- JobOutput::Complete(complete_response) => {
- completion_queue.push(JobComplete {
- job_id: id.job_id.clone(),
- response: complete_response.response,
- });
- if let Some(run) = complete_response.run_no_output {
+ let wrapped: Box<dyn FnOnce() + Send + 'static> = {
+ profiling::scope!("box gen");
+ Box::new(move || {
+ let res = run_job();
+ match res {
+ JobOutput::Complete(complete_response) => {
+ completion_queue.push(JobComplete {
+ job_id: id.job_id.clone(),
+ response: complete_response.response,
+ });
+ if let Some(run) = complete_response.run_no_output {
+ if let Err(e) = send_new_jobs.send(JobPackage {
+ id: id.into_internal(),
+ run: RunType::NoOutput(run),
+ }) {
+ tracing::error!("{e}");
+ }
+ }
+ }
+ JobOutput::Next(job_run) => {
if let Err(e) = send_new_jobs.send(JobPackage {
id: id.into_internal(),
- run: RunType::NoOutput(run),
+ run: RunType::Output(job_run),
}) {
tracing::error!("{e}");
}
}
}
- JobOutput::Next(job_run) => {
- if let Err(e) = send_new_jobs.send(JobPackage {
- id: id.into_internal(),
- run: RunType::Output(job_run),
- }) {
- tracing::error!("{e}");
- }
- }
- }
- });
+ })
+ };
tracing::trace!("Spawning sync job: {id_c:?}");
job_pool.schedule_no_output(wrapped);
}
+#[profiling::function]
fn run_async<K, T>(
send_new_jobs: Sender<JobPackage<K, T>>,
completion_queue: CompletionQueue<K, T>,
@@ -187,6 +195,7 @@ fn run_async<K, T>(
});
}
+#[profiling::function]
fn no_output_run(pool: &mut JobPool, run: NoOutputRun) {
match run {
NoOutputRun::Sync(c) => {
diff --git a/crates/notedeck/src/jobs/media.rs b/crates/notedeck/src/jobs/media.rs
@@ -23,6 +23,7 @@ pub enum MediaJobResult {
Animation(Result<Animation, Error>),
}
+#[profiling::function]
pub fn deliver_completed_media_job(
completed: JobComplete<MediaJobKind, MediaJobResult>,
tex_cache: &mut TexturesCache,
@@ -56,6 +57,7 @@ pub fn deliver_completed_media_job(
tracing::trace!("Delivered job for {id_c}");
}
+#[profiling::function]
pub fn run_media_job_pre_action(job_id: &JobId<MediaJobKind>, tex_cache: &mut TexturesCache) {
let id = job_id.id.clone();
match job_id.job_kind {
diff --git a/crates/notedeck/src/nip51_set.rs b/crates/notedeck/src/nip51_set.rs
@@ -53,6 +53,7 @@ impl Nip51SetCache {
})
}
+ #[profiling::function]
pub fn poll_for_notes(&mut self, ndb: &Ndb, unknown_ids: &mut UnknownIds) {
let new_notes = ndb.poll_for_notes(self.sub.local, 5);
@@ -86,6 +87,7 @@ impl Nip51SetCache {
}
}
+#[profiling::function]
fn add(
notes: Vec<Note>,
cache: &mut IndexMap<PackId, Nip51Set>,
diff --git a/crates/notedeck/src/persist/settings_handler.rs b/crates/notedeck/src/persist/settings_handler.rs
@@ -231,6 +231,7 @@ impl SettingsHandler {
self.try_save_settings();
}
+ #[profiling::function]
pub fn update_batch<F>(&mut self, update_fn: F)
where
F: FnOnce(&mut Settings),
diff --git a/crates/notedeck/src/unknowns.rs b/crates/notedeck/src/unknowns.rs
@@ -164,6 +164,7 @@ impl UnknownIds {
}
}
+ #[profiling::function]
pub fn update_from_note(
txn: &Transaction,
ndb: &Ndb,
diff --git a/crates/notedeck/src/zaps/cache.rs b/crates/notedeck/src/zaps/cache.rs
@@ -276,6 +276,7 @@ impl Zaps {
states.push(*id);
}
+ #[profiling::function]
pub fn process(
&mut self,
accounts: &mut Accounts,
diff --git a/crates/notedeck_columns/src/app.rs b/crates/notedeck_columns/src/app.rs
@@ -77,6 +77,7 @@ pub struct Damus {
hovered_column: Option<usize>,
}
+#[profiling::function]
fn handle_egui_events(
input: &egui::InputState,
columns: &mut Columns,
diff --git a/crates/notedeck_columns/src/nav.rs b/crates/notedeck_columns/src/nav.rs
@@ -259,6 +259,7 @@ fn process_popup_resp(
process_result
}
+#[profiling::function]
fn process_nav_resp(
app: &mut Damus,
ctx: &mut AppContext<'_>,
@@ -512,6 +513,7 @@ impl RouterAction {
}
}
+#[profiling::function]
fn process_render_nav_action(
app: &mut Damus,
ctx: &mut AppContext<'_>,
diff --git a/crates/notedeck_columns/src/timeline/cache.rs b/crates/notedeck_columns/src/timeline/cache.rs
@@ -115,6 +115,7 @@ impl TimelineCache {
}
/// Get and/or update the notes associated with this timeline
+ #[profiling::function]
fn notes<'a>(
&'a mut self,
ndb: &Ndb,
@@ -137,6 +138,7 @@ impl TimelineCache {
let mut notes = Vec::new();
for package in filters.local().packages {
+ profiling::scope!("ndb query");
if let Ok(results) = ndb.query(txn, package.filters, 1000) {
let cur_notes: Vec<NoteRef> = results
.into_iter()
@@ -174,6 +176,7 @@ impl TimelineCache {
/// When `load_local` is false, the timeline is created and subscribed
/// without running a blocking local query. Use this for startup paths
/// where initial notes are loaded asynchronously.
+ #[profiling::function]
pub fn open(
&mut self,
ndb: &Ndb,
diff --git a/crates/notedeck_columns/src/timeline/mod.rs b/crates/notedeck_columns/src/timeline/mod.rs
@@ -164,6 +164,7 @@ impl TimelineTab {
self.list.borrow_mut().reset();
}
+ #[profiling::function]
fn insert<'a>(
&mut self,
payloads: Vec<&'a NotePayload>,
@@ -409,6 +410,7 @@ impl Timeline {
let now = unix_time_secs();
let mut unknown_pks = HashSet::new();
for note_ref in notes {
+ profiling::scope!("inserting notes");
if is_future_timestamp(note_ref.created_at, now) {
continue;
}
@@ -450,6 +452,7 @@ impl Timeline {
/// The main function used for inserting notes into timelines. Handles
/// inserting into multiple views if we have them. All timeline note
/// insertions should use this function.
+ #[profiling::function]
pub fn insert(
&mut self,
new_note_ids: &[NoteKey],
@@ -528,7 +531,10 @@ impl Timeline {
.get_local()
.ok_or(Error::App(notedeck::Error::no_active_sub()))?;
- let new_note_ids = ndb.poll_for_notes(sub, 500);
+ let new_note_ids = {
+ profiling::scope!("big ndb poll");
+ ndb.poll_for_notes(sub, 500)
+ };
if new_note_ids.is_empty() {
return Ok(());
} else {
@@ -889,6 +895,7 @@ fn setup_timeline_nostrdb_sub(
/// Our timelines may require additional data before it is functional. For
/// example, when we have to fetch a contact list before we do the actual
/// following list query.
+#[profiling::function]
pub fn is_timeline_ready(
ndb: &Ndb,
pool: &mut RelayPool,
diff --git a/crates/notedeck_columns/src/timeline/thread.rs b/crates/notedeck_columns/src/timeline/thread.rs
@@ -61,6 +61,7 @@ impl Threads {
/// Opening a thread.
/// Similar to [[super::cache::TimelineCache::open]]
#[allow(clippy::too_many_arguments)]
+ #[profiling::function]
pub fn open(
&mut self,
ndb: &mut Ndb,
@@ -136,6 +137,8 @@ impl Threads {
}
/// Responsible for making sure the chain and the direct replies are up to date
+ #[allow(clippy::too_many_arguments)]
+ #[profiling::function]
pub fn update(
&mut self,
selected: &Note<'_>,
diff --git a/crates/notedeck_columns/src/timeline/timeline_units.rs b/crates/notedeck_columns/src/timeline/timeline_units.rs
@@ -39,6 +39,7 @@ impl TimelineUnits {
}
/// returns number of new entries merged
+ #[profiling::function]
pub fn merge_new_notes<'a>(
&mut self,
payloads: Vec<&'a NotePayload>,