Home » Script to measure write speeds in SQL Server

Script to measure write speeds in SQL Server

by Vlad Drumea

In this post I cover a script I’ve put together for measuring storage write speeds in SQL Server, namely against database data files.

This is meant to help get an idea of how the underlying storage performs when SQL Server is writing 1GB of data to a database.

At this point, you might be asking yourself: “Why not use CrystalDiskMark instead?”.
The answer is simple: you might not always be able to install/run additional software in an environment. Even more so if you work with external customers or you’re a consultant. It’s a lot simpler to ask a customer to run a script and send you the output, than it is to ask them to install and run some 3rd party software.

The script

Since the TestWriteSpeeds.sql script is too big to include here, I’ve added it in my SQL Server Scripts GitHub repository.

In short, the script does the following:

  1. Loads a table variable with 1GB of data.
  2. Creates a table named io_stats_writes to store IO stats related data.
  3. Creates the speed_test table in which the previously generated data will be written.
  4. Does 4 passes of inserting the 1GB worth of records using the TABLOCK hint.
  5. Captures info from the sys.dm_io_virtual_file_stats DMV to the io_stats_writes table for every pass of the insert into the speed_test table.
  6. Outputs information about the duration of the inserts, IO write stall duration, if and by how much the data and/or transaction log file grew during the test.
  7. Drops the 2 tables created during its execution.

Result sets explained

The first result set shows the following:

  • The name of the database.
  • Total number of passes.
  • The file’s physical name.
  • The file type.
  • The average amount of MBs written to each file (data and transaction log).
  • The average duration in milliseconds of a pass.
  • The average time in milliseconds that SQL Server had to wait for the IO request (the IO stall time aka latency).
  • Average writes per file.
  • Average amount of MB by which the data and/or transaction log file increased in size during the test

The second result set is fairly similar to the first one, but this one shows the totals for all 4 passes instead of averages.

The third result set shows the non-aggregated information for each of the 4 passes.


While the script uses a table variable to store the initial data, it still gets written to tempdb, although, from my tests the table variable way was faster than using a temp table, the read speed of the storage on which tempdb resides can be a bottleneck if it’s slower than the write speed on any of the storage used by the data files.

Since the insert duration is calculated as pre-insert timestamp - post-insert timestamp both data and tlog files have the same duration per pass. The only way to tell if your transaction log file is the bottleneck, and not the data file, is by looking at the amount of data written to it, if and by how much the file has grown during a pass, and the IO stall time.


While working on another post, I’ve ran into a situation where parallelism related waits add to the write times, but only on databases using the FULL recovery model.

If you’re seeing suspiciously long times on databases with FULL recovery model, uncomment line 136 (the OPTION(MAXDOP 1) hint) and re-run the script.

How to run it

Ideally, you run this when the database isn’t being used. Or you create a dedicated database on the same storage and config that you’d want to test.

Test runs

All my 3 test runs are done against databases created using variations of the following T-SQL.

So, the data file is 5000MB in size, with 1024MB growth increments. The transaction log file is 800MB with 300MB growth increments.
And the database is set to use the simple recovery model.

The only differences between them are the database and file names, and the locations of the files.

Slow storage

SlowDB, as the name implies, resides on the slowest drive in my machine, a SATA 3 Samsung 860 Evo SSD.

I’m reusing here the previous screenshot because it shows the results of the speed test on this specific database.

