switch to profiling crates

This switches to the profiling crate for compatible
profiling between rust libraries.

To enable:

$ cargo build --release --features puffin

Feel free to experiment with other profiling backends
as well! Would be great to get tracy working.
This commit is contained in:
William Casarin
2025-03-23 10:43:49 -07:00
parent 7b9e6f180c
commit 54deb2dd88
20 changed files with 46 additions and 80 deletions

6
.envrc
View File

@@ -13,6 +13,6 @@ source scripts/macos_build_secrets.sh || :
export PATH=$PATH:$HOME/.cargo/bin
export JB55=32e1827635450ebb3c5a7d12c1f8e7b2b514439ac10a67eef3d9fd9c5c68e245
export JACK=npub1sg6plzptd64u62a878hep2kev88swjh3tw00gjsfl8f237lmu63q0uf63m
export VROD=bd1e19980e2c91e6dc657e92c25762ca882eb9272d2579e221f037f93788de91
export JEFFG=npub1zuuajd7u3sx8xu92yav9jwxpr839cs0kc3q6t56vd5u9q033xmhsk6c2uc
# simple todo reminders
export TODO_FILE=TODO
2>/dev/null todo.sh ls || :

1
.gitignore vendored
View File

@@ -1,4 +1,5 @@
.buildcmd
TODO.bak
android-config.json
build.log
perf.data

4
Cargo.lock generated
View File

