NAS performance tester (C# and VB.Net)
This free utility benchmarks the read and write performance in megabytes per second of network attached storage connected through SMB/CIFS network shares.How it works: Temporary files of selectable sizes are generated and copied to and from the NAS a number of times, whereafter the average read and write speeds are calculated.
The utility is available compiled for .NET framework 4.x* with source code included:
Download NAS performance tester 1.700c
*If you only have Microsoft .NET framework version 2.0 installed, you can re-compile the program to that (or any newer .Net version installed on the PC) by running the script compile.vbs included in the zip file.
Note that reliable results depend on that the local computer drive, the test is being executed from, has faster read and write speeds than the NAS being tested. Also note that the program's method of generating the read speed test files on the NAS is specifically tuned for avoiding caching on network drives. This means that running the benchmark against local or iSCSI connected drives will give incorrect read speed results due to locally cached test files. Please use it only for testing network drives.
The standard disclaimer: The program is provided 'as is' without warranty of any kind. I have done my best to incorporate graceful exception handling and to test the code in different scenarios, so it shouldn't be able to do anything bad. The source code for the program is listed below.
using System;
using System.Drawing;
using System.Windows.Forms;
using System.Text;
using System.IO;
using System.Threading;
using System.Reflection;
[assembly: AssemblyTitle("NAS performance tester 1.700c")]
[assembly: AssemblyDescription("A small C# utility for benchmarking the read and write performance of network attached storage")]
[assembly: AssemblyCompany("www.700c.dk")]
[assembly: AssemblyProduct("NasTester")]
[assembly: AssemblyCopyright("Creative Commons Attribution (CC BY 3.0) https://creativecommons.org/licenses/by/3.0/ Reference source https://www.700c.dk/?code-csharp-nas-performance")]
[assembly: AssemblyVersion("1.7.0.0")]
[assembly: CLSCompliant(true)]
public class NasPerformanceForm : Form
{
private Label driveLetterLabel = new Label();
private Label networkPathLabel = new Label();
private Label fileSizeLabel = new Label();
private Label loopsLabel = new Label();
private Button benchmarkButton = new Button();
private TextBox networkPath = new TextBox();
private ComboBox driveLetter = new ComboBox();
private ComboBox fileSize = new ComboBox();
private ComboBox loops = new ComboBox();
private TextBox resultArea = new TextBox();
private Label infoLabel = new Label();
private LinkLabel urlLabel = new LinkLabel();
private bool loopBreak = false;
private string testPath;
private ulong testFileSize;
private ulong testIterations;
private string testType;
private string localStoragePath;
private Thread workerThread;
private bool testIsActive = false;
public NasPerformanceForm()
{
this.Text = "NAS performance tester 1.700c";
this.Size = new Size(558, 400);
this.Font = new Font("Microsoft Sans Serif", 8);
driveLetterLabel.Location = new Point(5, 8);
driveLetterLabel.Text = "NAS drive letter";
driveLetterLabel.Size = new Size (83, 20);
driveLetter.Location = new Point(90, 5);
driveLetter.Size = new Size(33, 15);
driveLetter.Items.AddRange(GetNetworkDriveLetters());
if (GetNetworkDriveLetters().Length > 0)
{
driveLetter.SelectedIndex = 0;
}
networkPathLabel.Location = new Point(123, 8);
networkPathLabel.Text = "or network path";
networkPathLabel.Size = new Size(80, 20);
networkPath.Location = new Point(205, 5);
networkPath.Text = "";
networkPath.Size = new Size(90, 20);
fileSizeLabel.Location = new Point(302, 8);
fileSizeLabel.Text = "File size";
fileSizeLabel.Size = new Size(50, 20);
fileSize.Location = new Point(352, 5);
fileSize.Size = new Size(49, 15);
fileSize.Items.AddRange(new object[]
{"100",
"200",
"400",
"800",
"1000",
"2000",
"4000",
"8000"});
fileSize.SelectedIndex = 2;
loopsLabel.Location = new Point(407, 8);
loopsLabel.Text = "Loops";
loopsLabel.Size = new Size(36, 20);
loops.Location = new Point(443, 5);
loops.Size = new Size(37, 15);
loops.Items.AddRange(new object[]
{"1",
"2",
"3",
"4",
"5",
"10",
"20",
"40"});
loops.SelectedIndex = 4;
benchmarkButton.Location = new Point(487, 5);
benchmarkButton.Size = new Size(50, 20);
benchmarkButton.Text = "Start";
resultArea.Location = new Point(5, 30);
resultArea.Size = new Size(533,305);
resultArea.ReadOnly = true;
resultArea.Multiline = true;
resultArea.ScrollBars = ScrollBars.Vertical;
resultArea.WordWrap = false;
resultArea.Text = "NAS performance tester 1.700c https://www.700c.dk/?nastester\r\n";
resultArea.Font = new Font("Courier New", 8);
infoLabel.Location = new Point(5, 341);
infoLabel.Text = "For more information, visit";
infoLabel.Size = new Size(140, 20);
urlLabel.Location = new Point(143, 341);
urlLabel.Text = "https://www.700c.dk/?code-csharp-nas-performance";
urlLabel.Links.Add(0, 48, "https://www.700c.dk/?code-csharp-nas-performance");
urlLabel.Size = new Size(300, 20);
this.Controls.Add(driveLetterLabel);
this.Controls.Add(driveLetter);
this.Controls.Add(networkPathLabel);
this.Controls.Add(networkPath);
this.Controls.Add(fileSizeLabel);
this.Controls.Add(fileSize);
this.Controls.Add(loopsLabel);
this.Controls.Add(loops);
this.Controls.Add(benchmarkButton);
this.Controls.Add(resultArea);
this.Controls.Add(infoLabel);
this.Controls.Add(urlLabel);
benchmarkButton.Click += new EventHandler(benchmarkButton_Click);
urlLabel.LinkClicked += new LinkLabelLinkClickedEventHandler(this.urlLabel_LinkClicked);
this.ActiveControl = networkPath;
if (IsFolderWritable(Path.GetDirectoryName(Application.ExecutablePath)))
{
localStoragePath = Path.GetDirectoryName(Application.ExecutablePath);
}
else // Write access to program folder denied, falling back to Windows temp folder as defined in the environment variables
{
localStoragePath = Path.GetTempPath();
}
}
private void benchmarkButton_Click(object sender, EventArgs e)
{
if (!testIsActive)
{
int checkFileSize;
int checkLoops;
if ((!String.IsNullOrEmpty(driveLetter.Text) || (networkPath.Text.IndexOf("\\\\") == 0 && networkPath.Text.IndexOf("\\", 2) > 2)) &&
int.TryParse(fileSize.Text, out checkFileSize) &&
int.TryParse(loops.Text, out checkLoops))
{
if(checkFileSize <= 64000)
{
Thread newThread = new Thread(new ThreadStart(BenchmarkHandler));
newThread.Start();
}
else
{
resultArea.AppendText("The maximum test file size is 64GB.\r\n");
resultArea.Invalidate();
}
}
else
{
resultArea.AppendText("Input invalid. Check drive letter or network path, file size and loops.\r\n");
resultArea.Invalidate();
}
}
else
{
loopBreak = true;
resultArea.AppendText("Cancelling. Please wait for current loop to finish...\r\n");
resultArea.Invalidate();
}
}
private void urlLabel_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
{
string target = e.Link.LinkData as string;
System.Diagnostics.Process.Start(target);
}
private void BenchmarkHandler()
{
testIsActive = true;
loopBreak = false;
benchmarkButton.Text = "Stop";
if (String.IsNullOrEmpty(networkPath.Text))
{
testPath = driveLetter.Text + ":";
}
else
{
testPath = networkPath.Text;
}
if (!loopBreak)
{ // Run warmup
testFileSize = 0; // for warmup run
testIterations = 1;
testType = "write";
LaunchTestThread();
}
testFileSize = Convert.ToUInt64(fileSize.Text) * 1000000;
testIterations = Convert.ToUInt64(loops.Text);
if (!loopBreak)
{ // Run write test
testType = "write";
LaunchTestThread();
}
if (!loopBreak)
{ // Run read test
testType = "read";
LaunchTestThread();
}
testIsActive = false;
loopBreak = false;
benchmarkButton.Text = "Start";
}
private void LaunchTestThread()
{
workerThread = new Thread(TestPerf);
workerThread.Start();
while (!workerThread.IsAlive); // wait for thread activation
while (workerThread.IsAlive) // wait for thread termination
{
Thread.Sleep(500);
}
}
private void TestPerf()
{
try
{
string firstPath;
string secondPath;
string shortType;
string iterText;
ulong appendIterations;
double totalPerf;
DateTime startTime;
DateTime stopTime;
string randomText = RandomString(100000);
if (testType == "read")
{
firstPath = testPath;
secondPath = localStoragePath;
shortType = "R";
}
else
{
firstPath = localStoragePath;
secondPath = testPath;
shortType = "W";
}
if (testIterations == 1)
{
iterText = "once";
}
else if (testIterations == 2)
{
iterText = "twice";
}
else
{
iterText = testIterations + " times";
}
if (testFileSize == 0)
{
resultArea.AppendText("Running warmup...\r\n");
resultArea.Invalidate();
appendIterations = 100; // equals a 10MB warmup file.
}
else
{
resultArea.AppendText(
"Running a " + testFileSize/1000000 + "MB file " + testType +
" on " + testPath + " " + iterText + "...\r\n");
appendIterations = testFileSize/100000;
// Note: dividing integers in C# always produce a whole number,
// so no explicit rounding or type conversion is needed
}
totalPerf = 0;
for(ulong j=1; j<=testIterations; j++)
{
Application.DoEvents();
if (File.Exists(firstPath + "\\" + j + "test.tmp"))
{
File.Delete(firstPath + "\\" + j + "test.tmp");
}
if (File.Exists(secondPath + "\\" + j + "test.tmp"))
{
File.Delete(secondPath + "\\" + j + "test.tmp");
}
if (loopBreak == true)
{
resultArea.AppendText("Benchmark cancelled.\r\n");
resultArea.Invalidate();
break;
}
StreamWriter sWriter = new StreamWriter(firstPath +
"\\" + j + "test.tmp", true, Encoding.UTF8, 1048576);
for(ulong i=1; i<=appendIterations; i++)
{
sWriter.Write(randomText);
}
sWriter.Close();
startTime = DateTime.Now;
File.Copy(firstPath + "\\" + j + "test.tmp",
secondPath + "\\" + j + "test.tmp");
stopTime = DateTime.Now;
File.Delete(firstPath + "\\" + j + "test.tmp");
File.Delete(secondPath + "\\" + j + "test.tmp");
TimeSpan interval = stopTime - startTime;
if (testIterations > 1)
{
resultArea.AppendText(
("Iteration " + j + ":").PadRight(15) +
((testFileSize / 1000) / interval.TotalMilliseconds).ToString("F2").PadLeft(7) +
" MB/sec\r\n");
resultArea.Invalidate();
}
totalPerf += (testFileSize/1000)/interval.TotalMilliseconds;
}
if ((testFileSize != 0) && (loopBreak == false))
{
resultArea.AppendText("-----------------------------\r\n");
resultArea.AppendText("Average (" + shortType + "):" +
(totalPerf / testIterations).ToString("F2").PadLeft(10) + " MB/sec\r\n");
resultArea.AppendText("-----------------------------\r\n");
resultArea.Invalidate();
}
}
catch (Exception e)
{
resultArea.AppendText("An error occured: " + e.Message + "\r\n");
}
}
private string RandomString(int size)
{
StringBuilder builder = new StringBuilder();
Random random = new Random();
char ch;
for (int i = 0; i < size; i++)
{
ch = Convert.ToChar(Convert.ToInt32(Math.Floor(26 * random.NextDouble() + 65)));
builder.Append(ch);
}
return builder.ToString();
}
private string[] GetNetworkDriveLetters()
{
System.Collections.ArrayList NetworkDriveLetters = new System.Collections.ArrayList();
DriveInfo[] allDrives = DriveInfo.GetDrives();
foreach (DriveInfo d in allDrives)
{
if (d.DriveType == DriveType.Network)
{
NetworkDriveLetters.Add(d.Name.Substring(0, 1));
}
}
return NetworkDriveLetters.ToArray(typeof(string)) as string[];
}
private bool IsFolderWritable(string folderPath)
{
try
{
using (FileStream fs = File.Create(
Path.Combine(folderPath, Path.GetRandomFileName()),
1, FileOptions.DeleteOnClose)
)
{ }
return true;
}
catch
{
return false;
}
}
static void Main()
{
Application.Run(new NasPerformanceForm());
}
}
using System.Drawing;
using System.Windows.Forms;
using System.Text;
using System.IO;
using System.Threading;
using System.Reflection;
[assembly: AssemblyTitle("NAS performance tester 1.700c")]
[assembly: AssemblyDescription("A small C# utility for benchmarking the read and write performance of network attached storage")]
[assembly: AssemblyCompany("www.700c.dk")]
[assembly: AssemblyProduct("NasTester")]
[assembly: AssemblyCopyright("Creative Commons Attribution (CC BY 3.0) https://creativecommons.org/licenses/by/3.0/ Reference source https://www.700c.dk/?code-csharp-nas-performance")]
[assembly: AssemblyVersion("1.7.0.0")]
[assembly: CLSCompliant(true)]
public class NasPerformanceForm : Form
{
private Label driveLetterLabel = new Label();
private Label networkPathLabel = new Label();
private Label fileSizeLabel = new Label();
private Label loopsLabel = new Label();
private Button benchmarkButton = new Button();
private TextBox networkPath = new TextBox();
private ComboBox driveLetter = new ComboBox();
private ComboBox fileSize = new ComboBox();
private ComboBox loops = new ComboBox();
private TextBox resultArea = new TextBox();
private Label infoLabel = new Label();
private LinkLabel urlLabel = new LinkLabel();
private bool loopBreak = false;
private string testPath;
private ulong testFileSize;
private ulong testIterations;
private string testType;
private string localStoragePath;
private Thread workerThread;
private bool testIsActive = false;
public NasPerformanceForm()
{
this.Text = "NAS performance tester 1.700c";
this.Size = new Size(558, 400);
this.Font = new Font("Microsoft Sans Serif", 8);
driveLetterLabel.Location = new Point(5, 8);
driveLetterLabel.Text = "NAS drive letter";
driveLetterLabel.Size = new Size (83, 20);
driveLetter.Location = new Point(90, 5);
driveLetter.Size = new Size(33, 15);
driveLetter.Items.AddRange(GetNetworkDriveLetters());
if (GetNetworkDriveLetters().Length > 0)
{
driveLetter.SelectedIndex = 0;
}
networkPathLabel.Location = new Point(123, 8);
networkPathLabel.Text = "or network path";
networkPathLabel.Size = new Size(80, 20);
networkPath.Location = new Point(205, 5);
networkPath.Text = "";
networkPath.Size = new Size(90, 20);
fileSizeLabel.Location = new Point(302, 8);
fileSizeLabel.Text = "File size";
fileSizeLabel.Size = new Size(50, 20);
fileSize.Location = new Point(352, 5);
fileSize.Size = new Size(49, 15);
fileSize.Items.AddRange(new object[]
{"100",
"200",
"400",
"800",
"1000",
"2000",
"4000",
"8000"});
fileSize.SelectedIndex = 2;
loopsLabel.Location = new Point(407, 8);
loopsLabel.Text = "Loops";
loopsLabel.Size = new Size(36, 20);
loops.Location = new Point(443, 5);
loops.Size = new Size(37, 15);
loops.Items.AddRange(new object[]
{"1",
"2",
"3",
"4",
"5",
"10",
"20",
"40"});
loops.SelectedIndex = 4;
benchmarkButton.Location = new Point(487, 5);
benchmarkButton.Size = new Size(50, 20);
benchmarkButton.Text = "Start";
resultArea.Location = new Point(5, 30);
resultArea.Size = new Size(533,305);
resultArea.ReadOnly = true;
resultArea.Multiline = true;
resultArea.ScrollBars = ScrollBars.Vertical;
resultArea.WordWrap = false;
resultArea.Text = "NAS performance tester 1.700c https://www.700c.dk/?nastester\r\n";
resultArea.Font = new Font("Courier New", 8);
infoLabel.Location = new Point(5, 341);
infoLabel.Text = "For more information, visit";
infoLabel.Size = new Size(140, 20);
urlLabel.Location = new Point(143, 341);
urlLabel.Text = "https://www.700c.dk/?code-csharp-nas-performance";
urlLabel.Links.Add(0, 48, "https://www.700c.dk/?code-csharp-nas-performance");
urlLabel.Size = new Size(300, 20);
this.Controls.Add(driveLetterLabel);
this.Controls.Add(driveLetter);
this.Controls.Add(networkPathLabel);
this.Controls.Add(networkPath);
this.Controls.Add(fileSizeLabel);
this.Controls.Add(fileSize);
this.Controls.Add(loopsLabel);
this.Controls.Add(loops);
this.Controls.Add(benchmarkButton);
this.Controls.Add(resultArea);
this.Controls.Add(infoLabel);
this.Controls.Add(urlLabel);
benchmarkButton.Click += new EventHandler(benchmarkButton_Click);
urlLabel.LinkClicked += new LinkLabelLinkClickedEventHandler(this.urlLabel_LinkClicked);
this.ActiveControl = networkPath;
if (IsFolderWritable(Path.GetDirectoryName(Application.ExecutablePath)))
{
localStoragePath = Path.GetDirectoryName(Application.ExecutablePath);
}
else // Write access to program folder denied, falling back to Windows temp folder as defined in the environment variables
{
localStoragePath = Path.GetTempPath();
}
}
private void benchmarkButton_Click(object sender, EventArgs e)
{
if (!testIsActive)
{
int checkFileSize;
int checkLoops;
if ((!String.IsNullOrEmpty(driveLetter.Text) || (networkPath.Text.IndexOf("\\\\") == 0 && networkPath.Text.IndexOf("\\", 2) > 2)) &&
int.TryParse(fileSize.Text, out checkFileSize) &&
int.TryParse(loops.Text, out checkLoops))
{
if(checkFileSize <= 64000)
{
Thread newThread = new Thread(new ThreadStart(BenchmarkHandler));
newThread.Start();
}
else
{
resultArea.AppendText("The maximum test file size is 64GB.\r\n");
resultArea.Invalidate();
}
}
else
{
resultArea.AppendText("Input invalid. Check drive letter or network path, file size and loops.\r\n");
resultArea.Invalidate();
}
}
else
{
loopBreak = true;
resultArea.AppendText("Cancelling. Please wait for current loop to finish...\r\n");
resultArea.Invalidate();
}
}
private void urlLabel_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
{
string target = e.Link.LinkData as string;
System.Diagnostics.Process.Start(target);
}
private void BenchmarkHandler()
{
testIsActive = true;
loopBreak = false;
benchmarkButton.Text = "Stop";
if (String.IsNullOrEmpty(networkPath.Text))
{
testPath = driveLetter.Text + ":";
}
else
{
testPath = networkPath.Text;
}
if (!loopBreak)
{ // Run warmup
testFileSize = 0; // for warmup run
testIterations = 1;
testType = "write";
LaunchTestThread();
}
testFileSize = Convert.ToUInt64(fileSize.Text) * 1000000;
testIterations = Convert.ToUInt64(loops.Text);
if (!loopBreak)
{ // Run write test
testType = "write";
LaunchTestThread();
}
if (!loopBreak)
{ // Run read test
testType = "read";
LaunchTestThread();
}
testIsActive = false;
loopBreak = false;
benchmarkButton.Text = "Start";
}
private void LaunchTestThread()
{
workerThread = new Thread(TestPerf);
workerThread.Start();
while (!workerThread.IsAlive); // wait for thread activation
while (workerThread.IsAlive) // wait for thread termination
{
Thread.Sleep(500);
}
}
private void TestPerf()
{
try
{
string firstPath;
string secondPath;
string shortType;
string iterText;
ulong appendIterations;
double totalPerf;
DateTime startTime;
DateTime stopTime;
string randomText = RandomString(100000);
if (testType == "read")
{
firstPath = testPath;
secondPath = localStoragePath;
shortType = "R";
}
else
{
firstPath = localStoragePath;
secondPath = testPath;
shortType = "W";
}
if (testIterations == 1)
{
iterText = "once";
}
else if (testIterations == 2)
{
iterText = "twice";
}
else
{
iterText = testIterations + " times";
}
if (testFileSize == 0)
{
resultArea.AppendText("Running warmup...\r\n");
resultArea.Invalidate();
appendIterations = 100; // equals a 10MB warmup file.
}
else
{
resultArea.AppendText(
"Running a " + testFileSize/1000000 + "MB file " + testType +
" on " + testPath + " " + iterText + "...\r\n");
appendIterations = testFileSize/100000;
// Note: dividing integers in C# always produce a whole number,
// so no explicit rounding or type conversion is needed
}
totalPerf = 0;
for(ulong j=1; j<=testIterations; j++)
{
Application.DoEvents();
if (File.Exists(firstPath + "\\" + j + "test.tmp"))
{
File.Delete(firstPath + "\\" + j + "test.tmp");
}
if (File.Exists(secondPath + "\\" + j + "test.tmp"))
{
File.Delete(secondPath + "\\" + j + "test.tmp");
}
if (loopBreak == true)
{
resultArea.AppendText("Benchmark cancelled.\r\n");
resultArea.Invalidate();
break;
}
StreamWriter sWriter = new StreamWriter(firstPath +
"\\" + j + "test.tmp", true, Encoding.UTF8, 1048576);
for(ulong i=1; i<=appendIterations; i++)
{
sWriter.Write(randomText);
}
sWriter.Close();
startTime = DateTime.Now;
File.Copy(firstPath + "\\" + j + "test.tmp",
secondPath + "\\" + j + "test.tmp");
stopTime = DateTime.Now;
File.Delete(firstPath + "\\" + j + "test.tmp");
File.Delete(secondPath + "\\" + j + "test.tmp");
TimeSpan interval = stopTime - startTime;
if (testIterations > 1)
{
resultArea.AppendText(
("Iteration " + j + ":").PadRight(15) +
((testFileSize / 1000) / interval.TotalMilliseconds).ToString("F2").PadLeft(7) +
" MB/sec\r\n");
resultArea.Invalidate();
}
totalPerf += (testFileSize/1000)/interval.TotalMilliseconds;
}
if ((testFileSize != 0) && (loopBreak == false))
{
resultArea.AppendText("-----------------------------\r\n");
resultArea.AppendText("Average (" + shortType + "):" +
(totalPerf / testIterations).ToString("F2").PadLeft(10) + " MB/sec\r\n");
resultArea.AppendText("-----------------------------\r\n");
resultArea.Invalidate();
}
}
catch (Exception e)
{
resultArea.AppendText("An error occured: " + e.Message + "\r\n");
}
}
private string RandomString(int size)
{
StringBuilder builder = new StringBuilder();
Random random = new Random();
char ch;
for (int i = 0; i < size; i++)
{
ch = Convert.ToChar(Convert.ToInt32(Math.Floor(26 * random.NextDouble() + 65)));
builder.Append(ch);
}
return builder.ToString();
}
private string[] GetNetworkDriveLetters()
{
System.Collections.ArrayList NetworkDriveLetters = new System.Collections.ArrayList();
DriveInfo[] allDrives = DriveInfo.GetDrives();
foreach (DriveInfo d in allDrives)
{
if (d.DriveType == DriveType.Network)
{
NetworkDriveLetters.Add(d.Name.Substring(0, 1));
}
}
return NetworkDriveLetters.ToArray(typeof(string)) as string[];
}
private bool IsFolderWritable(string folderPath)
{
try
{
using (FileStream fs = File.Create(
Path.Combine(folderPath, Path.GetRandomFileName()),
1, FileOptions.DeleteOnClose)
)
{ }
return true;
}
catch
{
return false;
}
}
static void Main()
{
Application.Run(new NasPerformanceForm());
}
}
Or, if you prefer VB.Net:
Imports System
Imports System.Drawing
Imports System.Windows.Forms
Imports System.Text
Imports System.IO
Imports System.Threading
Imports System.Reflection
<Assembly: AssemblyTitle("NAS performance tester 1.700c")>
<Assembly: AssemblyDescription("A small VB.Net utility for benchmarking the read and write performance of network attached storage")>
<Assembly: AssemblyCompany("www.700c.dk")>
<Assembly: AssemblyProduct("NasTester")>
<Assembly: AssemblyCopyright("Creative Commons Attribution (CC BY 3.0) https://creativecommons.org/licenses/by/3.0/ Reference source https://www.700c.dk/?code-csharp-nas-performance")>
<Assembly: AssemblyVersion("1.7.0.0")>
<Assembly: CLSCompliant(True)>
Public Class NasPerformanceForm
Inherits Form
Private driveLetterLabel As New Label()
Private networkPathLabel As New Label()
Private fileSizeLabel As New Label()
Private loopsLabel As New Label()
Private benchmarkButton As New Button()
Private networkPath As New TextBox()
Private driveLetter As New ComboBox()
Private fileSize As New ComboBox()
Private loops As New ComboBox()
Private resultArea As New TextBox()
Private infoLabel As New Label()
Private urlLabel As New LinkLabel()
Private loopBreak As Boolean = False
Private testPath As String
Private testFileSize As ULong
Private testIterations As ULong
Private testType As String
Private localStoragePath As String
Private workerThread As Thread
Private testIsActive As Boolean = False
Public Sub New()
Me.Text = "NAS performance tester 1.700c"
Me.Size = New Size(558, 400)
Me.Font = New Font("Microsoft Sans Serif", 8)
driveLetterLabel.Location = New Point(5, 8)
driveLetterLabel.Text = "NAS drive letter"
driveLetterLabel.Size = New Size(83, 20)
driveLetter.Location = New Point(90, 5)
driveLetter.Size = New Size(33, 15)
driveLetter.Items.AddRange(GetNetworkDriveLetters())
If GetNetworkDriveLetters().Length > 0 Then
driveLetter.SelectedIndex = 0
End If
networkPathLabel.Location = New Point(123, 8)
networkPathLabel.Text = "or network path"
networkPathLabel.Size = New Size(80, 20)
networkPath.Location = New Point(205, 5)
networkPath.Text = ""
networkPath.Size = New Size(90, 20)
fileSizeLabel.Location = New Point(302, 8)
fileSizeLabel.Text = "File size"
fileSizeLabel.Size = New Size(50, 20)
fileSize.Location = New Point(352, 5)
fileSize.Size = New Size(49, 15)
fileSize.Items.AddRange(New Object() _
{"100", _
"200", _
"400", _
"800", _
"1000", _
"2000", _
"4000", _
"8000"})
fileSize.SelectedIndex = 2
loopsLabel.Location = New Point(407, 8)
loopsLabel.Text = "Loops"
loopsLabel.Size = New Size(36, 20)
loops.Location = New Point(443, 5)
loops.Size = New Size(37, 15)
loops.Items.AddRange(New Object() _
{"1", _
"2", _
"3", _
"4", _
"5", _
"10", _
"20", _
"40"})
loops.SelectedIndex = 4
benchmarkButton.Location = New Point(487, 5)
benchmarkButton.Size = New Size(50, 20)
benchmarkButton.Text = "Start"
resultArea.Location = New Point(5, 30)
resultArea.Size = New Size(533, 305)
resultArea.[ReadOnly] = True
resultArea.Multiline = True
resultArea.ScrollBars = ScrollBars.Vertical
resultArea.WordWrap = False
resultArea.Text = "NAS performance tester 1.7 https://www.700c.dk/?nastester" & vbCrLf
resultArea.Font = New Font("Courier New", 8)
infoLabel.Location = New Point(5, 341)
infoLabel.Text = "For more information, visit"
infoLabel.Size = New Size(140, 20)
urlLabel.Location = New Point(143, 341)
urlLabel.Text = "https://www.700c.dk/?code-csharp-nas-performance"
urlLabel.Links.Add(0, 48, "https://www.700c.dk/?code-csharp-nas-performance")
urlLabel.Size = New Size(300, 20)
Me.Controls.Add(driveLetterLabel)
Me.Controls.Add(driveLetter)
Me.Controls.Add(networkPathLabel)
Me.Controls.Add(networkPath)
Me.Controls.Add(fileSizeLabel)
Me.Controls.Add(fileSize)
Me.Controls.Add(loopsLabel)
Me.Controls.Add(loops)
Me.Controls.Add(benchmarkButton)
Me.Controls.Add(resultArea)
Me.Controls.Add(infoLabel)
Me.Controls.Add(urlLabel)
AddHandler benchmarkButton.Click, New EventHandler(AddressOf benchmarkButton_Click)
AddHandler urlLabel.LinkClicked, New LinkLabelLinkClickedEventHandler(AddressOf Me.urlLabel_LinkClicked)
Me.ActiveControl = networkPath
If IsFolderWritable(Path.GetDirectoryName(Application.ExecutablePath)) Then
localStoragePath = Path.GetDirectoryName(Application.ExecutablePath)
Else ' Write access to program folder denied, falling back to Windows temp folder as defined in the environment variables
localStoragePath = Path.GetTempPath()
End If
End Sub
Private Sub benchmarkButton_Click(sender As Object, e As EventArgs)
If Not testIsActive Then
Dim checkFileSize As Integer
Dim checkLoops As Integer
If (Not String.IsNullOrEmpty(driveLetter.Text) Or (networkPath.Text.IndexOf("\\") = 0 AndAlso networkPath.Text.IndexOf("\", 2) > 2)) AndAlso _
Integer.TryParse(fileSize.Text, checkFileSize) AndAlso _
Integer.TryParse(loops.Text, checkLoops) Then
If checkFileSize <= 64000 Then
Dim newThread As New Thread(New ThreadStart(AddressOf BenchmarkHandler))
newThread.Start()
Else
resultArea.AppendText("The maximum test file size is 64GB." & vbCrLf)
resultArea.Invalidate()
End If
Else
resultArea.AppendText("Input invalid. Check drive letter or network path, file size and loops." & vbCrLf)
resultArea.Invalidate()
End If
Else
loopBreak = True
resultArea.AppendText("Cancelling. Please wait for current loop to finish..." & vbCrLf)
resultArea.Invalidate()
End If
End Sub
Private Sub urlLabel_LinkClicked(sender As Object, e As LinkLabelLinkClickedEventArgs)
Dim target As String = TryCast(e.Link.LinkData, String)
System.Diagnostics.Process.Start(target)
End Sub
Private Sub BenchmarkHandler()
testIsActive = True
loopBreak = False
benchmarkButton.Text = "Stop"
If String.IsNullOrEmpty(networkPath.Text) Then
testPath = driveLetter.Text & ":"
Else
testPath = networkPath.Text
End If
If Not loopBreak Then ' Run warmup
testFileSize = 0 ' for warmup run
testIterations = 1
testType = "write"
LaunchTestThread()
End If
testFileSize = Convert.ToUInt64(fileSize.Text) * 1000000
testIterations = Convert.ToUInt64(loops.Text)
If Not loopBreak Then ' Run write test
testType = "write"
LaunchTestThread()
End If
If Not loopBreak Then ' Run read test
testType = "read"
LaunchTestThread()
End If
testIsActive = False
loopBreak = False
benchmarkButton.Text = "Start"
End Sub
Private Sub LaunchTestThread()
workerThread = New Thread(AddressOf TestPerf)
workerThread.Start()
While Not workerThread.IsAlive ' wait for thread activation
End While
While workerThread.IsAlive ' wait for thread termination
Thread.Sleep(500)
End While
End Sub
Private Sub TestPerf()
Try
Dim firstPath As String
Dim secondPath As String
Dim shortType As String
Dim iterText As String
Dim appendIterations As ULong
Dim totalPerf As Double
Dim startTime As DateTime
Dim stopTime As DateTime
Dim randomText As String = RandomString(100000)
If testType = "read" Then
firstPath = testPath
secondPath = localStoragePath
shortType = "R"
Else
firstPath = localStoragePath
secondPath = testPath
shortType = "W"
End If
If testIterations = 1 Then
iterText = "once"
ElseIf testIterations = 2 Then
iterText = "twice"
Else
iterText = testIterations & " times"
End If
If testFileSize = 0 Then
resultArea.AppendText("Running warmup..." & vbCrLf)
resultArea.Invalidate()
appendIterations = 100 ' equals a 10MB warmup file.
Else
resultArea.AppendText( _
"Running a " & testFileSize \ 1000000 & "MB file " & testType & _
" on " & testPath & " " & iterText & "..." & vbCrLf)
appendIterations = testFileSize \ 100000
' Note: dividing integers in VB.Net can produce a decimal number,
' so the integer division operator '\' is used instead of '/'
End If
totalPerf = 0
For j As Integer = 1 To testIterations
Application.DoEvents()
If File.Exists(firstPath & "\" & j & "test.tmp") Then
File.Delete(firstPath & "\" & j & "test.tmp")
End If
If File.Exists(secondPath & "\" & j & "test.tmp") Then
File.Delete(secondPath & "\" & j & "test.tmp")
End If
If loopBreak = True Then
resultArea.AppendText("Benchmark cancelled." & vbCrLf)
resultArea.Invalidate()
Exit For
End If
Dim sWriter As New StreamWriter(firstPath & _
"\" & j & "test.tmp", True, Encoding.UTF8, 1048576)
For i As Integer = 1 To appendIterations
sWriter.Write(randomText)
Next
sWriter.Close()
startTime = DateTime.Now
File.Copy(firstPath & "\" & j & "test.tmp", _
secondPath & "\" & j & "test.tmp")
stopTime = DateTime.Now
File.Delete(firstPath & "\" & j & "test.tmp")
File.Delete(secondPath & "\" & j & "test.tmp")
Dim interval As TimeSpan = stopTime - startTime
If testIterations > 1 Then
resultArea.AppendText( _
("Iteration " & j & ":").PadRight(15) & _
((testFileSize \ 1000) / interval.TotalMilliseconds).ToString("F2").PadLeft(7) & _
" MB/sec" & vbCrLf)
resultArea.Invalidate()
End If
totalPerf += (testFileSize \ 1000) / interval.TotalMilliseconds
Next
If (testFileSize <> 0) AndAlso (loopBreak = False) Then
resultArea.AppendText("-----------------------------" & vbCrLf)
resultArea.AppendText("Average (" & shortType & "):" & _
(totalPerf / testIterations).ToString("F2").PadLeft(10) & " MB/sec" & vbCrLf)
resultArea.AppendText("-----------------------------" & vbCrLf)
resultArea.Invalidate()
End If
Catch e As Exception
resultArea.AppendText("An error occured: " & e.Message & vbCrLf)
End Try
End Sub
Private Function RandomString(size As Integer) As String
Dim builder As New StringBuilder()
Dim random As New Random()
Dim ch As Char
For i As Integer = 0 To size - 1
ch = Convert.ToChar(Convert.ToInt32(Math.Floor(26 * random.NextDouble() + 65)))
builder.Append(ch)
Next
Return builder.ToString()
End Function
Private Function GetNetworkDriveLetters() As String()
Dim NetworkDriveLetters As New System.Collections.ArrayList()
Dim allDrives As DriveInfo() = DriveInfo.GetDrives()
For Each d As DriveInfo In allDrives
If d.DriveType = DriveType.Network Then
NetworkDriveLetters.Add(d.Name.Substring(0, 1))
End If
Next
Return TryCast(NetworkDriveLetters.ToArray(GetType(String)), String())
End Function
Private Function IsFolderWritable(folderPath As String) As Boolean
Try
Using fs As FileStream = _
File.Create(Path.Combine(folderPath, _
Path.GetRandomFileName()), 1, FileOptions.DeleteOnClose)
End Using
Return True
Catch
Return False
End Try
End Function
Shared Sub Main()
Application.Run(New NasPerformanceForm())
End Sub
End Class
Imports System.Drawing
Imports System.Windows.Forms
Imports System.Text
Imports System.IO
Imports System.Threading
Imports System.Reflection
<Assembly: AssemblyTitle("NAS performance tester 1.700c")>
<Assembly: AssemblyDescription("A small VB.Net utility for benchmarking the read and write performance of network attached storage")>
<Assembly: AssemblyCompany("www.700c.dk")>
<Assembly: AssemblyProduct("NasTester")>
<Assembly: AssemblyCopyright("Creative Commons Attribution (CC BY 3.0) https://creativecommons.org/licenses/by/3.0/ Reference source https://www.700c.dk/?code-csharp-nas-performance")>
<Assembly: AssemblyVersion("1.7.0.0")>
<Assembly: CLSCompliant(True)>
Public Class NasPerformanceForm
Inherits Form
Private driveLetterLabel As New Label()
Private networkPathLabel As New Label()
Private fileSizeLabel As New Label()
Private loopsLabel As New Label()
Private benchmarkButton As New Button()
Private networkPath As New TextBox()
Private driveLetter As New ComboBox()
Private fileSize As New ComboBox()
Private loops As New ComboBox()
Private resultArea As New TextBox()
Private infoLabel As New Label()
Private urlLabel As New LinkLabel()
Private loopBreak As Boolean = False
Private testPath As String
Private testFileSize As ULong
Private testIterations As ULong
Private testType As String
Private localStoragePath As String
Private workerThread As Thread
Private testIsActive As Boolean = False
Public Sub New()
Me.Text = "NAS performance tester 1.700c"
Me.Size = New Size(558, 400)
Me.Font = New Font("Microsoft Sans Serif", 8)
driveLetterLabel.Location = New Point(5, 8)
driveLetterLabel.Text = "NAS drive letter"
driveLetterLabel.Size = New Size(83, 20)
driveLetter.Location = New Point(90, 5)
driveLetter.Size = New Size(33, 15)
driveLetter.Items.AddRange(GetNetworkDriveLetters())
If GetNetworkDriveLetters().Length > 0 Then
driveLetter.SelectedIndex = 0
End If
networkPathLabel.Location = New Point(123, 8)
networkPathLabel.Text = "or network path"
networkPathLabel.Size = New Size(80, 20)
networkPath.Location = New Point(205, 5)
networkPath.Text = ""
networkPath.Size = New Size(90, 20)
fileSizeLabel.Location = New Point(302, 8)
fileSizeLabel.Text = "File size"
fileSizeLabel.Size = New Size(50, 20)
fileSize.Location = New Point(352, 5)
fileSize.Size = New Size(49, 15)
fileSize.Items.AddRange(New Object() _
{"100", _
"200", _
"400", _
"800", _
"1000", _
"2000", _
"4000", _
"8000"})
fileSize.SelectedIndex = 2
loopsLabel.Location = New Point(407, 8)
loopsLabel.Text = "Loops"
loopsLabel.Size = New Size(36, 20)
loops.Location = New Point(443, 5)
loops.Size = New Size(37, 15)
loops.Items.AddRange(New Object() _
{"1", _
"2", _
"3", _
"4", _
"5", _
"10", _
"20", _
"40"})
loops.SelectedIndex = 4
benchmarkButton.Location = New Point(487, 5)
benchmarkButton.Size = New Size(50, 20)
benchmarkButton.Text = "Start"
resultArea.Location = New Point(5, 30)
resultArea.Size = New Size(533, 305)
resultArea.[ReadOnly] = True
resultArea.Multiline = True
resultArea.ScrollBars = ScrollBars.Vertical
resultArea.WordWrap = False
resultArea.Text = "NAS performance tester 1.7 https://www.700c.dk/?nastester" & vbCrLf
resultArea.Font = New Font("Courier New", 8)
infoLabel.Location = New Point(5, 341)
infoLabel.Text = "For more information, visit"
infoLabel.Size = New Size(140, 20)
urlLabel.Location = New Point(143, 341)
urlLabel.Text = "https://www.700c.dk/?code-csharp-nas-performance"
urlLabel.Links.Add(0, 48, "https://www.700c.dk/?code-csharp-nas-performance")
urlLabel.Size = New Size(300, 20)
Me.Controls.Add(driveLetterLabel)
Me.Controls.Add(driveLetter)
Me.Controls.Add(networkPathLabel)
Me.Controls.Add(networkPath)
Me.Controls.Add(fileSizeLabel)
Me.Controls.Add(fileSize)
Me.Controls.Add(loopsLabel)
Me.Controls.Add(loops)
Me.Controls.Add(benchmarkButton)
Me.Controls.Add(resultArea)
Me.Controls.Add(infoLabel)
Me.Controls.Add(urlLabel)
AddHandler benchmarkButton.Click, New EventHandler(AddressOf benchmarkButton_Click)
AddHandler urlLabel.LinkClicked, New LinkLabelLinkClickedEventHandler(AddressOf Me.urlLabel_LinkClicked)
Me.ActiveControl = networkPath
If IsFolderWritable(Path.GetDirectoryName(Application.ExecutablePath)) Then
localStoragePath = Path.GetDirectoryName(Application.ExecutablePath)
Else ' Write access to program folder denied, falling back to Windows temp folder as defined in the environment variables
localStoragePath = Path.GetTempPath()
End If
End Sub
Private Sub benchmarkButton_Click(sender As Object, e As EventArgs)
If Not testIsActive Then
Dim checkFileSize As Integer
Dim checkLoops As Integer
If (Not String.IsNullOrEmpty(driveLetter.Text) Or (networkPath.Text.IndexOf("\\") = 0 AndAlso networkPath.Text.IndexOf("\", 2) > 2)) AndAlso _
Integer.TryParse(fileSize.Text, checkFileSize) AndAlso _
Integer.TryParse(loops.Text, checkLoops) Then
If checkFileSize <= 64000 Then
Dim newThread As New Thread(New ThreadStart(AddressOf BenchmarkHandler))
newThread.Start()
Else
resultArea.AppendText("The maximum test file size is 64GB." & vbCrLf)
resultArea.Invalidate()
End If
Else
resultArea.AppendText("Input invalid. Check drive letter or network path, file size and loops." & vbCrLf)
resultArea.Invalidate()
End If
Else
loopBreak = True
resultArea.AppendText("Cancelling. Please wait for current loop to finish..." & vbCrLf)
resultArea.Invalidate()
End If
End Sub
Private Sub urlLabel_LinkClicked(sender As Object, e As LinkLabelLinkClickedEventArgs)
Dim target As String = TryCast(e.Link.LinkData, String)
System.Diagnostics.Process.Start(target)
End Sub
Private Sub BenchmarkHandler()
testIsActive = True
loopBreak = False
benchmarkButton.Text = "Stop"
If String.IsNullOrEmpty(networkPath.Text) Then
testPath = driveLetter.Text & ":"
Else
testPath = networkPath.Text
End If
If Not loopBreak Then ' Run warmup
testFileSize = 0 ' for warmup run
testIterations = 1
testType = "write"
LaunchTestThread()
End If
testFileSize = Convert.ToUInt64(fileSize.Text) * 1000000
testIterations = Convert.ToUInt64(loops.Text)
If Not loopBreak Then ' Run write test
testType = "write"
LaunchTestThread()
End If
If Not loopBreak Then ' Run read test
testType = "read"
LaunchTestThread()
End If
testIsActive = False
loopBreak = False
benchmarkButton.Text = "Start"
End Sub
Private Sub LaunchTestThread()
workerThread = New Thread(AddressOf TestPerf)
workerThread.Start()
While Not workerThread.IsAlive ' wait for thread activation
End While
While workerThread.IsAlive ' wait for thread termination
Thread.Sleep(500)
End While
End Sub
Private Sub TestPerf()
Try
Dim firstPath As String
Dim secondPath As String
Dim shortType As String
Dim iterText As String
Dim appendIterations As ULong
Dim totalPerf As Double
Dim startTime As DateTime
Dim stopTime As DateTime
Dim randomText As String = RandomString(100000)
If testType = "read" Then
firstPath = testPath
secondPath = localStoragePath
shortType = "R"
Else
firstPath = localStoragePath
secondPath = testPath
shortType = "W"
End If
If testIterations = 1 Then
iterText = "once"
ElseIf testIterations = 2 Then
iterText = "twice"
Else
iterText = testIterations & " times"
End If
If testFileSize = 0 Then
resultArea.AppendText("Running warmup..." & vbCrLf)
resultArea.Invalidate()
appendIterations = 100 ' equals a 10MB warmup file.
Else
resultArea.AppendText( _
"Running a " & testFileSize \ 1000000 & "MB file " & testType & _
" on " & testPath & " " & iterText & "..." & vbCrLf)
appendIterations = testFileSize \ 100000
' Note: dividing integers in VB.Net can produce a decimal number,
' so the integer division operator '\' is used instead of '/'
End If
totalPerf = 0
For j As Integer = 1 To testIterations
Application.DoEvents()
If File.Exists(firstPath & "\" & j & "test.tmp") Then
File.Delete(firstPath & "\" & j & "test.tmp")
End If
If File.Exists(secondPath & "\" & j & "test.tmp") Then
File.Delete(secondPath & "\" & j & "test.tmp")
End If
If loopBreak = True Then
resultArea.AppendText("Benchmark cancelled." & vbCrLf)
resultArea.Invalidate()
Exit For
End If
Dim sWriter As New StreamWriter(firstPath & _
"\" & j & "test.tmp", True, Encoding.UTF8, 1048576)
For i As Integer = 1 To appendIterations
sWriter.Write(randomText)
Next
sWriter.Close()
startTime = DateTime.Now
File.Copy(firstPath & "\" & j & "test.tmp", _
secondPath & "\" & j & "test.tmp")
stopTime = DateTime.Now
File.Delete(firstPath & "\" & j & "test.tmp")
File.Delete(secondPath & "\" & j & "test.tmp")
Dim interval As TimeSpan = stopTime - startTime
If testIterations > 1 Then
resultArea.AppendText( _
("Iteration " & j & ":").PadRight(15) & _
((testFileSize \ 1000) / interval.TotalMilliseconds).ToString("F2").PadLeft(7) & _
" MB/sec" & vbCrLf)
resultArea.Invalidate()
End If
totalPerf += (testFileSize \ 1000) / interval.TotalMilliseconds
Next
If (testFileSize <> 0) AndAlso (loopBreak = False) Then
resultArea.AppendText("-----------------------------" & vbCrLf)
resultArea.AppendText("Average (" & shortType & "):" & _
(totalPerf / testIterations).ToString("F2").PadLeft(10) & " MB/sec" & vbCrLf)
resultArea.AppendText("-----------------------------" & vbCrLf)
resultArea.Invalidate()
End If
Catch e As Exception
resultArea.AppendText("An error occured: " & e.Message & vbCrLf)
End Try
End Sub
Private Function RandomString(size As Integer) As String
Dim builder As New StringBuilder()
Dim random As New Random()
Dim ch As Char
For i As Integer = 0 To size - 1
ch = Convert.ToChar(Convert.ToInt32(Math.Floor(26 * random.NextDouble() + 65)))
builder.Append(ch)
Next
Return builder.ToString()
End Function
Private Function GetNetworkDriveLetters() As String()
Dim NetworkDriveLetters As New System.Collections.ArrayList()
Dim allDrives As DriveInfo() = DriveInfo.GetDrives()
For Each d As DriveInfo In allDrives
If d.DriveType = DriveType.Network Then
NetworkDriveLetters.Add(d.Name.Substring(0, 1))
End If
Next
Return TryCast(NetworkDriveLetters.ToArray(GetType(String)), String())
End Function
Private Function IsFolderWritable(folderPath As String) As Boolean
Try
Using fs As FileStream = _
File.Create(Path.Combine(folderPath, _
Path.GetRandomFileName()), 1, FileOptions.DeleteOnClose)
End Using
Return True
Catch
Return False
End Try
End Function
Shared Sub Main()
Application.Run(New NasPerformanceForm())
End Sub
End Class
Tags: software
Page last updated 2024-03-24 14:36. Some rights reserved (CC by 3.0)