<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Performance on NoneBack</title><link>https://noneback.github.io/tags/performance/</link><description>Recent content in Performance on NoneBack created by</description><generator>Hugo -- gohugo.io</generator><language>en</language><copyright>@NoneBack All rights reserved</copyright><lastBuildDate>Mon, 10 Mar 2025 14:46:54 +0800</lastBuildDate><atom:link href="https://noneback.github.io/tags/performance/index.xml" rel="self" type="application/rss+xml"/><item><title>CPU Profiling: What, How, and When</title><link>https://noneback.github.io/blog/cpu-profiling-what-how-and-when/</link><pubDate>Mon, 10 Mar 2025 14:46:54 +0800</pubDate><guid>https://noneback.github.io/blog/cpu-profiling-what-how-and-when/</guid><description>&lt;h2 id="what-what-is-cpu-profiling"&gt;What: What is CPU Profiling&lt;/h2&gt;
&lt;p&gt;A technique for analyzing program CPU performance. By collecting detailed data during program execution (such as function call frequency, time consumption, call stacks, etc.), it helps developers identify performance bottlenecks and optimize code efficiency. Typically used in performance analysis and root cause diagnosis scenarios.&lt;/p&gt;
&lt;h2 id="how-how-profiling-data-is-collected"&gt;How: How Profiling Data is Collected&lt;/h2&gt;
&lt;p&gt;Common tools like &lt;code&gt;perf&lt;/code&gt; are used to collect process stack information. These tools use sampling statistics to capture stack samples executing on the CPU for performance analysis.&lt;/p&gt;
&lt;div class="code-block" data-language="mermaid"&gt;
&lt;div class="code-block-header"&gt;
&lt;div class="code-block-meta"&gt;
&lt;span class="code-block-language"&gt;mermaid&lt;/span&gt;
&lt;/div&gt;
&lt;div class="code-block-actions"&gt;
&lt;button class="copy-code" type="button" aria-label="Copy"&gt;Copy&lt;/button&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;pre tabindex="0"&gt;&lt;code class="language-mermaid" data-lang="mermaid"&gt;graph TD
A[Sampling Trigger] --&amp;gt;|Interrupt| B[Sampling]
B --&amp;gt;|perf_event/ebpf| C[Process Stack Addresses]
C --&amp;gt;|Address Translation| D[ELF, OFFSET]
D --&amp;gt;|Symbol Resolution| E[Call Stack]
E --&amp;gt;|Formatting| F[pprof/perf script]
F --&amp;gt; |Visualization| G[Flame Graph/Call Graph]&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;h3 id="trigger-mechanisms"&gt;Trigger Mechanisms&lt;/h3&gt;
&lt;p&gt;Generally uses timer interrupts or event-counter-based strategies.&lt;/p&gt;
&lt;h4 id="timer-interrupts"&gt;Timer Interrupts&lt;/h4&gt;
&lt;p&gt;Default fixed frequency (e.g., 99Hz) clock interrupts (SIGPROF). Shorter intervals increase precision but also overhead. Linux perf defaults to 99Hz frequency (≈10.1ms intervals).&lt;/p&gt;
&lt;h4 id="event-counter-sampling"&gt;Event-Counter Sampling&lt;/h4&gt;
&lt;p&gt;Triggers sampling when hardware performance counters (e.g., &lt;code&gt;PERF_COUNT_HW_CPU_CYCLES&lt;/code&gt;) reach thresholds. Useful for analyzing hardware-related events like Cache Misses.&lt;/p&gt;
&lt;h3 id="sampling-methods"&gt;Sampling Methods&lt;/h3&gt;
&lt;p&gt;Typically, the OS kernel-provided interfaces like eBPF or perf_event are used for stack sampling.&lt;/p&gt;
&lt;h4 id="ebpf-approach"&gt;eBPF Approach&lt;/h4&gt;
&lt;p&gt;Using eBPF programs (e.g., bpf_get_stackid), both user-space and kernel-space call stacks can be captured directly without additional stack unwinding. This method retrieves complete stack IP information.&lt;/p&gt;
&lt;h4 id="perf_event-approach"&gt;perf_event Approach&lt;/h4&gt;
&lt;p&gt;The perf_event_open interface (e.g., perf record command) captures the instruction pointer (RIP). However, it only records the currently executing function address, not the full call stack. This means only the function name triggered by the sample can be resolved.&lt;/p&gt;
&lt;p&gt;Example perf record output:&lt;/p&gt;
&lt;div class="code-block" data-language="shell"&gt;
&lt;div class="code-block-header"&gt;
&lt;div class="code-block-meta"&gt;
&lt;span class="code-block-language"&gt;shell&lt;/span&gt;
&lt;/div&gt;
&lt;div class="code-block-actions"&gt;
&lt;button class="copy-code" type="button" aria-label="Copy"&gt;Copy&lt;/button&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-shell" data-lang="shell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;node &lt;span class="m"&gt;3236535&lt;/span&gt; 34397396.208842: &lt;span class="m"&gt;250000&lt;/span&gt; cpu-clock:pppH: 110c800 v8::internal::Heap_CombinedGenerationalAndSharedBarrierSlow+0x0 &lt;span class="o"&gt;(&lt;/span&gt;/root/.vscode-server/cli/servers/Stable-e54c774e0add60467559eb0d1e229c6452cf8447/server/node&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;node &lt;span class="m"&gt;3236535&lt;/span&gt; 34397396.354632: &lt;span class="m"&gt;250000&lt;/span&gt; cpu-clock:pppH: 7f7d63e87ef4 Builtins_LoadIC+0x574 &lt;span class="o"&gt;(&lt;/span&gt;/root/.vscode-server/cli/servers/Stable-e54c774e0add60467559eb0d1e229c6452cf8447/server/node&lt;span class="o"&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;To obtain a full call stack, tools like libunwind perform stack unwinding. For example, &lt;code&gt;perf record -g&lt;/code&gt; generates a full stack trace by unwinding the stack frames.&lt;/p&gt;
&lt;p&gt;Example perf record -g output:&lt;/p&gt;
&lt;div class="code-block" data-language="shell"&gt;
&lt;div class="code-block-header"&gt;
&lt;div class="code-block-meta"&gt;
&lt;span class="code-block-language"&gt;shell&lt;/span&gt;
&lt;/div&gt;
&lt;div class="code-block-actions"&gt;
&lt;button class="copy-code" type="button" aria-label="Copy"&gt;Copy&lt;/button&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-shell" data-lang="shell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;node &lt;span class="m"&gt;3236535&lt;/span&gt; 34397238.259753: &lt;span class="m"&gt;250000&lt;/span&gt; cpu-clock:pppH:
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; 7f7d44339100 &lt;span class="o"&gt;[&lt;/span&gt;unknown&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;/tmp/perf-3236535.map&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; 18ea0dc Builtins_JSEntryTrampoline+0x5c &lt;span class="o"&gt;(&lt;/span&gt;/root/.vscode-server/cli/servers/Stable-e54c774e0add60467559eb0d1e229c6452cf8447/server/node&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; 18e9e03 Builtins_JSEntry+0x83 &lt;span class="o"&gt;(&lt;/span&gt;...&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;...
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; c7d43f node::Start+0x58f &lt;span class="o"&gt;(&lt;/span&gt;...&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; 7f7d6ba14d90 __libc_start_call_main+0x80 &lt;span class="o"&gt;(&lt;/span&gt;/usr/lib/x86_64-linux-gnu/libc.so.6&lt;span class="o"&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;h3 id="address-translation"&gt;Address Translation&lt;/h3&gt;
&lt;p&gt;The sampled address information corresponds to the process&amp;rsquo;s virtual addresses, such as:&lt;/p&gt;
&lt;div class="code-block" data-language="shell"&gt;
&lt;div class="code-block-header"&gt;
&lt;div class="code-block-meta"&gt;
&lt;span class="code-block-language"&gt;shell&lt;/span&gt;
&lt;/div&gt;
&lt;div class="code-block-actions"&gt;
&lt;button class="copy-code" type="button" aria-label="Copy"&gt;Copy&lt;/button&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-shell" data-lang="shell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;7f7d44339100
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;18ea0dc
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;18e9e03
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;106692b
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;10679c4
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;f2a090d
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;c1c738
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;...&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;To resolve these addresses into ELF + OFFSET for symbol translation, we use the memory mapping information from &lt;code&gt;/proc/[pid]/maps&lt;/code&gt;. The key fields in the maps file include:&lt;/p&gt;
&lt;address range&gt; &lt;permissions&gt; &lt;file offset&gt; &lt;inode&gt; &lt;file path&gt;
&lt;p&gt;Example /proc/[pid]/maps entries:&lt;/p&gt;
&lt;div class="code-block" data-language="shell"&gt;
&lt;div class="code-block-header"&gt;
&lt;div class="code-block-meta"&gt;
&lt;span class="code-block-language"&gt;shell&lt;/span&gt;
&lt;/div&gt;
&lt;div class="code-block-actions"&gt;
&lt;button class="copy-code" type="button" aria-label="Copy"&gt;Copy&lt;/button&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-shell" data-lang="shell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;00400000-00b81000 r--p &lt;span class="m"&gt;00000000&lt;/span&gt; fc:03 &lt;span class="m"&gt;550055&lt;/span&gt; /root/.vscode-server/cli/servers/Stable-e54c774e0add60467559eb0d1e229c6452cf8447/server/node
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;7f7d6bf3c000-7f7d6bf3d000 ---p 0021a000 fc:03 &lt;span class="m"&gt;67&lt;/span&gt; /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.30
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;7f7d6bf61000-7f7d6bf63000 r--p &lt;span class="m"&gt;00000000&lt;/span&gt; fc:03 &lt;span class="m"&gt;2928&lt;/span&gt; /usr/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2 &lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;h4 id="translation-process"&gt;Translation Process&lt;/h4&gt;
&lt;ol&gt;
&lt;li&gt;Match the virtual address to the appropriate memory segment in &lt;code&gt;/proc/[pid]/maps&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Calculate the offset within the ELF file using:
&lt;code&gt;offset = virtual_address - segment_start + file_offset&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id="symbol-resolution"&gt;Symbol Resolution&lt;/h3&gt;
&lt;p&gt;After translating virtual addresses into &lt;code&gt;ELF + OFFSET&lt;/code&gt; pairs, the next step is resolving these offsets into human-readable function symbols. This involves leveraging symbol tables or debugging information embedded in the ELF files.&lt;/p&gt;
&lt;h4 id="methods-for-symbol-resolution"&gt;Methods for Symbol Resolution&lt;/h4&gt;
&lt;ol&gt;
&lt;li&gt;Using Symbol Tables
Tools like nm can extract symbol information from the .dynsym (dynamic symbol table) or .symtab (static symbol table) sections of an ELF file.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Example:&lt;/p&gt;
&lt;div class="code-block" data-language="bash"&gt;
&lt;div class="code-block-header"&gt;
&lt;div class="code-block-meta"&gt;
&lt;span class="code-block-language"&gt;bash&lt;/span&gt;
&lt;/div&gt;
&lt;div class="code-block-actions"&gt;
&lt;button class="copy-code" type="button" aria-label="Copy"&gt;Copy&lt;/button&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Extract malloc-related symbols from a Node.js binary&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;nm -D /path/to/node &lt;span class="p"&gt;|&lt;/span&gt; grep malloc
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Output:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;00000000055f9d18 D ares_malloc
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;0000000001f1a2a0 T ares_malloc_data
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;...
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; U malloc@GLIBC_2.2.5&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;ol start="2"&gt;
&lt;li&gt;Using DWARF Debugging Information
DWARF debug data provides richer details, including source file locations and variable scopes. Tools like readelf or addr2line can parse this information.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Example:&lt;/p&gt;
&lt;div class="code-block" data-language="bash"&gt;
&lt;div class="code-block-header"&gt;
&lt;div class="code-block-meta"&gt;
&lt;span class="code-block-language"&gt;bash&lt;/span&gt;
&lt;/div&gt;
&lt;div class="code-block-actions"&gt;
&lt;button class="copy-code" type="button" aria-label="Copy"&gt;Copy&lt;/button&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Extract function names and source locations from DWARF info&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;readelf --debug-dump&lt;span class="o"&gt;=&lt;/span&gt;info /path/to/node &lt;span class="p"&gt;|&lt;/span&gt; grep &lt;span class="s2"&gt;&amp;#34;DW_AT_name&amp;#34;&lt;/span&gt; -A3
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Output:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&amp;lt;1&amp;gt;&amp;lt;1980&amp;gt;: DW_AT_name: uv__make_close_pending
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; DW_AT_decl_file: &lt;span class="m"&gt;19&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; DW_AT_decl_line: &lt;span class="m"&gt;247&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;ol start="3"&gt;
&lt;li&gt;Demangling C++ Symbols
C++ symbols are often mangled (encoded) for uniqueness. Tools like c++filt restore human-readable names.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Example:&lt;/p&gt;
&lt;div class="code-block" data-language="bash"&gt;
&lt;div class="code-block-header"&gt;
&lt;div class="code-block-meta"&gt;
&lt;span class="code-block-language"&gt;bash&lt;/span&gt;
&lt;/div&gt;
&lt;div class="code-block-actions"&gt;
&lt;button class="copy-code" type="button" aria-label="Copy"&gt;Copy&lt;/button&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Demangle a mangled symbol&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;_ZN4node14ThreadPoolWork12ScheduleWorkEv&amp;#34;&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; c++filt
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Output:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;node::ThreadPoolWork::ScheduleWork&lt;span class="o"&gt;()&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;h3 id="stack-output-formatting"&gt;Stack Output Formatting&lt;/h3&gt;
&lt;p&gt;Resolved stack traces are formatted for analysis tools like pprof or perf script. Additional metadata (e.g., container ID, service type) may be included for aggregation.&lt;/p&gt;
&lt;h3 id="data-visualization"&gt;Data Visualization&lt;/h3&gt;
&lt;p&gt;All those data above will eventually be rendered as flamegraph or call-chain graph.&lt;/p&gt;
&lt;h2 id="when-when-to-use-cpu-profiling-tools"&gt;When: When to Use CPU Profiling Tools&lt;/h2&gt;
&lt;p&gt;CPU profiling is most effective when analyzing CPU-bound performance issues. Below are common scenarios and their workflows:&lt;/p&gt;
&lt;div class="code-block" data-language="mermaid"&gt;
&lt;div class="code-block-header"&gt;
&lt;div class="code-block-meta"&gt;
&lt;span class="code-block-language"&gt;mermaid&lt;/span&gt;
&lt;/div&gt;
&lt;div class="code-block-actions"&gt;
&lt;button class="copy-code" type="button" aria-label="Copy"&gt;Copy&lt;/button&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;pre tabindex="0"&gt;&lt;code class="language-mermaid" data-lang="mermaid"&gt;graph TD
A[Observe anomaly: Unavailability/Performance Jitter] --&amp;gt; B[Identify target process &amp;amp; timeframe]
B --&amp;gt; C[Check core metrics: CPU, memory, disk, QPS]
C --&amp;gt; D{Is CPU the bottleneck?}
D --&amp;gt;|Yes| E[Profile CPU stacks]
D --&amp;gt;|No| F[Use alternative tools e.g., memory profiler, I/O tracer]
E --&amp;gt; G[Analyze flame graphs/call chains]
G --&amp;gt; H[Root cause identified]&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Scenario Category&lt;/th&gt;
&lt;th&gt;Typical Symptoms&lt;/th&gt;
&lt;th&gt;Tool Choices&lt;/th&gt;
&lt;th&gt;Data Collection Strategy&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Sudden CPU Spikes&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Sawtooth-shaped CPU peaks in monitoring charts.&lt;/td&gt;
&lt;td&gt;Continuous Profiling Systems&lt;/td&gt;
&lt;td&gt;Capture 5-minute context before/after spikes + regular sampling.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Version Performance Regression&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;QPS/TPS drops post-deployment.&lt;/td&gt;
&lt;td&gt;Differential FlameGraph&lt;/td&gt;
&lt;td&gt;A/B version comparison sampling under identical loads.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;High CpuSys&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Elevated OS kernel CPU usage causing host instability.&lt;/td&gt;
&lt;td&gt;FlameGraph/Call-Chain Graph&lt;/td&gt;
&lt;td&gt;Regular sampling with kernel stack analysis.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 id="when-cpu-profiling-is-not-suitable"&gt;When CPU Profiling Is NOT Suitable&lt;/h3&gt;
&lt;p&gt;For non-CPU-bound issues, profiling data may have limited value. Alternative tools are recommended:&lt;/p&gt;
&lt;div class="code-block" data-language="mermaid"&gt;
&lt;div class="code-block-header"&gt;
&lt;div class="code-block-meta"&gt;
&lt;span class="code-block-language"&gt;mermaid&lt;/span&gt;
&lt;/div&gt;
&lt;div class="code-block-actions"&gt;
&lt;button class="copy-code" type="button" aria-label="Copy"&gt;Copy&lt;/button&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;pre tabindex="0"&gt;&lt;code class="language-mermaid" data-lang="mermaid"&gt;graph TD
A[CPU Profiling Limitations] --&amp;gt; B[Memory Bottlenecks]
A --&amp;gt; C[I/O-Bound Workloads]
A --&amp;gt; D[Lock Contention]
A --&amp;gt; E[Short-lived Processes]
B --&amp;gt;|Signs| B1(High page faults, GC pauses)
B --&amp;gt;|Tools| B2{{Heap profiler: e.g., pprof, vmstat}}
C --&amp;gt;|Signs| C1(High iowait, low CPU utilization)
C --&amp;gt;|Tools| C2{{iostat, blktrace}}
D --&amp;gt;|Signs| D1(High context switches, sys%)
D --&amp;gt;|Tools| D2{{perf lock, lockstat}}
E --&amp;gt;|Signs| E1(Process lifetime &amp;lt; sampling interval)
E --&amp;gt;|Tools| E2{{execsnoop, dynamic tracing:e.g., bpftrace}}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;h2 id="references"&gt;References&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;code expaple：&lt;a href="https://github.com/noneback/doctor"&gt;https://github.com/noneback/doctor&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;stack unwind: &lt;a href="https://zhuanlan.zhihu.com/p/460686470"&gt;https://zhuanlan.zhihu.com/p/460686470&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;proc_pid_maps: &lt;a href="https://man7.org/linux/man-pages/man5/proc_pid_maps.5.html"&gt;https://man7.org/linux/man-pages/man5/proc_pid_maps.5.html&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;dwarf: &lt;a href="https://www.hitzhangjie.pro/debugger101.io/8-dwarf/"&gt;https://www.hitzhangjie.pro/debugger101.io/8-dwarf/&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;demange &amp;amp; mangle: &lt;a href="https://www.cnblogs.com/BloodAndBone/p/7912179.html"&gt;https://www.cnblogs.com/BloodAndBone/p/7912179.html&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;</description></item><item><title>CPU False Sharing</title><link>https://noneback.github.io/blog/cpu%E4%BC%AA%E5%85%B1%E4%BA%AB/</link><pubDate>Sun, 02 May 2021 13:47:30 +0800</pubDate><guid>https://noneback.github.io/blog/cpu%E4%BC%AA%E5%85%B1%E4%BA%AB/</guid><description>&lt;p&gt;The motivation for this post comes from an interview question I was asked: What is CPU false sharing?&lt;/p&gt;
&lt;h2 id="cpu-cache"&gt;CPU Cache&lt;/h2&gt;
&lt;p&gt;Let&amp;rsquo;s start by discussing CPU cache.&lt;/p&gt;
&lt;p&gt;CPU cache is a type of storage medium introduced to bridge the speed gap between the CPU and main memory. In the pyramid-shaped storage hierarchy, it is located just below CPU registers. Its capacity is much smaller than that of main memory, but its speed can be close to the processor&amp;rsquo;s frequency.&lt;/p&gt;
&lt;p&gt;The effectiveness of caching relies on the principle of temporal and spatial locality.&lt;/p&gt;
&lt;p&gt;When the processor issues a memory access request, it first checks if the requested data is in the cache. If it is (a cache hit), it directly returns the data without accessing main memory. If it isn&amp;rsquo;t (a cache miss), it loads the data from main memory into the cache before returning it to the processor.&lt;/p&gt;
&lt;h2 id="cpu-cache-architecture"&gt;CPU Cache Architecture&lt;/h2&gt;
&lt;p&gt;There are usually three levels of cache between the CPU and main memory. The closer the cache is to the CPU, the faster it is but the smaller its capacity. When accessing data, the CPU first checks &lt;strong&gt;L1&lt;/strong&gt;, then &lt;strong&gt;L2&lt;/strong&gt;, and finally &lt;strong&gt;L3&lt;/strong&gt;. If the data isn&amp;rsquo;t in any of these caches, it must be fetched from main memory.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://i.loli.net/2021/05/12/CSi7FqmcUZk2LTH.png" alt="Cache Architecture" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;L1&lt;/strong&gt; is close to the CPU core that uses it. L1 and L2 caches can only be used by a single CPU core.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;L3&lt;/strong&gt; can be shared by all CPU cores in a socket.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="cpu-cache-line"&gt;CPU Cache Line&lt;/h2&gt;
&lt;p&gt;Caches operate on the basis of &lt;strong&gt;cache lines&lt;/strong&gt;, which are the smallest unit of data transfer between the cache and main memory, typically 64 bytes. A cache line effectively references a block of memory (64 bytes).&lt;/p&gt;
&lt;p&gt;Loading a cache line has the advantage that if the required data is located close to each other, it can be accessed without reloading the cache.&lt;/p&gt;
&lt;p&gt;However, it can also lead to a problem known as &lt;strong&gt;CPU false sharing&lt;/strong&gt;.&lt;/p&gt;
&lt;h2 id="cpu-false-sharing"&gt;CPU False Sharing&lt;/h2&gt;
&lt;p&gt;Consider this scenario:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;We have a &lt;code&gt;long&lt;/code&gt; variable &lt;code&gt;a&lt;/code&gt;, which is not part of an array but is a standalone variable, and there&amp;rsquo;s another &lt;code&gt;long&lt;/code&gt; variable &lt;code&gt;b&lt;/code&gt; right next to it. When &lt;code&gt;a&lt;/code&gt; is loaded, &lt;code&gt;b&lt;/code&gt; is also loaded into the cache line for free.&lt;/li&gt;
&lt;li&gt;Now, a thread on one CPU core modifies &lt;code&gt;a&lt;/code&gt;, while another thread on a different CPU core reads &lt;code&gt;b&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;When &lt;code&gt;a&lt;/code&gt; is modified, both &lt;code&gt;a&lt;/code&gt; and &lt;code&gt;b&lt;/code&gt; are loaded into the cache line of the modifying core, and after updating &lt;code&gt;a&lt;/code&gt;, all other cache lines containing &lt;code&gt;a&lt;/code&gt; become invalid, since they no longer hold the latest value of &lt;code&gt;a&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;When the other core reads &lt;code&gt;b&lt;/code&gt;, it finds that the cache line is invalid and must reload it from main memory.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Because the cache operates at the level of cache lines, invalidating &lt;code&gt;a&lt;/code&gt;&amp;rsquo;s cache line also invalidates &lt;code&gt;b&lt;/code&gt;, and vice versa.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://pic3.zhimg.com/80/v2-32672c4b2b7fc48437fc951c27497bee_1440w.jpg" alt="False Sharing" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;p&gt;This causes a problem:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;b&lt;/code&gt; and &lt;code&gt;a&lt;/code&gt; are completely unrelated, but each time &lt;code&gt;a&lt;/code&gt; is updated, &lt;code&gt;b&lt;/code&gt; has to be reloaded from main memory due to a cache miss, slowing down the process.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;CPU false sharing&lt;/strong&gt;: When multiple threads modify independent variables that share the same cache line, they unintentionally affect each other&amp;rsquo;s performance. This is known as false sharing.&lt;/p&gt;
&lt;h2 id="avoiding-cpu-false-sharing"&gt;Avoiding CPU False Sharing&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Ensure that memory for different variables is not placed adjacently.&lt;/li&gt;
&lt;li&gt;Align variables during compilation to avoid false sharing. See &lt;a href="https://zh.wikipedia.org/wiki/%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84%E5%AF%B9%E9%BD%90"&gt;data structure alignment&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="references"&gt;References&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://zhuanlan.zhihu.com/p/65394173"&gt;Discussion: What is CPU False Sharing&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://en.wikipedia.org/wiki/CPU_cache"&gt;Wikipedia - CPU Cache&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description></item></channel></rss>