Skip to content

Action/Header/SM/Vector: move const calc expression out of loop#2038

Open
GermanAizek wants to merge 1 commit into
htop-dev:mainfrom
GermanAizek:const-out-of-loop
Open

Action/Header/SM/Vector: move const calc expression out of loop#2038
GermanAizek wants to merge 1 commit into
htop-dev:mainfrom
GermanAizek:const-out-of-loop

Conversation

@GermanAizek

Copy link
Copy Markdown
Contributor

Since I started contributing to heavily optimized AV1 decoder (ffmpeg dav1d), there are high performance requirements. I experimentally found out that putting calculated expressions from the body of the loop into separate constants gives a strong two-fold increase. What is strange is the C/C++ compiler that GCC and Clang on -O3 cannot automatically optimize.

It's also visually more readable with a const separate variable.

@coderabbitai

coderabbitai Bot commented Jul 8, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@GermanAizek, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 28 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Repository UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 3f3d61f8-2031-4e60-be65-7e05ccace56c

📥 Commits

Reviewing files that changed from the base of the PR and between 0b96907 and 53143da.

📒 Files selected for processing (4)
  • Action.c
  • Header.c
  • ScreenManager.c
  • Vector.c
📝 Walkthrough

Walkthrough

Changes

This change caches repeated size and count lookups in loops across Vector.c, Header.c, ScreenManager.c, and Action.c. It also consolidates a few repeated computations, including the active sort key in actionSetSortColumn, the selected process thread-group id in the backtrace flow, and the cache-memory flag in the help screen. ScreenManager.c also switches some loop bounds and comparisons from size_t-based checks to int-based checks.

Sequence Diagram(s)

Not applicable.

Estimated code review effort: Medium

Suggested labels: refactor, code-quality

Suggested reviewers: maintainers familiar with Action.c, Header.c, ScreenManager.c, and Vector.c

Poem

Counts now rest in locals small,
And loops no longer call them all.
One sort key, one thgid, held tight,
One cache flag guiding the light.
The code moves on with lighter feet,
More direct, compact, and neat.


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.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1


ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: d591b336-4f7c-4a3d-8422-e3671e304828

📥 Commits

Reviewing files that changed from the base of the PR and between 1ef4226 and 43abf70.

📒 Files selected for processing (4)
  • Action.c
  • Header.c
  • ScreenManager.c
  • Vector.c

Comment thread ScreenManager.c
Comment on lines +82 to +84
const int n = this->panelCount;
if (idx < n) {
for (int i = idx + 1; i <= n; i++) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Description: Inspect Vector_get to determine if it handles out-of-bounds access safely.

ast-grep run --pattern 'Vector_get($_, $_) {
  $$$
}' --lang c Vector.c

# Also check for bounds validation in Vector_get
rg -n -A 10 'void\*\s*Vector_get' Vector.c Vector.h

Repository: htop-dev/htop

Length of output: 476


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '\n## Files\n'
git ls-files 'Vector.c' 'Vector.h' 'ScreenManager.c' 'ScreenManager.h'

printf '\n## Vector.c outline\n'
ast-grep outline Vector.c --view expanded || true

printf '\n## Vector.h outline\n'
ast-grep outline Vector.h --view expanded || true

printf '\n## ScreenManager.c relevant lines\n'
sed -n '70,110p' ScreenManager.c | cat -n

printf '\n## Vector_get declaration/definition search\n'
rg -n -A 20 -B 5 'Vector_get|vector_get' Vector.c Vector.h

printf '\n## Vector_insert/Vector_set search\n'
rg -n -A 20 -B 5 'Vector_insert|Vector_set|Vector_size|Vector_get' Vector.c Vector.h

Repository: htop-dev/htop

Length of output: 13785


Fix the shift loop bounds in ScreenManager_insert — start at idx and stop before n. i = idx + 1; i <= n skips the panel at idx and reads slot n, which is outside the populated range and can crash or corrupt layout.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

♻️ Duplicate comments (1)
ScreenManager.c (1)

