Skip to content

Const var make more optimized loops in -O3 GCC/Clang - #2057

Open
GermanAizek wants to merge 2 commits into
htop-dev:mainfrom
GermanAizek:more-optimize-for-loops
Open

Const var make more optimized loops in -O3 GCC/Clang#2057
GermanAizek wants to merge 2 commits into
htop-dev:mainfrom
GermanAizek:more-optimize-for-loops

Conversation

@GermanAizek

Copy link
Copy Markdown
Contributor

Many thanks @fasterit for making me check the loop, I found such a cool optimization on GCC and Clang compilers with the -O3 optimization flag. Amazing results, it's strange that the compiler itself doesn't optimize for -O3.

Example with actionUntagAll() function:

screen

Opt A my branch (with const int size):

 actionUntagAll:
    subq    $24, %rsp
    movq    %rbp, 8(%rsp)
    movq    %rdi, %rbp
    movq    8(%rdi), %rdi
    call    Panel_size@PLT
    testl    %eax, %eax
    jle    .L33
    movq    %rbx, (%rsp)
    xorl    %ebx, %ebx
    movq    %r12, 16(%rsp)
    movl    %eax, %r12d
.L34:
    movq    8(%rbp), %rdi
    movl    %ebx, %esi
    addl    $1, %ebx
    call    Panel_get@PLT
    movb    $0, 29(%rax)
    cmpl    %ebx, %r12d
    jne    .L34
    movq    (%rsp), %rbx
    movq    16(%rsp), %r12
.L33:
    movq    8(%rsp), %rbp
    movl    $1, %eax
    addq    $24, %rsp
    ret 
screen2

Op B master branch (without const int size):

 actionUntagAll:
    pushq    %rbp
    movq    %rdi, %rbp
    pushq    %rbx
    xorl    %ebx, %ebx
    subq    $8, %rsp
    jmp    .L33
.L34:
    movq    8(%rbp), %rdi
    movl    %ebx, %esi
    addl    $1, %ebx
    call    Panel_get@PLT
    movb    $0, 29(%rax)
.L33:
    movq    8(%rbp), %rdi
    call    Panel_size@PLT
    cmpl    %ebx, %eax
    jg    .L34
    addq    $8, %rsp
    movl    $1, %eax
    popq    %rbx
    popq    %rbp
    ret 

Why separate const int (option A) faster

In Option A, I saved the size to a constant size before the loop started.
In the assembler, we see that the call Panel_size@PLT occurs only once before the start of the loop (before the label .L34). The result is stored in the %r12d register, and a simple and very fast comparison with the register takes place inside the loop itself: cmpl %ebx, %r12d.

For each iteration of the loop in Variant A, there is 1 function call.:

`call Panel_get@PLT`

Why Option B vanilla code slower

In Option B, the exit condition of the loop is calculated anew at each iteration.
There is a label in the assembly code .L33 (which is a loop condition check) contains:
Code snippet

.L33:
    movq 8(%rbp), %rdi
    call Panel_size@PLT <--- Function call!
    cmpl    %ebx, %eax
    jg      .L34

This means that for each iteration of the loop in Option B, there are 2 function calls.:

`call Panel_get@PLT`

`call Panel_size@PLT`

@coderabbitai

coderabbitai Bot commented Aug 4, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: b5a7426d-04c3-4ab3-bfc5-ea25387e764f

📥 Commits

Reviewing files that changed from the base of the PR and between 609eb26 and 7727886.

📒 Files selected for processing (5)
  • BacktraceScreen.c
  • Header.c
  • Panel.c
  • ProcessTable.c
  • Table.c

📝 Walkthrough

Walkthrough

The change caches panel, vector, and row sizes before loop execution across multiple UI and action modules. It also marks unchanged local size variables as const. Loop processing and observable behavior remain unchanged.

Poem

Counts rest once before the loop,
Rows follow bounds that stay in view.
Vectors keep their sizes near,
Panels turn through each frontier.
No behavior changes course.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@GermanAizek

GermanAizek commented Aug 4, 2026

Copy link
Copy Markdown
Contributor Author

In short calling a function (especially via PLT — Procedure Linkage Table) is an expensive operation for CPU compared to simply reading from ALU register. It requires preparing arguments, jumping through memory, changing the stack pointer, and returning.

If htop panel has, for example, 10lines :

for loop A will call Panel_size 1 time
for loop B will call Panel_size 11 times

Increddible.

@GermanAizek
GermanAizek force-pushed the more-optimize-for-loops branch from 609eb26 to 7727886 Compare August 4, 2026 01:22
GermanAizek added a commit to GermanAizek/fltk that referenced this pull request Aug 4, 2026
@BenBE BenBE added the code quality ♻️ Code quality enhancement label Aug 4, 2026
@BenBE

BenBE commented Aug 4, 2026

Copy link
Copy Markdown
Member

The const part is not the main issue here, but the repeated call.

Often times doing

for(int i = 0, size = Panel_size(panel); i < size; i++) {…

works just the same and also limits the scope of size

@fasterit

fasterit commented Aug 4, 2026

Copy link
Copy Markdown
Member

I like @BenBE's style better as it limits the scope of the temp variable.

@GermanAizek: Did you do some benchmarking (perf) how much the loop optimizations save overall?
I.e. htop 10s wallclock time runs and compare CPU cycle counts?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

code quality ♻️ Code quality enhancement

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants