diff --git a/.gitignore b/.gitignore
index c20b14d2..a02a88d7 100644
--- a/.gitignore
+++ b/.gitignore
@@ -3,7 +3,7 @@
################################################################################
obj/
/.vs/OpenMcdf/v14
-/bin/Debug
+bin/
/TestResults
/sources/Test/OpenMcdf.Test/bin/Debug
/sources/Test/OpenMcdf.PerfTest/obj/Debug
@@ -27,7 +27,7 @@ obj/
/Memory Test/bin/Debug
/bin/Release/StructuredStorageXplorer
/bin/Release/OpenMcdf.Extensions
-/bin/Release
+/bin/Release/
/sources/Html Help/Help
/.vs
# User-specific files
diff --git a/OpenMcdf.sln b/OpenMcdf.sln
index 12e58e8b..ab4ab2ab 100644
--- a/OpenMcdf.sln
+++ b/OpenMcdf.sln
@@ -52,6 +52,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OpenMcdf.PerfTest", "source
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OpenMcdf.Test", "sources\Test\OpenMcdf.Test\OpenMcdf.Test.csproj", "{FD339266-8842-40B4-9230-F8E84FC42AC2}"
EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OpenMcdf.Benchmark", "sources\Test\OpenMcdf.Benchmark\OpenMcdf.Benchmark.csproj", "{B3645D34-1E22-4BCC-8956-A8A56FA9F114}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -86,6 +88,10 @@ Global
{FD339266-8842-40B4-9230-F8E84FC42AC2}.Debug|Any CPU.Build.0 = Debug|Any CPU
{FD339266-8842-40B4-9230-F8E84FC42AC2}.Release|Any CPU.ActiveCfg = Release|Any CPU
{FD339266-8842-40B4-9230-F8E84FC42AC2}.Release|Any CPU.Build.0 = Release|Any CPU
+ {B3645D34-1E22-4BCC-8956-A8A56FA9F114}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {B3645D34-1E22-4BCC-8956-A8A56FA9F114}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {B3645D34-1E22-4BCC-8956-A8A56FA9F114}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {B3645D34-1E22-4BCC-8956-A8A56FA9F114}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -96,6 +102,7 @@ Global
{E2BAD82D-3040-462B-BAA2-6E608A9054F4} = {73814657-FC73-4066-AABD-86062F2A132E}
{7077508F-B313-4DF6-8855-4764911BE161} = {73814657-FC73-4066-AABD-86062F2A132E}
{FD339266-8842-40B4-9230-F8E84FC42AC2} = {73814657-FC73-4066-AABD-86062F2A132E}
+ {B3645D34-1E22-4BCC-8956-A8A56FA9F114} = {73814657-FC73-4066-AABD-86062F2A132E}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {DEAA45F3-54F5-4F50-8402-9D89EB95A04C}
diff --git a/OpenMcdf.sln.DotSettings b/OpenMcdf.sln.DotSettings
new file mode 100644
index 00000000..43677b3c
--- /dev/null
+++ b/OpenMcdf.sln.DotSettings
@@ -0,0 +1,2 @@
+
+ True
\ No newline at end of file
diff --git a/sources/Test/OpenMcdf.Benchmark/InMemory.cs b/sources/Test/OpenMcdf.Benchmark/InMemory.cs
new file mode 100644
index 00000000..128b5ac0
--- /dev/null
+++ b/sources/Test/OpenMcdf.Benchmark/InMemory.cs
@@ -0,0 +1,100 @@
+using System;
+using System.IO;
+using BenchmarkDotNet.Attributes;
+using BenchmarkDotNet.Order;
+
+namespace OpenMcdf.Benchmark
+{
+ [CoreJob]
+ [CsvExporter]
+ [HtmlExporter]
+ [MarkdownExporter]
+ //[DryCoreJob] // I always forget this attribute, so please leave it commented out
+ [MemoryDiagnoser]
+ [Orderer(SummaryOrderPolicy.FastestToSlowest)]
+ public class InMemory : IDisposable
+ {
+ private const int Kb = 1024;
+ private const int Mb = Kb * Kb;
+ private const string storageName = "MyStorage";
+ private const string streamName = "MyStream";
+
+ private byte[] _readBuffer;
+
+ private Stream _stream;
+
+ [Params(Kb / 2, Kb, 4 * Kb, 128 * Kb, 256 * Kb, 512 * Kb, Kb * Kb)]
+ public int BufferSize { get; set; }
+
+ [Params(Mb /*, 8 * Mb, 64 * Mb, 128 * Mb*/)]
+ public int TotalStreamSize { get; set; }
+
+ public void Dispose()
+ {
+ _stream?.Dispose();
+ }
+
+ [GlobalSetup]
+ public void GlobalSetup()
+ {
+ _stream = new MemoryStream();
+ _readBuffer = new byte[BufferSize];
+ CreateFile(1);
+ }
+
+ [GlobalCleanup]
+ public void GlobalCleanup()
+ {
+ _stream.Dispose();
+ _stream = null;
+ _readBuffer = null;
+ }
+
+
+ [Benchmark]
+ public void Test()
+ {
+ //
+ _stream.Seek(0L, SeekOrigin.Begin);
+ //
+ var compoundFile = new CompoundFile(_stream);
+ var cfStream = compoundFile.RootStorage
+ .GetStorage(storageName)
+ .GetStream(streamName + 0);
+ var streamSize = cfStream.Size;
+ var position = 0L;
+ while (true)
+ {
+ if (position >= streamSize) break;
+ var read = cfStream
+ .Read(_readBuffer, position, _readBuffer.Length);
+ position += read;
+ if (read <= 0) break;
+ }
+
+ //compoundFile.Close();
+ }
+
+ private void CreateFile(int streamCount)
+ {
+ var iterationCount = TotalStreamSize / BufferSize;
+
+ var buffer = new byte[BufferSize];
+ Array.Fill(buffer, byte.MaxValue);
+ const CFSConfiguration flags = CFSConfiguration.Default | CFSConfiguration.LeaveOpen;
+ using (var compoundFile = new CompoundFile(CFSVersion.Ver_4, flags))
+ {
+ var st = compoundFile.RootStorage.AddStorage(storageName);
+ for (var streamId = 0; streamId < streamCount; ++streamId)
+ {
+ var sm = st.AddStream(streamName + streamId);
+
+ for (var iteration = 0; iteration < iterationCount; ++iteration) sm.Append(buffer);
+ }
+
+ compoundFile.Save(_stream);
+ compoundFile.Close();
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/sources/Test/OpenMcdf.Benchmark/OpenMcdf.Benchmark.csproj b/sources/Test/OpenMcdf.Benchmark/OpenMcdf.Benchmark.csproj
new file mode 100644
index 00000000..2f8767e7
--- /dev/null
+++ b/sources/Test/OpenMcdf.Benchmark/OpenMcdf.Benchmark.csproj
@@ -0,0 +1,16 @@
+
+
+
+ Exe
+ netcoreapp2.2
+
+
+
+
+
+
+
+
+
+
+
diff --git a/sources/Test/OpenMcdf.Benchmark/Program.cs b/sources/Test/OpenMcdf.Benchmark/Program.cs
new file mode 100644
index 00000000..f441ba1d
--- /dev/null
+++ b/sources/Test/OpenMcdf.Benchmark/Program.cs
@@ -0,0 +1,12 @@
+using BenchmarkDotNet.Running;
+
+namespace OpenMcdf.Benchmark
+{
+ internal class Program
+ {
+ private static void Main(string[] args)
+ {
+ var summary = BenchmarkRunner.Run();
+ }
+ }
+}
\ No newline at end of file