@@ -2740,6 +2740,7 @@ dependencies = [
"nostr",
"nostrdb",
"poll-promise",
"profiling",
"puffin",
"puffin_egui",
"serde",
@@ -2765,6 +2766,7 @@ dependencies = [
"egui_extras",
"notedeck",
"notedeck_columns",
"profiling",
"puffin",
"puffin_egui",
"serde",
@@ -2803,6 +2805,7 @@ dependencies = [
"open",
"poll-promise",
"pretty_assertions",
"profiling",
"puffin",
"puffin_egui",
"rfd",
@@ -3487,6 +3490,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "afbdc74edc00b6f6a218ca6a5364d6226a259d4b8ea1af4a0ea063f27e179f4d"
dependencies = [
"profiling-procmacros",
"puffin",
]
[[package]]

View File

@@ -62,6 +62,7 @@ bincode = "1.3.3"
mime_guess = "2.0.5"
pretty_assertions = "1.4.1"
jni = "0.21.1"
profiling = "1.0"
[profile.small]
inherits = 'release'
@@ -84,6 +85,8 @@ eframe = { git = "https://github.com/damus-io/egui", rev = "93cd1cedc1e8eed2b055
egui-winit = { git = "https://github.com/damus-io/egui", rev = "93cd1cedc1e8eed2b055e317226838e37a845aad" }
egui_extras = { git = "https://github.com/damus-io/egui", rev = "93cd1cedc1e8eed2b055e317226838e37a845aad" }
epaint = { git = "https://github.com/damus-io/egui", rev = "93cd1cedc1e8eed2b055e317226838e37a845aad" }
puffin = { git = "https://github.com/jb55/puffin", package = "puffin", rev = "c6a6242adaf90b6292c0f462d2acd34d96d224d2" }
puffin_egui = { git = "https://github.com/jb55/puffin", package = "puffin_egui", rev = "c6a6242adaf90b6292c0f462d2acd34d96d224d2" }
#winit = { git = "https://github.com/damus-io/winit", rev = "14d61a74bee0c9863abe7ef28efae2c4d8bd3743" }
#winit = { path = "/home/jb55/dev/github/rust-windowing/winit" }
#android-activity = { git = "https://github.com/damus-io/android-activity", rev = "da17773852312a58c3445422dfe477162f2f1265" }

0
TODO Normal file
View File

View File

@@ -32,6 +32,7 @@ ehttp = {workspace = true }
mime_guess = { workspace = true }
egui-winit = { workspace = true }
tokenator = { workspace = true }
profiling = { workspace = true }
[dev-dependencies]
tempfile = { workspace = true }
@@ -40,4 +41,4 @@ tempfile = { workspace = true }
jni = { workspace = true }
[features]
profiling = ["puffin", "puffin_egui"]
puffin = ["puffin_egui", "dep:puffin"]

View File

@@ -76,8 +76,7 @@ fn render_notedeck(notedeck: &mut Notedeck, ctx: &egui::Context) {
impl eframe::App for Notedeck {
fn update(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) {
#[cfg(feature = "profiling")]
puffin::GlobalProfiler::lock().new_frame();
profiling::finish_frame!();
// handle account updates
self.accounts.update(&mut self.ndb, &mut self.pool, ctx);
@@ -97,7 +96,7 @@ impl eframe::App for Notedeck {
}
}
#[cfg(feature = "profiling")]
#[cfg(feature = "puffin")]
puffin_egui::profiler_window(ctx);
}
@@ -107,15 +106,16 @@ impl eframe::App for Notedeck {
}
}
#[cfg(feature = "profiling")]
fn setup_profiling() {
#[cfg(feature = "puffin")]
fn setup_puffin() {
info!("setting up puffin");
puffin::set_scopes_on(true); // tell puffin to collect data
}
impl Notedeck {
pub fn new<P: AsRef<Path>>(ctx: &egui::Context, data_path: P, args: &[String]) -> Self {
#[cfg(feature = "profiling")]
setup_profiling();
#[cfg(feature = "puffin")]
setup_puffin();
// Skip the first argument, which is the program name.
let (parsed_args, unrecognized_args) = Args::parse(&args[1..]);

View File

@@ -246,6 +246,7 @@ impl UnknownId {
/// We return all of this in a HashSet so that we can fetch these from
/// remote relays.
///
#[profiling::function]
pub fn get_unknown_note_ids<'a>(
ndb: &Ndb,
cached_note: &CachedNote,
@@ -253,9 +254,6 @@ pub fn get_unknown_note_ids<'a>(
note: &Note<'a>,
ids: &mut HashMap<UnknownId, HashSet<RelayUrl>>,
) -> Result<()> {
#[cfg(feature = "profiling")]
puffin::profile_function!();
// the author pubkey
if ndb.get_profile_by_pubkey(txn, note.pubkey()).is_err() {
ids.entry(UnknownId::Pubkey(Pubkey::new(*note.pubkey())))

View File

@@ -23,6 +23,7 @@ tokio = { workspace = true }
tracing-appender = { workspace = true }
tracing-subscriber = { workspace = true }
tracing = { workspace = true }
profiling = { workspace = true }
[dev-dependencies]
tempfile = { workspace = true }
@@ -40,7 +41,7 @@ path = "src/preview.rs"
[features]
default = []
profiling = ["notedeck_columns/puffin", "puffin", "puffin_egui"]
puffin = ["profiling/profile-with-puffin", "dep:puffin"]
debug-widget-callstack = ["egui/callstack"]
debug-interactive-widgets = []

View File

@@ -49,6 +49,7 @@ uuid = { workspace = true }
sha2 = { workspace = true }
base64 = { workspace = true }
egui-winit = { workspace = true }
profiling = { workspace = true }
[target.'cfg(any(target_os = "windows", target_os = "macos", target_os = "linux"))'.dependencies]
rfd = "0.15"
@@ -62,5 +63,5 @@ security-framework = "2.11.0"
[features]
default = []
profiling = ["puffin", "puffin_egui"]
puffin = ["dep:puffin", "profiling/profile-with-puffin"]

View File

@@ -507,10 +507,8 @@ fn circle_icon(ui: &mut egui::Ui, openness: f32, response: &egui::Response) {
}
*/
#[profiling::function]
fn render_damus_mobile(app: &mut Damus, app_ctx: &mut AppContext<'_>, ui: &mut egui::Ui) {
#[cfg(feature = "profiling")]
puffin::profile_function!();
//let routes = app.timelines[0].routes.clone();
if !app.columns(app_ctx.accounts).columns().is_empty()
@@ -522,10 +520,8 @@ fn render_damus_mobile(app: &mut Damus, app_ctx: &mut AppContext<'_>, ui: &mut e
}
}
#[profiling::function]
fn render_damus_desktop(app: &mut Damus, app_ctx: &mut AppContext<'_>, ui: &mut egui::Ui) {
#[cfg(feature = "profiling")]
puffin::profile_function!();
let screen_size = ui.ctx().screen_rect().width();
let calc_panel_width = (screen_size
/ get_active_columns(app_ctx.accounts, &app.decks_cache).num_columns() as f32)

View File

@@ -67,10 +67,8 @@ pub fn aspect_fill(
response
}
#[profiling::function]
pub fn round_image(image: &mut ColorImage) {
#[cfg(feature = "profiling")]
puffin::profile_function!();
// The radius to the edge of of the avatar circle
let edge_radius = image.size[0] as f32 / 2.0;
let edge_radius_squared = edge_radius * edge_radius;
@@ -114,10 +112,8 @@ pub fn round_image(image: &mut ColorImage) {
}
}
#[profiling::function]
fn process_pfp_bitmap(imgtyp: ImageType, mut image: image::DynamicImage) -> ColorImage {
#[cfg(feature = "profiling")]
puffin::profile_function!();
match imgtyp {
ImageType::Content => {
let image_buffer = image.clone().into_rgba8();
@@ -156,10 +152,8 @@ fn process_pfp_bitmap(imgtyp: ImageType, mut image: image::DynamicImage) -> Colo
}
}
#[profiling::function]
fn parse_img_response(response: ehttp::Response, imgtyp: ImageType) -> Result<ColorImage> {
#[cfg(feature = "profiling")]
puffin::profile_function!();
let content_type = response.content_type().unwrap_or_default();
let size_hint = match imgtyp {
ImageType::Profile(size) => SizeHint::Size(size, size),
@@ -167,16 +161,14 @@ fn parse_img_response(response: ehttp::Response, imgtyp: ImageType) -> Result<Co
};
if content_type.starts_with("image/svg") {
#[cfg(feature = "profiling")]
puffin::profile_scope!("load_svg");
profiling::scope!("load_svg");
let mut color_image =
egui_extras::image::load_svg_bytes_with_size(&response.bytes, Some(size_hint))?;
round_image(&mut color_image);
Ok(color_image)
} else if content_type.starts_with("image/") {
#[cfg(feature = "profiling")]
puffin::profile_scope!("load_from_memory");
profiling::scope!("load_from_memory");
let dyn_image = image::load_from_memory(&response.bytes)?;
Ok(process_pfp_bitmap(imgtyp, dyn_image))
} else {

View File

@@ -63,6 +63,7 @@ impl egui::Widget for Mention<'_> {
}
#[allow(clippy::too_many_arguments)]
#[profiling::function]
fn mention_ui(
ndb: &Ndb,
img_cache: &mut Images,
@@ -72,9 +73,6 @@ fn mention_ui(
size: f32,
selectable: bool,
) -> egui::InnerResponse<Option<NoteAction>> {
#[cfg(feature = "profiling")]
puffin::profile_function!();
let link_color = ui.visuals().hyperlink_color;
ui.horizontal(|ui| {

View File

@@ -60,6 +60,7 @@ impl egui::Widget for &mut NoteContents<'_, '_> {
/// Render an inline note preview with a border. These are used when
/// notes are references within a note
#[allow(clippy::too_many_arguments)]
#[profiling::function]
pub fn render_note_preview(
ui: &mut egui::Ui,
note_context: &mut NoteContext,
@@ -68,9 +69,6 @@ pub fn render_note_preview(
parent: NoteKey,
note_options: NoteOptions,
) -> NoteResponse {
#[cfg(feature = "profiling")]
puffin::profile_function!();
let note = if let Ok(note) = note_context.ndb.get_note_by_id(txn, id) {
// TODO: support other preview kinds
if note.kind() == 1 {
@@ -118,6 +116,7 @@ pub fn render_note_preview(
}
#[allow(clippy::too_many_arguments)]
#[profiling::function]
fn render_note_contents(
ui: &mut egui::Ui,
note_context: &mut NoteContext,
@@ -125,9 +124,6 @@ fn render_note_contents(
note: &Note,
options: NoteOptions,
) -> NoteResponse {
#[cfg(feature = "profiling")]
puffin::profile_function!();
let note_key = note.key().expect("todo: implement non-db notes");
let selectable = options.has_selectable_text();
let mut images: Vec<(String, MediaCacheType)> = vec![];
@@ -197,8 +193,6 @@ fn render_note_contents(
},
BlockType::Hashtag => {
#[cfg(feature = "profiling")]
puffin::profile_scope!("hashtag contents");
let resp = ui.colored_label(link_color, format!("#{}", block.as_str()));
if resp.clicked() {
@@ -223,8 +217,6 @@ fn render_note_contents(
}
};
if hide_media || !found_supported() {
#[cfg(feature = "profiling")]
puffin::profile_scope!("url contents");
ui.add(Hyperlink::from_label_and_url(
RichText::new(block.as_str()).color(link_color),
block.as_str(),
@@ -233,8 +225,6 @@ fn render_note_contents(
}
BlockType::Text => {
#[cfg(feature = "profiling")]
puffin::profile_scope!("text contents");
if options.has_scramble_text() {
ui.add(egui::Label::new(rot13(block.as_str())).selectable(selectable));
} else {

View File

@@ -97,10 +97,8 @@ impl NoteContextButton {
Self::max_distance_between_circles() / Self::expansion_multiple()
}
#[profiling::function]
pub fn show(ui: &mut egui::Ui, note_key: NoteKey, put_at: Rect) -> egui::Response {
#[cfg(feature = "profiling")]
puffin::profile_function!();
let id = ui.id().with(("more_options_anim", note_key));
let min_radius = Self::min_radius();
@@ -138,13 +136,11 @@ impl NoteContextButton {
response
}
#[profiling::function]
pub fn menu(
ui: &mut egui::Ui,
button_response: egui::Response,
) -> Option<NoteContextSelection> {
#[cfg(feature = "profiling")]
puffin::profile_function!();
let mut context_selection: Option<NoteContextSelection> = None;
stationary_arbitrary_menu_button(ui, button_response, |ui| {

View File

@@ -307,15 +307,13 @@ impl<'a, 'd> NoteView<'a, 'd> {
}
}
#[profiling::function]
fn note_header(
ui: &mut egui::Ui,
note_cache: &mut NoteCache,
note: &Note,
profile: &Result<nostrdb::ProfileRecord<'_>, nostrdb::Error>,
) {
#[cfg(feature = "profiling")]
puffin::profile_function!();
let note_key = note.key().unwrap();
ui.horizontal(|ui| {
@@ -327,9 +325,8 @@ impl<'a, 'd> NoteView<'a, 'd> {
});
}
#[profiling::function]
fn show_standard(&mut self, ui: &mut egui::Ui) -> NoteResponse {
#[cfg(feature = "profiling")]
puffin::profile_function!();
let note_key = self.note.key().expect("todo: support non-db notes");
let txn = self.note.txn().expect("todo: support non-db notes");
@@ -563,14 +560,12 @@ fn note_hitbox_clicked(
}
}
#[profiling::function]
fn render_note_actionbar(
ui: &mut egui::Ui,
note_id: &[u8; 32],
note_key: NoteKey,
) -> egui::InnerResponse<Option<NoteAction>> {
#[cfg(feature = "profiling")]
puffin::profile_function!();
ui.horizontal(|ui| {
let reply_resp = reply_button(ui, note_key);
let quote_resp = quote_repost_button(ui, note_key);
@@ -590,14 +585,12 @@ fn secondary_label(ui: &mut egui::Ui, s: impl Into<String>) {
ui.add(Label::new(RichText::new(s).size(10.0).color(color)));
}
#[profiling::function]
fn render_reltime(
ui: &mut egui::Ui,
note_cache: &mut CachedNote,
before: bool,
) -> egui::InnerResponse<()> {
#[cfg(feature = "profiling")]
puffin::profile_function!();
ui.horizontal(|ui| {
if before {
secondary_label(ui, "");

View File

@@ -8,6 +8,7 @@ use nostrdb::{Note, NoteReply, Transaction};
use super::{contents::NoteContext, NoteOptions};
#[must_use = "Please handle the resulting note action"]
#[profiling::function]
pub fn reply_desc(
ui: &mut egui::Ui,
txn: &Transaction,
@@ -15,9 +16,6 @@ pub fn reply_desc(
note_context: &mut NoteContext,
note_options: NoteOptions,
) -> Option<NoteAction> {
#[cfg(feature = "profiling")]
puffin::profile_function!();
let mut note_action: Option<NoteAction> = None;
let size = 10.0;
let selectable = false;

View File

@@ -80,6 +80,7 @@ impl<'cache, 'url> ProfilePic<'cache, 'url> {
}
}
#[profiling::function]
fn render_pfp(
ui: &mut egui::Ui,
img_cache: &mut Images,
@@ -87,9 +88,6 @@ fn render_pfp(
ui_size: f32,
border: Option<Stroke>,
) -> egui::Response {
#[cfg(feature = "profiling")]
puffin::profile_function!();
// We will want to downsample these so it's not blurry on hi res displays
let img_size = 128u32;
@@ -116,15 +114,13 @@ fn render_pfp(
)
}
#[profiling::function]
fn pfp_image(
ui: &mut egui::Ui,
img: &TextureHandle,
size: f32,
border: Option<Stroke>,
) -> egui::Response {
#[cfg(feature = "profiling")]
puffin::profile_function!();
let (rect, response) = ui.allocate_at_least(vec2(size, size), Sense::hover());
if let Some(stroke) = border {
draw_bg_border(ui, rect.center(), size, stroke);

View File

@@ -25,6 +25,7 @@ pub fn update_from_columns(
}
}
#[profiling::function]
pub fn get_unknown_ids(
txn: &Transaction,
unknown_ids: &mut UnknownIds,
@@ -32,9 +33,6 @@ pub fn get_unknown_ids(
ndb: &Ndb,
note_cache: &mut NoteCache,
) -> Result<()> {
#[cfg(feature = "profiling")]
puffin::profile_function!();
let mut new_cached_notes: Vec<(NoteKey, CachedNote)> = vec![];
for (_kind, timeline) in timeline_cache.timelines.iter() {

View File

@@ -1,5 +1,5 @@
#!/usr/bin/env bash
# pass --mobile for mobile previews
#RUST_LOG=info cargo run --bin ui_preview --features profiling --release -- "$@"
#RUST_LOG=info cargo run --bin ui_preview --features puffin --release -- "$@"
cargo run --bin ui_preview --release -- "$@"