82-84: 🩺 Stability & Availability | 🔴 Critical | ⚡ Quick win

Shift loop bounds in ScreenManager_insert are still incorrect.

This was previously flagged but persists. The loop for (int i = idx + 1; i <= n; i++) has two errors:

  1. Starts at idx + 1 — skips the panel at idx, which will be shifted right by Vector_insert and needs visual adjustment.
  2. Ends at n (inclusive) — accesses Vector_get(this->panels, n) where n == this->panelCount. Only indices 0 through n-1 are populated, so index n is out of bounds and may crash or corrupt layout.

Compare with ScreenManager_remove (line 101) which correctly uses for (int i = idx; i < n; i++).

Fix: adjust both loop bounds
    const int n = this->panelCount;
    if (idx < n) {
-      for (int i = idx + 1; i <= n; i++) {
+      for (int i = idx; i < n; i++) {
         Panel* p = (Panel*) Vector_get(this->panels, i);
         Panel_move(p, p->x + size, p->y);
      }
    }

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: e7aae4cb-b36b-4dfa-ad63-598e1c93648f

📥 Commits

Reviewing files that changed from the base of the PR and between 1e62a80 and 3e61aec.

📒 Files selected for processing (4)
  • Action.c
  • Header.c
  • ScreenManager.c
  • Vector.c

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

♻️ Duplicate comments (1)
ScreenManager.c (1)

82-84: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Shift loop bounds in ScreenManager_insert are still off by one.

The loop for (int i = idx + 1; i <= n; i++) skips the panel at idx (which needs to shift right) and accesses index n (out of bounds — valid indices are 0 to n-1). Vector_get performs no bounds checking, so this can dereference a stale or NULL pointer. The correct loop is for (int i = idx; i < n; i++).

This was previously flagged and remains unfixed.

Fix shift loop bounds
    if (idx < n) {
-      for (int i =  idx + 1; i <= n; i++) {
+      for (int i = idx; i < n; i++) {
          Panel* p = (Panel*) Vector_get(this->panels, i);
          Panel_move(p, p->x + size, p->y);
       }
    }

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 18942c5c-b617-4e60-af6b-be5f5069de44

📥 Commits

Reviewing files that changed from the base of the PR and between 3e61aec and 0b96907.

📒 Files selected for processing (4)
  • Action.c
  • Header.c
  • ScreenManager.c
  • Vector.c

Comment thread Action.c
Process* process = (Process *)Vector_get(allProcesses, i);
if (process && Process_getThreadGroup(process) == Process_getThreadGroup(selectedProcess)) {
const int thgid = Process_getThreadGroup(selectedProcess);
const int pvecsize = Vector_size(allProcesses);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A more readable variable name:

Suggested change
const int pvecsize = Vector_size(allProcesses);
const int numProcesses = Vector_size(allProcesses);

Comment thread Action.c
static Htop_Reaction actionUntagAll(State* st) {
for (int i = 0; i < Panel_size((Panel*)st->mainPanel); i++) {
Row* row = (Row*) Panel_get((Panel*)st->mainPanel, i);
Panel* panel = (Panel*)st->mainPanel;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We probably don't need a separate variable for (Panel*)st->mainPanel. The pointer stays unchanged during the loop, so the compiler should be able to optimize it.

Comment thread Header.c
Comment on lines -69 to +70
for (int j = this->columns[i]->items - 1; j >= 0; j--) {
const int n = this->columns[i]->items;
for (int j = n - 1; j >= 0; j--) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unnecessary change for optimization.

Comment thread Header.c
for (int i = 0; i < Vector_size(this->columns[col]); i++) {
Meter* meter = (Meter*) Vector_get(this->columns[col], i);
const Vector* meters = this->columns[col];
const int n = Vector_size(meters);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just curious, how can a compiler fail to optimize the Vector_size call here?

Vector_size is an inline function when NDEBUG is defined (i.e. when htop is not built for debug), and Vector_size merely retrieves the items member of the Vector object, which should be a constant throughout the loop.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants