# Performance Considerations for Dear ImGui: Optimization Guide and Best Practices

> Optimize Dear ImGui performance with this guide. Learn best practices to maintain high frame rates and avoid bottlenecks with efficient rendering and callback design.

- Repository: [omar/imgui](https://github.com/ocornut/imgui)
- Tags: best-practices
- Published: 2026-07-27

---

**Dear ImGui maintains high frame rates through single-pass immediate-mode rendering, zero heap allocations during normal frames, and linear-time algorithms, though developers must avoid per-frame memory allocations and heavy computations inside UI callbacks to prevent bottlenecks.**

Dear ImGui (ocornut/imgui) is deliberately engineered as a single-pass, immediate-mode UI library optimized for high on-the-fly performance while maintaining API simplicity. Understanding the performance considerations for Dear ImGui ensures your integration remains responsive in Debug builds and avoids common architectural pitfalls that violate the library's zero-allocation philosophy.

## Architectural Foundations for High Performance

### Single-Pass Immediate Mode Rendering

In [`imgui.cpp`](https://github.com/ocornut/imgui/blob/main/imgui.cpp) (lines 215-218), the core architecture emphasizes that UI code runs **once per frame** and directly emits draw-lists without constructing or traversing a retained UI tree. This design eliminates the overhead of scene graph updates and multiple traversal passes found in retained-mode systems, ensuring that widget rendering cost scales linearly with visible complexity rather than total UI complexity.

### Zero Heap Allocation Policy

The library guarantees **no calls to `malloc` or `free`** during idle frames (imgui.cpp lines 221-224). Memory allocations occur only when users explicitly create persistent data structures like `ImVector`, eliminating garbage collection pauses and heap fragmentation during the UI loop. This constraint ensures predictable frame timings regardless of runtime duration.

### Linear-Time Algorithms and Early-Out Optimization

All internal searches, layout passes, and clipping operations use **O(N) or constant-time algorithms** (imgui.cpp lines 221-224). Additionally, functions such as `BeginPopup` implement cheap size comparisons to skip work entirely when popups cannot be displayed (imgui.cpp line 12684), preventing unnecessary draw-list generation for hidden UI elements.

### POD Containers and Minimal C++ Features

The `ImVector` container defined in [`imgui_internal.h`](https://github.com/ocornut/imgui/blob/main/imgui_internal.h) stores plain-old-data without invoking constructors or destructors, enabling faster insertion and iteration than standard library containers. The codebase deliberately avoids templates, exceptions, and RTTI (imgui.cpp lines 260-268) to improve compile times, reduce binary size, and facilitate cross-language bindings.

## Common Performance Pitfalls

### Development-Time Widgets in Production

Calling `ImGui::ShowDemoWindow()` every frame renders hundreds of widgets and consumes significant CPU/GPU time. Remove demo code from production builds or wrap it with compile-time flags such as `#ifdef IMGUI_SHOW_DEMO`.

### Per-Frame Memory Allocations

Creating temporary `ImVector` or `std::string` objects inside the UI loop triggers heap allocations, breaking the library's "no malloc" rule. Reuse containers by declaring them as `static` or calling `clear()` instead of reconstructing them each frame.

### Expensive Computations in UI Callbacks

Performing heavy calculations inside widget callbacks slows the entire frame because UI code executes every frame. Move expensive work outside the ImGui pass or cache results using dirty flags to recompute only when data changes.

### Excessive Clipping Rectangles

Large numbers of overlapping clipping rectangles force the clipping algorithm to scan all rectangles each frame. Keep draw-list depth low and avoid nesting many invisible windows to reduce per-frame clipping overhead in [`imgui_draw.cpp`](https://github.com/ocornut/imgui/blob/main/imgui_draw.cpp).

### Font Rasterization Without Caching

High-resolution fonts without cached atlases require rebuilding glyph data each frame. Pre-bake fonts using `ImFontAtlas::Build()` once during initialization rather than allowing dynamic rebuilding.

### Redundant Layout Calculations

Failing to use `ImGui::SetNextWindowSize()` or `SetNextWindowPos()` for static windows forces the layout engine to recompute sizes every frame. Set these values once and disable auto-fit to eliminate redundant calculations.

## Practical Optimization Techniques

```cpp
// 1️⃣ Create a persistent ImVector for a dynamic list – reuse it each frame.
static ImVector<int> selected_items;
if (ImGui::Button("Clear")) selected_items.clear();
for (int i = 0; i < 1000; ++i) {
    bool selected = selected_items.contains(i);
    if (ImGui::Selectable(("Item " + std::to_string(i)).c_str(), selected))
        selected ? selected_items.erase(selected_items.find(i)) : selected_items.push_back(i);
}

// 2️⃣ Avoid per‑frame allocations in heavy widgets.
static char buf[256];
ImGui::InputText("Search##my", buf, IM_ARRAYSIZE(buf));

// 3️⃣ Use early‑out flags when a window is hidden.
if (!ImGui::Begin("DebugOverlay", nullptr, ImGuiWindowFlags_NoInputs))
    return;               // No draw‑list work for hidden overlay.
ImGui::Text("FPS: %.1f", ImGui::GetIO().Framerate);
ImGui::End();

// 4️⃣ Cache complex calculations outside the UI pass.
static float expensive_result = 0.0f;
if (need_recalc) expensive_result = VeryExpensiveFunction();
ImGui::Text("Result = %.3f", expensive_result);

```

**Key implementation details:**

- **Static storage** removes per-frame allocation overhead for containers and buffers.
- **Early return** when `ImGui::Begin` returns `false` prevents any draw-list generation for invisible windows.
- **Pre-allocated char arrays** avoid `std::string` allocations inside `InputText` widgets.

## Critical Source Files for Performance Analysis

| File | Role |
|------|------|
| [`imgui.cpp`](https://github.com/ocornut/imgui/blob/main/imgui.cpp) | Contains the frame life-cycle, zero-allocation guarantees, and performance notes (lines 215-224). |
| [`imgui_internal.h`](https://github.com/ocornut/imgui/blob/main/imgui_internal.h) | Defines `ImVector`, clipping algorithms, and low-level draw-list structures. |
| [`imgui_draw.cpp`](https://github.com/ocornut/imgui/blob/main/imgui_draw.cpp) | Translates UI commands to vertex/index buffers; manages clipping rectangle scans. |
| [`imgui_widgets.cpp`](https://github.com/ocornut/imgui/blob/main/imgui_widgets.cpp) | Implements core widgets with early-out checks like `BeginPopup`. |
| [`backends/imgui_impl_opengl3.cpp`](https://github.com/ocornut/imgui/blob/main/backends/imgui_impl_opengl3.cpp) | Reference OpenGL backend showing efficient draw-list submission. |
| [`backends/imgui_impl_vulkan.cpp`](https://github.com/ocornut/imgui/blob/main/backends/imgui_impl_vulkan.cpp) | Demonstrates zero-copy vertex buffers and command-buffer recycling. |

## Summary

- **Never allocate** memory inside the UI pass unless explicitly required for persistent data.
- **Reuse containers** (`ImVector`, fixed-size arrays) across frames using `clear()` rather than reconstruction.
- **Guard heavy widgets** with `if (ImGui::Begin(...))` early-out patterns to skip hidden windows.
- **Cache expensive calculations** outside the ImGui rendering loop to maintain frame budget.
- **Pre-bake font atlases** once at initialization to avoid per-frame glyph rasterization.
- **Set static window sizes** with `SetNextWindowSize()` to prevent redundant layout recomputation.

## Frequently Asked Questions

### Does Dear ImGui perform well in Debug builds?

Yes, the library is specifically designed to run fast even in unoptimized builds. By avoiding heavy C++ abstractions, exceptions, and heap allocations by design, Dear ImGui maintains consistent frame rates regardless of compiler optimization levels, making it ideal for rapid iteration during development.

### Why does my frame rate drop when using many windows?

Each visible window adds clipping rectangles and draw-list commands that must be processed every frame. According to the source in [`imgui_draw.cpp`](https://github.com/ocornut/imgui/blob/main/imgui_draw.cpp), the clipping algorithm scans all active rectangles linearly. Use `ImGui::Begin()` early-out patterns when windows are collapsed or invisible, and avoid deeply nested window hierarchies to reduce per-frame scanning costs.

### How can I avoid memory allocations in dynamic lists?

Declare `ImVector` containers as `static` or class members and invoke `clear()` between frames rather than constructing new vectors each frame. This adheres to the zero-allocation philosophy documented in [`imgui.cpp`](https://github.com/ocornut/imgui/blob/main/imgui.cpp) (lines 221-224) and prevents heap fragmentation during long-running sessions.

### Should I rebuild font atlases every frame?

Never. Call `ImFontAtlas::Build()` once during initialization after loading your fonts. Rebuilding atlases per frame triggers expensive glyph rasterization and GPU texture uploads that destroy performance. The font atlas is designed to be persistent, with glyphs cached for the lifetime of the application or until explicit invalidation.