mirror of
https://github.com/zed-industries/zed.git
synced 2026-05-31 19:05:00 +07:00
Disable miniprofiler by default (#54645)
Needs https://github.com/zed-industries/zed/pull/54635 for the profile overrides added into default settings json to work. Part of https://github.com/zed-industries/zed/issues/48968 Another part of the fix related seems to be https://github.com/zed-industries/zed/pull/45669 ? Using the steps from the issue and profiling on macOs had shown that Zed has 2 memory "leaks" in play when a certain file is being rewritten a lot of times. * First, the thread profiler registers a lot of tasks' data and fills its buffer to the limit: <img width="3456" height="2158" alt="image" src="https://github.com/user-attachments/assets/f183312d-4389-4072-8915-d54e60419b08" /> * Second, if the buffer gets open, the undo history fragments start to creep up infinitely: <img width="3456" height="2158" alt="image" src="https://github.com/user-attachments/assets/61a2b66b-81fd-4973-9c3c-c339f886d9b2" /> The PR aims to solve the first issue by disabling the profiling by default, yet leaving the way to turn in on quickly with settings. The memory usage profiling shows that the memory usage is now dynamically affected by the new setting: <img width="2032" height="1136" alt="image" src="https://github.com/user-attachments/assets/8a6c76b9-6fb7-44bc-ac1d-3c34afe7c575" /> While the test directory being thrashed with the script from the issue, * first, Zed starts with the profiling disabled * then gets the profiling enabled which results in the memory growth close to 1 minute mark of the screenshot * last, the profiling gets disabled again, releasing all the memory accumulated Release Notes: - Improved Zed's default memory usage
This commit is contained in:
parent
2f445d4059
commit
fca4d60ce1
9 changed files with 194 additions and 2 deletions
2
Cargo.lock
generated
2
Cargo.lock
generated
|
|
@ -10790,9 +10790,11 @@ checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a"
|
|||
name = "miniprofiler_ui"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"command_palette_hooks",
|
||||
"gpui",
|
||||
"rpc",
|
||||
"serde_json",
|
||||
"settings",
|
||||
"smol",
|
||||
"theme_settings",
|
||||
"util",
|
||||
|
|
|
|||
|
|
@ -2510,6 +2510,11 @@
|
|||
// Mostly useful for developers who are managing multiple instances of Zed.
|
||||
"nightly": {
|
||||
// "theme": "Andromeda"
|
||||
"instrumentation": {
|
||||
"performance_profiler": {
|
||||
"enabled": true,
|
||||
},
|
||||
},
|
||||
},
|
||||
// Settings overrides to use when using Zed Stable.
|
||||
// Mostly useful for developers who are managing multiple instances of Zed.
|
||||
|
|
@ -2520,6 +2525,11 @@
|
|||
// Mostly useful for developers who are managing multiple instances of Zed.
|
||||
"dev": {
|
||||
// "theme": "Andromeda"
|
||||
"instrumentation": {
|
||||
"performance_profiler": {
|
||||
"enabled": true,
|
||||
},
|
||||
},
|
||||
},
|
||||
// Settings overrides to use when using Linux.
|
||||
"linux": {},
|
||||
|
|
@ -2639,4 +2649,16 @@
|
|||
//
|
||||
// Example: {"log": {"client": "warn"}}
|
||||
"log": {},
|
||||
|
||||
// Configuration for developer-oriented instrumentation tools that can be
|
||||
// toggled at runtime.
|
||||
"instrumentation": {
|
||||
// Performance profiler, accessed via the `zed: open performance profiler`
|
||||
// action. Collects timing data for foreground and background executor
|
||||
// tasks. Enabling this may lead to increased memory usage, hence it's
|
||||
// disabled by default for regular builds.
|
||||
"performance_profiler": {
|
||||
"enabled": false,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,7 +3,10 @@ use std::{
|
|||
cell::LazyCell,
|
||||
collections::{HashMap, VecDeque},
|
||||
hash::{DefaultHasher, Hash, Hasher},
|
||||
sync::Arc,
|
||||
sync::{
|
||||
Arc,
|
||||
atomic::{AtomicBool, Ordering},
|
||||
},
|
||||
thread::ThreadId,
|
||||
};
|
||||
|
||||
|
|
@ -348,6 +351,9 @@ impl Drop for ThreadTimings {
|
|||
|
||||
#[doc(hidden)]
|
||||
pub fn add_task_timing(timing: TaskTiming) {
|
||||
if !PROFILER_ENABLED.load(Ordering::Acquire) {
|
||||
return;
|
||||
}
|
||||
THREAD_TIMINGS.with(|timings| {
|
||||
timings.lock().add_task_timing(timing);
|
||||
});
|
||||
|
|
@ -357,3 +363,28 @@ pub fn add_task_timing(timing: TaskTiming) {
|
|||
pub fn get_current_thread_task_timings() -> ThreadTaskTimings {
|
||||
THREAD_TIMINGS.with(|timings| timings.lock().get_thread_task_timings())
|
||||
}
|
||||
|
||||
static PROFILER_ENABLED: AtomicBool = AtomicBool::new(false);
|
||||
|
||||
/// Enables or disables task timing collection at runtime.
|
||||
///
|
||||
/// When transitioning from enabled to disabled, `add_task_timing` becomes a
|
||||
/// no-op and the existing per-thread buffers are cleared so stale data isn't
|
||||
/// reported after a later re-enable. Calls with the current value are a no-op.
|
||||
pub fn set_enabled(enabled: bool) -> bool {
|
||||
if PROFILER_ENABLED.swap(enabled, Ordering::AcqRel) == enabled {
|
||||
return false;
|
||||
}
|
||||
|
||||
if !enabled {
|
||||
for global in GLOBAL_THREAD_TIMINGS.lock().iter() {
|
||||
if let Some(timings) = global.timings.upgrade() {
|
||||
let mut timings = timings.lock();
|
||||
timings.timings.clear();
|
||||
timings.timings.shrink_to_fit();
|
||||
timings.total_pushed = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
true
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,8 +12,10 @@ workspace = true
|
|||
path = "src/miniprofiler_ui.rs"
|
||||
|
||||
[dependencies]
|
||||
command_palette_hooks.workspace = true
|
||||
gpui.workspace = true
|
||||
rpc.workspace = true
|
||||
settings.workspace = true
|
||||
theme_settings.workspace = true
|
||||
zed_actions.workspace = true
|
||||
workspace.workspace = true
|
||||
|
|
|
|||
|
|
@ -5,14 +5,17 @@ use std::{
|
|||
time::{Duration, Instant},
|
||||
};
|
||||
|
||||
use command_palette_hooks::CommandPaletteFilter;
|
||||
use gpui::{
|
||||
App, AppContext, ClipboardItem, Context, Div, Entity, Hsla, InteractiveElement,
|
||||
ParentElement as _, ProfilingCollector, Render, SerializedLocation, SerializedTaskTiming,
|
||||
SerializedThreadTaskTimings, SharedString, StatefulInteractiveElement, Styled, Task,
|
||||
ThreadTimingsDelta, TitlebarOptions, UniformListScrollHandle, WeakEntity, WindowBounds,
|
||||
WindowOptions, div, prelude::FluentBuilder, px, relative, size, uniform_list,
|
||||
WindowOptions, div, prelude::FluentBuilder, profiler, px, relative, size, uniform_list,
|
||||
};
|
||||
use rpc::{AnyProtoClient, proto};
|
||||
use settings::{RegisterSetting, Settings, SettingsContent, SettingsStore};
|
||||
use std::any::TypeId;
|
||||
use util::ResultExt;
|
||||
use workspace::{
|
||||
Workspace,
|
||||
|
|
@ -61,7 +64,49 @@ impl ProfileSource {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, Default, RegisterSetting)]
|
||||
struct PerformanceProfilerSettings {
|
||||
enabled: bool,
|
||||
}
|
||||
|
||||
impl Settings for PerformanceProfilerSettings {
|
||||
fn from_settings(content: &SettingsContent) -> Self {
|
||||
let instrumentation = content.instrumentation.as_ref().unwrap();
|
||||
let profiler = instrumentation.performance_profiler.as_ref().unwrap();
|
||||
Self {
|
||||
enabled: profiler.enabled.unwrap(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn init(startup_time: Instant, cx: &mut App) {
|
||||
let initial_enabled = PerformanceProfilerSettings::get_global(cx).enabled;
|
||||
profiler::set_enabled(initial_enabled);
|
||||
update_command_palette_filter(initial_enabled, cx);
|
||||
|
||||
cx.observe_global::<SettingsStore>(|cx| {
|
||||
let enabled = PerformanceProfilerSettings::get_global(cx).enabled;
|
||||
// `set_enabled` reports whether the value actually changed, so skip the
|
||||
// filter update and window cleanup on the common no-op path — the
|
||||
// settings observer fires for every settings change.
|
||||
if !profiler::set_enabled(enabled) {
|
||||
return;
|
||||
}
|
||||
update_command_palette_filter(enabled, cx);
|
||||
if !enabled {
|
||||
for window in cx
|
||||
.windows()
|
||||
.into_iter()
|
||||
.filter_map(|window| window.downcast::<ProfilerWindow>())
|
||||
{
|
||||
window
|
||||
.update(cx, |_, window, _| window.remove_window())
|
||||
.ok();
|
||||
}
|
||||
}
|
||||
})
|
||||
.detach();
|
||||
|
||||
cx.observe_new(move |workspace: &mut workspace::Workspace, _, cx| {
|
||||
let workspace_handle = cx.entity().downgrade();
|
||||
workspace.register_action(move |_workspace, _: &OpenPerformanceProfiler, window, cx| {
|
||||
|
|
@ -71,6 +116,17 @@ pub fn init(startup_time: Instant, cx: &mut App) {
|
|||
.detach();
|
||||
}
|
||||
|
||||
fn update_command_palette_filter(enabled: bool, cx: &mut App) {
|
||||
CommandPaletteFilter::update_global(cx, |filter, _| {
|
||||
let action = [TypeId::of::<OpenPerformanceProfiler>()];
|
||||
if enabled {
|
||||
filter.show_action_types(&action);
|
||||
} else {
|
||||
filter.hide_action_types(&action);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
fn open_performance_profiler(
|
||||
startup_time: Instant,
|
||||
workspace_handle: WeakEntity<Workspace>,
|
||||
|
|
|
|||
|
|
@ -222,6 +222,7 @@ impl VsCodeSettings {
|
|||
which_key: None,
|
||||
modeline_lines: None,
|
||||
feature_flags: None,
|
||||
instrumentation: None,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -223,6 +223,33 @@ pub struct SettingsContent {
|
|||
|
||||
/// Local overrides for feature flags, keyed by flag name.
|
||||
pub feature_flags: Option<FeatureFlagsMap>,
|
||||
|
||||
/// Settings for developer-oriented instrumentation tools (profilers,
|
||||
/// tracers, etc.) that can be toggled at runtime.
|
||||
pub instrumentation: Option<InstrumentationSettingsContent>,
|
||||
}
|
||||
|
||||
/// Configuration for developer-oriented instrumentation tools that collect
|
||||
/// diagnostic data about a running Zed instance.
|
||||
#[with_fallible_options]
|
||||
#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize, JsonSchema, MergeFrom)]
|
||||
pub struct InstrumentationSettingsContent {
|
||||
/// Configuration for the performance profiler, accessed via the
|
||||
/// `zed: open performance profiler` action.
|
||||
pub performance_profiler: Option<PerformanceProfilerSettingsContent>,
|
||||
}
|
||||
|
||||
/// Configuration for the performance profiler which collects timing data
|
||||
/// for foreground and background executor tasks.
|
||||
#[with_fallible_options]
|
||||
#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize, JsonSchema, MergeFrom)]
|
||||
pub struct PerformanceProfilerSettingsContent {
|
||||
/// Whether to collect timing data for foreground and background executor
|
||||
/// tasks. Enabling this may lead to increased memory usage, hence it's
|
||||
/// disabled by default for regular builds.
|
||||
///
|
||||
/// Default: false
|
||||
pub enabled: Option<bool>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Clone, PartialEq, Eq, Serialize, Deserialize, MergeFrom)]
|
||||
|
|
|
|||
|
|
@ -100,6 +100,31 @@ fn developer_page() -> SettingsPage {
|
|||
files: USER,
|
||||
render: crate::pages::render_feature_flags_page,
|
||||
}),
|
||||
SettingsPageItem::SectionHeader("Instrumentation"),
|
||||
SettingsPageItem::SettingItem(SettingItem {
|
||||
title: "Performance Profiler",
|
||||
description: "Collect timing data for foreground and background executor tasks so they can be inspected via `zed: open performance profiler`. May lead to increased memory usage.",
|
||||
field: Box::new(SettingField {
|
||||
json_path: Some("instrumentation.performance_profiler.enabled"),
|
||||
pick: |settings_content| {
|
||||
settings_content
|
||||
.instrumentation
|
||||
.as_ref()
|
||||
.and_then(|i| i.performance_profiler.as_ref())
|
||||
.and_then(|p| p.enabled.as_ref())
|
||||
},
|
||||
write: |settings_content, value| {
|
||||
settings_content
|
||||
.instrumentation
|
||||
.get_or_insert_default()
|
||||
.performance_profiler
|
||||
.get_or_insert_default()
|
||||
.enabled = value;
|
||||
},
|
||||
}),
|
||||
metadata: None,
|
||||
files: USER,
|
||||
}),
|
||||
]),
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3089,6 +3089,32 @@ If you wish to exclude certain hosts from using the proxy, set the `NO_PROXY` en
|
|||
}
|
||||
```
|
||||
|
||||
## Instrumentation
|
||||
|
||||
- Description: Configuration for developer-oriented instrumentation tools (profilers, tracers, etc.) that can be toggled at runtime.
|
||||
- Setting: `instrumentation`
|
||||
- Default:
|
||||
|
||||
```json
|
||||
{
|
||||
"instrumentation": {
|
||||
"performance_profiler": {
|
||||
"enabled": false
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Performance Profiler
|
||||
|
||||
- Description: Collects timing data for foreground and background executor tasks so they can be inspected via the `zed: open performance profiler` action. Enabling this may lead to increased memory usage, hence it's disabled by default for regular builds.
|
||||
- Setting: `instrumentation.performance_profiler.enabled`
|
||||
- Default: `false`
|
||||
|
||||
**Options**
|
||||
|
||||
`boolean` values
|
||||
|
||||
## Profiles
|
||||
|
||||
- Description: Configuration profiles that can be temporarily applied on top of existing settings or Zed's defaults.
|
||||
|
|
|
|||
Loading…
Reference in a new issue