result set 1 database|passes|file_physical_name|file_type|written_per_pass_MB|avg_duration_ms|avg_io_stall_write_ms|avg_writes_per_pass|avg_file_size_increase_MB SlowDB|4|E:\VSQL\Data\SlowDB.mdf|ROWS|1024.31|2054|443|4897|0 SlowDB|4|E:\VSQL\TLog\SlowDB_log.ldf|LOG|12.57|2054|389|220|0 result set 2 database|file_physical_name|file_type|total_written_MB|total_duration_ms|total_avg_io_stall_write_ms|total_writes|total_file_size_increase_MB SlowDB|E:\VSQL\Data\SlowDB.mdf|ROWS|4097.24|8216|1774|19589|0 SlowDB|E:\VSQL\TLog\SlowDB_log.ldf|LOG|50.26|8216|1559|880|0 result set 3 database|pass|file_logical_name|file_physical_name|file_type|duration_ms|writes|avg_io_stall_write_ms|written_MB|file_size_increase_MB SlowDB|1|SlowDB|E:\VSQL\Data\SlowDB.mdf|ROWS|2060|5504|451|1024.46|0 SlowDB|1|SlowDB_log|E:\VSQL\TLog\SlowDB_log.ldf|LOG|2060|221|386|12.56|0 SlowDB|2|SlowDB|E:\VSQL\Data\SlowDB.mdf|ROWS|2053|5220|443|1024.30|0 SlowDB|2|SlowDB_log|E:\VSQL\TLog\SlowDB_log.ldf|LOG|2053|220|392|12.60|0 SlowDB|3|SlowDB|E:\VSQL\Data\SlowDB.mdf|ROWS|2050|3991|435|1024.21|0 SlowDB|3|SlowDB_log|E:\VSQL\TLog\SlowDB_log.ldf|LOG|2050|220|392|12.59|0 SlowDB|4|SlowDB|E:\VSQL\Data\SlowDB.mdf|ROWS|2053|4874|445|1024.27|0 SlowDB|4|SlowDB_log|E:\VSQL\TLog\SlowDB_log.ldf|LOG|2053|219|389|12.51|0

The main info to keep from this is that the average time it takes for 1GB to be written to the data file is 2 seconds, the average IO write stall time is 443 milliseconds.

With the total amount of time required to write 4GB being 8.21 seconds out of which 1.77 seconds of IO stall time.

Since the database is set to use the Simple recovery model, there’s not so much data written to the transaction log – an average of 12.57MB per pass.

And none of the files go through autogrowth events since they’re both properly sized to accommodate the test.

Fast storage

Up next is FastDB, which resides on a Samsung 980 Pro SSD connected to a PCIe 3.0 M.2 slot.

result set 1 database|passes|file_physical_name|file_type|written_per_pass_MB|avg_duration_ms|avg_io_stall_write_ms|avg_writes_per_pass|avg_file_size_increase_MB FastDB|4|F:\VSQL\FastDB.mdf|ROWS|1024.41|433|7|2448|0 FastDB|4|F:\VSQL\FastDB_log.ldf|LOG|12.14|433|7|213|0 result set 2 database|file_physical_name|file_type|total_written_MB|total_duration_ms|total_avg_io_stall_write_ms|total_writes|total_file_size_increase_MB FastDB|F:\VSQL\FastDB.mdf|ROWS|4097.62|1735|30|9795|0 FastDB|F:\VSQL\FastDB_log.ldf|LOG|48.56|1735|29|854|0 result set 3 database|pass|file_logical_name|file_physical_name|file_type|duration_ms|writes|avg_io_stall_write_ms|written_MB|file_size_increase_MB FastDB|1|FastDB|F:\VSQL\FastDB.mdf|ROWS|540|2449|2|1024.93|0 FastDB|1|FastDB_log|F:\VSQL\FastDB_log.ldf|LOG|540|226|1|12.84|0 FastDB|2|FastDB|F:\VSQL\FastDB.mdf|ROWS|406|2378|5|1024.28|0 FastDB|2|FastDB_log|F:\VSQL\FastDB_log.ldf|LOG|406|208|4|11.87|0 FastDB|3|FastDB|F:\VSQL\FastDB.mdf|ROWS|396|2643|10|1024.20|0 FastDB|3|FastDB_log|F:\VSQL\FastDB_log.ldf|LOG|396|211|10|11.94|0 FastDB|4|FastDB|F:\VSQL\FastDB.mdf|ROWS|393|2325|13|1024.21|0 FastDB|4|FastDB_log|F:\VSQL\FastDB_log.ldf|LOG|393|209|14|11.91|0 write speeds in SQL Server Vlad Drumea DBA SQL Server VladDBA

