How to Obtain Good Concurrent Read Performance from Disk in Windows

When working with large files in a multithreaded environment, achieving optimal read performance from disk can pose significant challenges. In scenarios where you have multiple threads attempting to read from separate files concurrently, you may experience poor throughput due to the operating system’s disk scheduling policy. This post delves into the common issues faced and presents strategies for enhancing disk read performance on Windows.

The Problem: Concurrent Read Performance Constraints

Imagine you have two large files, each around 2 GiB, and two separate threads trying to read them simultaneously. Instead of enjoying improved performance, you find both threads are performing poorly and achieving a combined throughput of only 2-3 MiB/sec. Here’s a summary of the situation:

  • Setup: Two threads, each reading one file.
  • Observation: Poor combined throughput when both threads are active (~2-3 MiB/sec) versus much better performance (~45 MiB/sec) for a single thread.
  • Suspected Cause: Disk seeking behavior impacted by the Windows disk scheduler, which is leading to inefficient reading patterns.

Understanding Disk Scheduling on Windows

Before we dive into solutions, it’s crucial to understand how Windows manages disk I/O requests. Historically, Windows used a FIFO (First In, First Out) queue for disk requests, where requests were split into 64 KB blocks. This resulted in:

  • Frequent Disk Seeks: When two threads were reading concurrently, their requests interfered, causing constant seeking back and forth across the disk.
  • Inflexibility: Prior to Windows Vista, there was very little that developers could do to modify the disk request handling.

However, with the introduction of Windows Vista, a more sophisticated disk scheduling algorithm was implemented, allowing for improved management of concurrent I/O operations. This raises the question: What can we do to optimize read performance in pre-Vista systems or even in later versions if issues persist?

Proposed Solutions to Improve Concurrent Read Performance

  1. Self-Made Disk Access Policy: Since you cannot modify the scheduling policy in earlier Windows versions, consider creating your own method to manage disk access in your threads.

    • Example Policy Implementation:
      if (THREAD_A is reading from disk) {
          wait for THREAD_A to stop reading or wait for X ms
      }
      read for X ms (or Y MB)
      stop reading and check the status of THREAD_A again
      

    This policy introduces a waiting mechanism where threads only access the disk when the other thread is not currently reading, thus mitigating seeking issues.

  2. Utilize Synchronization Primitives: Employ semaphores or mutexes to control access to the disk, ensuring that only one thread reads at a time. This may reduce throughput marginally compared to true concurrent reads but can improve overall efficiency.

  3. Monitor Performance with Metrics: Use performance monitoring tools (like perfmon) to assess disk queue status and adjust your reading intervals and data sizes dynamically. This ‘auto-tuning’ approach allows you to adapt your strategy based on real-time performance metrics:

    • Measure current transfer rates.
    • Adjust X and Y values based on historical performance data.
  4. Upgrade to Newer Windows Versions: If possible, consider upgrading your operating system. Windows Vista and beyond provide smarter disk scheduling, enabling more efficient concurrent reads.

Conclusion

Achieving good concurrent read performance from disk in Windows involves understanding the limitations of the OS’s I/O scheduling and implementing clever software techniques to work around them. By introducing a custom disk access policy, using synchronization techniques, and monitoring performance metrics, you can significantly enhance your application’s throughput when dealing with large files and multithreading.

Implement these strategies thoughtfully, and you’ll be on your way to optimizing your disk read processes and fully leveraging the power of concurrent programming in your applications.