Skip to content

Latest commit

Β 

History

2 Commits

Folders and files

NameName
Last commit message
Last commit date
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

diskrate

Can your drive actually keep up with what you are recording?

Windows measures your disk constantly and then refuses to give you the one number that matters. Task Manager draws a pretty graph that has already scrolled away by the time your recording drops a frame. Every consumer benchmark quotes you a peak that your drive can hold for about a second.

diskrate answers the only question a streamer or recorder actually has:

During the worst single second of a sustained write, how much data got through?

That number β€” not the average, not the peak β€” is what decides whether OBS drops frames.

Single PowerShell file. No install, no dependencies, nothing to download. Read-only apart from one temporary file it deletes itself.


What it looks like

  diskrate ───────────────────────────────────────────────────────────
  Can this drive keep up with what you are recording?

  Drive ──────────────────────────────────────────────────────────────
    Path            : C:\Users\hayyz\diskrate-test
    Filesystem      : NTFS   Fixed  SSD over NVMe
    Device          : SAMSUNG MZVLB1T0HBLR-00000
    Free space      : 754.62 GiB of 919.62 GiB   (18% used)
    Room to record  : 12.0 hours at 150 Mbps

  Measuring ──────────────────────────────────────────────────────────
    Writing 1.00 GiB x 1 pass(es) in 1.0 MiB chunks, OS write cache defeated.

    Drive is quick (882 MB/s) - extending the test to 3.00 GiB so a full 1000 ms window fits.
    Pass 1     868.5 MB/s avg   β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆ

    Final flush cost 1.4 ms across 1 pass(es) - near zero means the
    bytes really did reach the device during the timed run, not afterwards.

  Sustained write speed ───────────────────────────────────────────────
    Average          :    868.5 MB/s   (6,948 Mbps)
    Worst 1s window  :    803.2 MB/s   (6,426 Mbps)  <- what decides dropped frames
    Longest stall    :     57.1 ms     = 3.4 frames at 60 fps
    Typical write    :     1.13 ms  (median), 99th pct 2.00 ms

  Can this drive hold it? ─────────────────────────────────────────────
    1080p60 stream (Twitch/YouTube)         8 Mbps  needs    1.0 MB/s   PASS
    1080p60 record (OBS quality)           50 Mbps  needs    6.3 MB/s   PASS
    1440p60 record                        100 Mbps  needs   12.5 MB/s   PASS
    4K60 record (hardware encode)         150 Mbps  needs   18.8 MB/s   PASS
    4K60 record (high bitrate)            400 Mbps  needs   50.0 MB/s   PASS
    1080p60 lossless capture            1,500 Mbps  needs  187.5 MB/s   PASS

  Verdict ────────────────────────────────────────────────────────────
    No problems found. This drive is fine for recording.

That is a real run on a real machine, pasted unedited.


Quick start

# Windows blocks unsigned .ps1 by default. This bypasses it for ONE process
# only - it does not change your machine's execution policy.
powershell.exe -NoProfile -ExecutionPolicy Bypass -File .\diskrate.ps1

# Test the drive you actually record to
powershell.exe -NoProfile -ExecutionPolicy Bypass -File .\diskrate.ps1 -Path "D:\Recordings"

# Just the facts about the drive - writes nothing at all
powershell.exe -NoProfile -ExecutionPolicy Bypass -File .\diskrate.ps1 -Info

No administrator rights required. Everything diskrate reads is available to a standard user.


The thing most benchmarks get wrong

A normal write benchmark measures your RAM, not your disk.

When a program writes a file, Windows returns success as soon as the bytes land in the system file cache. The drive may not see them for another second. Time that loop and you get a number that has nothing to do with your hardware.

diskrate opens its test file with FileOptions.WriteThrough, so every write has to reach the device before it returns. Run -Compare and it shows you both, measured back to back on your drive:

  What a naive benchmark would have told you ──────────────────────────
    Buffered write loop :  1,802.2 MB/s   flush afterwards took 613 ms
    True (write-through):    738.6 MB/s   flush afterwards took 4 ms
    A buffered benchmark overstates this drive by 2.44x - it is timing RAM, not the disk.