The write speed improvement brought on by the faster storage is noticeable, with an average write speed of 433 milliseconds per 1GB and 7 milliseconds of IO stall time on average.

Faster storage

Now for FasterDB, which also resides on a Samsung 980 Pro SSD, but this one is connected to a PCIe 4.0 M.2 slot.

result set 1 database|passes|file_physical_name|file_type|written_per_pass_MB|avg_duration_ms|avg_io_stall_write_ms|avg_writes_per_pass|avg_file_size_increase_MB FasterDB|4|C:\VSQL\FasterDB.mdf|ROWS|1024.41|390|1|2520|0 FasterDB|4|C:\VSQL\FasterDB_log.ldf|LOG|11.93|390|0|209|0 result set 2 database|file_physical_name|file_type|total_written_MB|total_duration_ms|total_avg_io_stall_write_ms|total_writes|total_file_size_increase_MB FasterDB|C:\VSQL\FasterDB.mdf|ROWS|4097.63|1562|4|10080|0 FasterDB|C:\VSQL\FasterDB_log.ldf|LOG|47.72|1562|0|839|0 result set 3 database|pass|file_logical_name|file_physical_name|file_type|duration_ms|writes|avg_io_stall_write_ms|written_MB|file_size_increase_MB FasterDB|1|FasterDB|C:\VSQL\FasterDB.mdf|ROWS|393|2511|1|1024.93|0 FasterDB|1|FasterDB_log|C:\VSQL\FasterDB_log.ldf|LOG|393|212|0|11.91|0 FasterDB|2|FasterDB|C:\VSQL\FasterDB.mdf|ROWS|396|2528|1|1024.30|0 FasterDB|2|FasterDB_log|C:\VSQL\FasterDB_log.ldf|LOG|396|209|0|11.95|0 FasterDB|3|FasterDB|C:\VSQL\FasterDB.mdf|ROWS|390|2438|1|1024.20|0 FasterDB|3|FasterDB_log|C:\VSQL\FasterDB_log.ldf|LOG|390|209|0|11.93|0 FasterDB|4|FasterDB|C:\VSQL\FasterDB.mdf|ROWS|383|2603|1|1024.20|0 FasterDB|4|FasterDB_log|C:\VSQL\FasterDB_log.ldf|LOG|383|209|0|11.93|0 write speeds in SQL Server Vlad Drumea DBA SQL Server VladDBA

On this drive the average write time went down to 390 milliseconds for 1GB, and the IO write stall for the data file is a consistent 1 millisecond on each pass.


This script should be a decent option when you’re limited on what you can use to benchmark storage write speeds.

For testing storage read speeds, check this blog post covering how to do that via backing up to NUL device.

You may also like


Sab March 4, 2024 - 08:27

Good work!
For minimize the influence of the transaction log, delay durability is an option.

Vlad Drumea March 4, 2024 - 19:03

Thanks, Sab!
I plan to look into delay durability in an upcoming post where I’ll cover how write speeds are impacted by file size and auto-growth increments, full recovery model, and other configuration options.

Measuring Write Speeds in SQL Server – Curated SQL March 5, 2024 - 15:00

[…] Vlad Drumea performs a test: […]

patamons April 9, 2024 - 15:29

Hi Vlad,

Thank you for your script. Any idea what might be acceptable on a virtual server? I tried it on some of our VMWare servers and the results are much slower than your tests on slow storage:
avg_duration_ms : 7836 (horrible) ; avg_io_stall_write_ms : 46/5 (ok)

Is this acceptable on virtual servers or should I check with the virtualisation team?

Vlad Drumea April 9, 2024 - 22:39

Hi Patamons,
7836 is a bit on the high side. Is your database using the full recovery model? Since the speed is bound to the slowest file, transaction log write speeds affect the overall results.
If that’s the case then either try uncommenting line 136 or creating a separate database that uses the simple recovery model.
If not, then I’d recommend running sp_Blitz and checking if it returns the “Slow Storage Writes on Drive …” finding. If you get the same finding there, then it might be time to talk to your system/storage admins.


Leave a Comment

* By using this form you agree with the storage and handling of your data by this website.