Something unintelligible about hot loop

I am a beginner and I am currently reviewing the documentation for rust. For macro print! I am confused about some of the descriptions.

The print! macro will lock the standard output on each call. 
If you call print! within a hot loop, 
this behavior may be the bottleneck of the loop. 
To avoid this, lock stdout with io::stdout().lock():

What is a hot loop, why does this phenomenon occur, what is the specific phenomenon of the bottom of the loop it can cause, and what are the consequences of this.

A "hot loop" is a part of the program that executes very often. It means that this is the part of the program where performance changes have the most impact.

This is a standard term when discussing program performance, but when searching "hot loop" on Duckduckgo, I get results about electronics instead of software performance.

I'm not entirely sure how to solve this issue. "hot loop" is the right term for the concept, so it should probably remain. It means that a solution would be have a small description, either inline or linked.

My guess about this is similar to the impact on program performance, but as you said, I cannot summarize a visual result based on all the current search results. But I am still very grateful to you for sharing your understanding in this regard.

I cannot prove whether my guess is correct. One of my views is that the so-called "a hot loop" is a streamlined program with high usage frequency. In high concurrency situations, those in the program are similar to print! Macros that operate on data read and write buffers will spend more effort on processing IO rather than logic

I recommend using the users forum for usage questions. The internals forum is probably right to discuss how to update the documentation; but here you seem more confused by the concept. The users forum is better suited to answer this kind of questions.


One of my views is that the so-called "a hot loop" is a streamlined program with high usage frequency.

A hot loop can be present in any program. It's the part of the code that where performance changes have the most impact, because it's repeatedly called very often.

In a typical program, not all functions are called as often. Some code is only used during initialization, some during clean-up, some only occasionally, and some performs most of the work. This part doing most of the work is usually called in a loop: this is the hot loop.

If we simplify even more, ask yourself in which part of your code a 100x performance improvement would matter the most. In some parts like initialization, 100x improvement means that the program is a few milliseconds faster; while in other parts it means that your program goes from ten minutes to a few seconds. These parts where performance changes matter form the hot loop.

Here is an example program that can be divided in three parts:

fn main() {
  // Initialization
  let mut input: String = String::new();
  println!("How many squares to print?");
  std::io::stdin().read_line(&mut input).expect("failed to read stdin");
  let n: usize = input.trim().parse().expect("invalid input");

  // Main part
  for i in 0..n {
    let square = i * i;
    println!("{i}² = {square}");
  }

  // Shutdown
  println!("done");
}

The main part of this program can be considered a hot loop: if we want the program to go as fast possible, making the loop body faster will have way more impact than optimizing the initialization or shutdown code. The documentation you quoted in your original post recommends to use io::stdout().lock() instead of the print macro in hot loops:

use std::io::Write;

fn main() {
  // Initialization
  let mut input: String = String::new();
  println!("How many squares to print?");
  std::io::stdin().read_line(&mut input).expect("failed to read stdin");
  let n: usize = input.trim().parse().expect("invalid input");

  {
    // Still initialization
    let mut stdout = std::io::stdout().lock();

    // Hot loop
    for i in 0..n {
      let square = i * i;
      writeln!(stdout, "{i}² = {square}").expect("failed to write");
    }
  }

  // Shutdown
  println!("done");
}

In the new code, locking is moved out of the hot loop: it's now only performed once during initialization instead of every iteration.


Aside: I used println/writeln instead of print/write from the original post. It does not change the main idea, but there may be considerations about flushing if you care about I/O perf.


EDIT: I found a Wikpedia article on hot spots describing a similar concept.

3 Likes

Thank you very much for your wonderful answer, which has given me a better understanding of this aspect.