The flush cost is the proof. With write-through, the final flush costs 4 ms because there is nothing left to write. The buffered run spends 613 ms afterwards quietly draining a gigabyte out of RAM β€” time the "1,802 MB/s" figure conveniently never counted.

On this machine a 1 GiB buffered loop has reported as high as 2,640 MB/s against a device that genuinely sustains around 750 MB/s. A 3.5x lie, and a completely plausible-looking number.


Why "worst second" and not "average"

An average hides exactly the event you care about. A drive that writes at 900 MB/s for nine seconds and 0 MB/s for one still averages 810 MB/s β€” and drops a solid second of your recording.

diskrate timestamps every chunk, then finds the worst one-second interval in the whole run:

  • Worst 1s window β€” the least data completed in any one second. Every PASS/TIGHT/FAIL verdict is judged against this, not the average.
  • Longest stall β€” the biggest gap between two consecutive chunk completions, converted to dropped frames at your fps.
  • Typical write / 99th percentile β€” how consistent the drive is when nothing is going wrong.

Interval semantics matter here and are worth stating: a window runs (anchor, anchor + WindowMs], anchored at the start of the run or immediately after any completion. Anchoring on a completion and counting it would make a totally frozen second report as "one chunk" instead of zero β€” precisely the case the number exists to expose.

If the run is shorter than one window, the worst second is reported as - and null, never 0. Unknown must never masquerade as catastrophic.

Auto-extend. A quick NVMe finishes 1 GiB in under a second, which would leave the headline number empty on exactly the drives most people record to. If pass 1 runs too fast for a full window and you did not pin -SizeMB yourself, diskrate grows the test and runs it again (capped at 8 GiB, and never more than a third of free space).


Options

Switch Default What it does
-Path <dir> current directory Which drive to test. Point it at your recording folder.
-SizeMB <n> 1024 Data per pass, in MiB. Setting this disables auto-extend.
-Passes <n> 1 Run the benchmark n times and merge the timelines.
-ChunkKB <n> 1024 Write size, in KiB. Smaller values stress the drive harder.
-WindowMs <n> 1000 Length of the "worst window".
-Bitrate <n> 150 Your recording bitrate in Mbps, used for headroom maths.
-Fps <n> 60 Frame rate, used to convert stalls into dropped frames.
-Compare off Also run a buffered pass to show the cache-defeat difference.
-Info off Drive facts only. Writes absolutely nothing.
-FromJson <file> Re-analyse a saved -Json result. No disk access at all.
-Json off Machine-readable output on stdout, nothing else.

| -Quiet | off | Suppress all console output; use the exit code. |

Exit codes: 0 clean, 1 risks found, 2 error.

The JSON is the raw evidence

-Json emits the full per-chunk completion timeline under passes[].marks, not just the summary. Feed that file straight back in with -FromJson and you get byte-identical numbers β€” every figure the tool reports is reproducible from the data it publishes.

That is not a coincidence. Timestamps are quantised once, at capture, to 0.0001 ms (finer than the Stopwatch resolution), so analysis and publication see exactly the same values. Without it a median could land on a rounding boundary and come back a thousandth different on replay.

powershell.exe -NoProfile -ExecutionPolicy Bypass -File .\diskrate.ps1 -Json > run.json
powershell.exe -NoProfile -ExecutionPolicy Bypass -File .\diskrate.ps1 -FromJson run.json

A report with no usable timeline is refused with a one-line message and exit 2. It is never answered with blank numbers and ok: true.


What it warns about

diskrate only speaks up when something is genuinely wrong for recording:

  • FAT32 β€” hard 4 GiB file size cap. It tells you how many minutes that is at your bitrate (at 150 Mbps: 3.8 minutes, then your recording stops).
  • Network or removable drive β€” throughput depends on things this test cannot see.
  • Over 90% full β€” SSDs slow down sharply with no spare blocks.
  • Under 30 minutes of headroom at your bitrate.
  • A write stall of 100 ms or more (FAIL at 500 ms), reported in ms and in frames.
  • Wildly uneven speed between the best and worst second.

