paint-brush
How Order in iF Statement Impacts on Performanceby@idsulik
133 reads

How Order in iF Statement Impacts on Performance

by Suleiman DibirovJune 26th, 2024
Read on Terminal Reader
Read this story w/o Javascript
tldt arrow

Too Long; Didn't Read

Discover how reordering conditions in code significantly enhance performance. By prioritizing static checks over costly I/O operations, unnecessary computations will be avoided, reducing function calls by half during builds and updates. Practical insights on optimizing condition order in your own code are shared, emphasizing the importance of strategic placement for faster and more efficient executions.
featured image - How Order in iF Statement Impacts on Performance
Suleiman Dibirov HackerNoon profile picture

When writing code, the order of condition checks can significantly impact the correctness and efficiency of your program. Let's explore this through a recent update I made to Skaffold's source code, which led to a 50% performance increase, but before diving into the details, which option would you prefer?


Option 1:

if util.IsEmptyDir(path) || !info.IsDir()

Option 2:

if !info.IsDir() || util.IsEmptyDir(path)


Check the functions:

  • util.IsEmptyDir(path): This function checks if a directory at the specified path is empty.
func IsEmptyDir(path string) bool {
    d, err := os.Open(path)
    if err != nil {
        return false
    }
    defer d.Close()
    if _, err := d.Readdirnames(1); err == io.EOF {
        return true
    }
    return false
}


  • info.IsDir(): This method checks if the current instance of the struct is a directory.
func (x X) IsDir() bool {
    return x.modeType & os.ModeDir != 0
}


Let's remember that in Go (as in many other programming languages), the order of condition checks in an if statement is evaluated from left to right. This means that the first condition is evaluated before the second. If the first condition returns true, the second condition will not be executed and it’s important.


For example:

if !info.IsDir() || util.IsEmptyDir(path) {}

Here, !info.IsDir() is evaluated first. If it returns true, the util.IsEmptyDir(path) is not evaluated. But why is that so important?


info.IsDir() method makes only static checks, and this operation is pretty fast while the util.IsEmptyDir() makes I/O operations; it opens the given path and reads directory names.


When Go performs I/O operations like os.Open(path) or Readdirnames, several things happen:

  1. File Descriptor Management: When you open a file with os.Open(path), Go uses system calls like open (on Unix-like systems) to obtain a file descriptor, which represents an open file in the operating system.


  2. System Calls: Performing I/O operations involves making system calls to the underlying operating system. For example, os.Open(path) will ultimately call the open system call to open the file at the specified path.


  3. Blocking Nature: Many I/O operations in Go are blocked by default, meaning the program waits until the operation completes before proceeding.


Taking into consideration all the information above, the right answer here is the second option, for checking !info.IsDir() first, unnecessary calls to IsEmptyDir on non-directory paths are avoided. This reduces the number of costly I/O operations, in my case:


  • First build: from 102,958 to 20,764 calls

  • On file change: from 411,832 to 103,820 calls


Based on our experience, here are some practical guidelines for optimizing condition order in your code:

  • Prioritize Fast Checks: Place conditions that involve static checks or computations before those requiring I/O operations or more resource-intensive tasks.


  • Profile and Benchmark: Always profile your code to identify performance bottlenecks. Use benchmarks to validate the impact of condition order changes on overall performance.