A healthy drive gets a clean bill of health and exit code 0. This was tested deliberately: the suite includes a good-drive fixture that must produce zero risks, because a tool that cries wolf gets ignored.


Is it safe to run?

Yes, and here is exactly what it does:

  • It creates one file, diskrate-<guid>.tmp, in the folder you point it at, and deletes it in a finally block β€” even on Ctrl+C or an error.
  • It touches nothing else. No registry writes, no services, no processes, no settings.
  • -Info and -FromJson write nothing whatsoever.
  • It needs no undo, because it changes no state. That is verified, not asserted: the test suite hashes every file in the target folder before and after a full benchmark and requires byte-identical results, and greps the source for Set-ItemProperty, New-ItemProperty, Remove-ItemProperty, Stop-Process, Stop-Service and SetValue( β€” all zero.

It will write a few GiB to your drive and read nothing back. On an SSD that is a rounding error against its endurance rating.


Units

Deliberate and consistent, because this is where benchmarks get sloppy:

  • -SizeMB and -ChunkKB are binary β€” MiB and KiB, 1024-based.
  • Throughput is reported in decimal MB/s β€” 1,000,000 bytes, the same convention CrystalDiskMark uses.

That makes Mbps = MB/s x 8 exact, so it lines up with how video bitrates are quoted, and it makes every expected value in the test suite computable by hand.


Test evidence

Two suites ship with the tool. Run them yourself.

selftest.ps1 β€” 134 assertions, synthetic ground truth

PASSED: 134
FAILED: 0

Every expected number is hand-computed, and each fixture plants a different distinctive value in each field so a tool that mixes two fields up cannot pass:

  • 30 chunks on an exact 100 ms cadence: average exactly 10.00 MB/s, worst second exactly 10.00, median stall exactly 100.
  • One planted 1000 ms freeze: average exactly 7.00, worst second exactly 1.00 MB/s anchored at t=900, median stall unchanged at 100.
  • A 1200 ms freeze (longer than the window): worst second is a measured 0.00, and asserted to be non-null.
  • A run shorter than one window: worst second is null, not 0, while the average is still computed.
  • Recording maths by hand: FAT32's 4 GiB cap at 150 Mbps = 3.81775 min, at 8 Mbps = 71.58279 min; 1 TB at 400 Mbps = 333.3333 min.
  • Verdict boundaries tested at exactly 2.00x and 1.20x of 18.75 MB/s.
  • A planted FAT32 / removable / nearly-full volume raises a FAIL quoting 3.8 min and exits 1; a healthy fixture raises zero risks and exits 0.
  • Four error paths each print a single clean line, emit valid JSON, exit 2, and never a stack trace.
  • A real 64 MiB write asserts exactly 67,108,864 bytes on disk and that the reported rate equals bytes/elapsed.
  • A live benchmark is written out with -Json and read back with -FromJson; average, worst second, max stall, median, 99th percentile, duration, flush cost and device model must all come back exactly equal. Repeated five times on different data, identical every time.
  • -Quiet verified silent on the success, failure and error paths.

realcheck.ps1 β€” 57 assertions against this actual machine

PASSED: 57
FAILED: 0

Synthetic fixtures are clean and predictable, which is exactly why they miss things. These run against real hardware and cross-check against independent Windows APIs.

Drive facts confirmed by three different APIs (Get-Volume, Win32_LogicalDisk, Get-PhysicalDisk):

[PASS] total size matches Get-Volume                    -> 987439820800
[PASS] total size matches Win32_LogicalDisk             -> 987439820800
[PASS] free space agrees with Get-Volume within 64 MiB  -> 53248 bytes apart
[PASS] media type matches Get-PhysicalDisk              -> SSD
[PASS] bus type matches Get-PhysicalDisk                -> NVMe
[PASS] model matches Get-PhysicalDisk                   -> SAMSUNG MZVLB1T0HBLR-00000

Windows independently confirms the bytes. Win32_PerfRawData_PerfDisk_LogicalDisk.DiskWriteBytesPersec is, despite the name, a raw cumulative byte total. Read it before and after and you get an exact second opinion from the kernel:

[PASS] Windows saw the same bytes  -> 1.0062x  (270,102,528 observed vs 268,435,456 written)
[PASS] write-through leaves nothing to flush  -> 3.504 ms

An independent reference implementation β€” written with a deliberately naive pipeline-and-slice technique that shares no code with the tool's forward-only two-pointer sweep β€” agrees field for field on a real captured timeline:

[PASS] chunks agree       -> 256
[PASS] bytes agree        -> 268435456
[PASS] average MB/s agrees-> 972.28
[PASS] max stall agrees   -> 8.889
[PASS] median stall agrees-> 0.992

Known ground truth planted inside real data. A 1500 ms stall is spliced into a genuine 256-chunk timeline captured from this NVMe. The test asserts element-wise that exactly one real interval changed, that the tool recovers the planted value exactly, that it survives end to end through the published entry point alongside real device facts, and that the surrounding hundreds of real intervals do not leak into the answer:

[PASS] exactly one real interval was replaced    -> 1
[PASS] planted 1500 ms stall recovered exactly   -> 1500
[PASS] worst second is a measured 0.00 MB/s      -> 0
[PASS] real device model carried through         -> SAMSUNG MZVLB1T0HBLR-00000
[PASS] stall reported as 90 frames at 60fps
       -> Longest write stall was 1,500 ms - 90.0 frames at 60 fps.

The number tracks real work β€” it is not a constant. Three sizes, three distinct durations, and time scaling with data:

[PASS] duration grows with size   -> 74.975 / 144.525 / 300.822 ms
[PASS] quadrupling size ~quadruples time  -> 4.01x
[PASS] rates are non-zero and not identical -> 895.08 / 928.68 / 892.34 MB/s

Cache defeat, measured on real hardware:

[PASS] buffered loop looks faster than the device
       -> buffered=2380.41  writethrough=899.59 MB/s
[PASS] buffered leaves a real flush bill
       -> buffered flush=141.87ms  writethrough flush=1.345ms
[PASS] both wrote the same bytes  -> 268435456

Read-only behaviour proved, not claimed:

[PASS] -Info leaves the folder byte-identical
[PASS] benchmark leaves no temp file behind    -> 0
[PASS] folder identical after a full benchmark

Three real bugs these tests caught

Worth recording, because all three looked fine and produced plausible numbers.

1. A $script: variable silently destroyed a parameter. The script initialised $script:Quiet = $false for its own output helpers. But parameters live at script scope, so that assignment overwrote the user's -Quiet switch every single time β€” -Quiet simply never worked. -Json masked it, because -Json assigns $true afterwards. Renamed to $script:Silent. Section L of the self-test is now a permanent regression guard.

2. The worst-second window could never report zero. The window was anchored on a chunk completion and counted it, so a second in which the drive did absolutely nothing still came back as "one chunk". The bug only showed up when a planted stall was made longer than the window β€” a 750 ms stall cannot collapse a 1000 ms window, because no window fits inside it. Fixed the interval to (anchor, anchor + WindowMs], and the independent reference implementation was updated and re-agreed.

3. -FromJson could not actually read -Json. The documented round trip was broken from the start: -Json never emitted the marks timeline, so replaying the tool's own output produced ok: true with every number blank β€” a confidently empty answer, which is worse than an error. Only a smoke test from a fresh clone, exercising the modes the way a user would chain them, caught it. Fixed by publishing the timeline, refusing reports that have none, and asserting exact round-trip equality in the suite.


Requirements

  • Windows with PowerShell 5.1 or later (ships with Windows 10 and 11)
  • No admin rights, no installs, no external tools

See also

  • obs-4k60-recorder β€” the OBS settings guide for 4K60 recording
  • framecheck β€” tells you a recording dropped frames; diskrate tells you whether the drive was why
  • diskscout β€” find what is eating your disk space
  • gamemode β€” free up RAM and CPU before you record

License

MIT β€” see LICENSE.

About

Will your drive keep up with 4K60 recording? Measures true sustained write speed with the OS cache defeated, finds the worst-case stall that drops frames, and flags the FAT32 4GB cap and low headroom that silently kill long recordings. Read-only, zero dependencies.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages