Skip to content

Commit 1de29ff

Browse files
authored
Merge pull request #39105 from CyrusNajmabadi/sqliteBenchmark
Add an IDE storage service benchmark
2 parents 3484227 + 5fef3b4 commit 1de29ff

File tree

2 files changed

+148
-0
lines changed

2 files changed

+148
-0
lines changed
Lines changed: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,143 @@
1+
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
2+
3+
using System;
4+
using System.Collections.Generic;
5+
using System.IO;
6+
using System.Linq;
7+
using System.Threading.Tasks;
8+
using BenchmarkDotNet.Attributes;
9+
using Microsoft.CodeAnalysis;
10+
using Microsoft.CodeAnalysis.Editor.UnitTests.Workspaces;
11+
using Microsoft.CodeAnalysis.Host;
12+
using Microsoft.CodeAnalysis.Options;
13+
using Microsoft.CodeAnalysis.SolutionSize;
14+
using Microsoft.CodeAnalysis.SQLite;
15+
using Microsoft.CodeAnalysis.Storage;
16+
using Microsoft.CodeAnalysis.Test.Utilities;
17+
18+
namespace IdeBenchmarks
19+
{
20+
public class SQLitePersistentStorageBenchmarks
21+
{
22+
private readonly UseExportProviderAttribute _useExportProviderAttribute = new UseExportProviderAttribute();
23+
24+
// Run the test with different ratios of reads/writes.
25+
[Params(0, 25, 50, 75, 100)]
26+
public int ReadPercentage { get; set; }
27+
28+
private TestWorkspace _workspace;
29+
private SQLitePersistentStorageService _storageService;
30+
private IChecksummedPersistentStorage _storage;
31+
private Document _document;
32+
private Random _random;
33+
34+
[GlobalSetup]
35+
public void GlobalSetup()
36+
{
37+
_useExportProviderAttribute.Before(null);
38+
39+
if (_workspace != null)
40+
{
41+
throw new InvalidOperationException();
42+
}
43+
44+
_workspace = TestWorkspace.Create(
45+
@"<Workspace>
46+
<Project Language=""NoCompilation"" CommonReferences=""false"">
47+
<Document>
48+
// a no-compilation document
49+
</Document>
50+
</Project>
51+
</Workspace>");
52+
53+
// Ensure we always use the storage service, no matter what the size of the solution.
54+
_workspace.Options = _workspace.Options.WithChangedOption(StorageOptions.SolutionSizeThreshold, -1);
55+
56+
_storageService = new SQLitePersistentStorageService(
57+
_workspace.Services.GetService<IOptionService>(),
58+
new LocationService(),
59+
new SolutionSizeTracker());
60+
61+
_storage = _storageService.GetStorageWorker(_workspace.CurrentSolution);
62+
if (_storage == NoOpPersistentStorage.Instance)
63+
{
64+
throw new InvalidOperationException("We didn't properly get the sqlite storage instance.");
65+
}
66+
67+
Console.WriteLine("Storage type: " + _storage.GetType());
68+
_document = _workspace.CurrentSolution.Projects.Single().Documents.Single();
69+
_random = new Random(0);
70+
}
71+
72+
[GlobalCleanup]
73+
public void GlobalCleanup()
74+
{
75+
if (_workspace == null)
76+
{
77+
throw new InvalidOperationException();
78+
}
79+
80+
_document = null;
81+
_storage.Dispose();
82+
_storage = null;
83+
_storageService = null;
84+
_workspace.Dispose();
85+
_workspace = null;
86+
87+
_useExportProviderAttribute.After(null);
88+
}
89+
90+
private static readonly byte[] s_bytes = new byte[1000];
91+
92+
[Benchmark(Baseline = true)]
93+
public Task Perf()
94+
{
95+
var tasks = new List<Task>();
96+
97+
// Create a lot of overlapping reads and writes to the DB to several different keys. The
98+
// percentage of reads and writes is parameterized above, allowing us to validate
99+
// performance with several different usage patterns.
100+
for (var i = 0; i < 1000; i++)
101+
{
102+
var name = _random.Next(0, 4).ToString();
103+
if (_random.Next(0, 100) < ReadPercentage)
104+
{
105+
tasks.Add(Task.Run(async () =>
106+
{
107+
using var stream = await _storage.ReadStreamAsync(_document, name);
108+
}));
109+
}
110+
else
111+
{
112+
tasks.Add(Task.Run(async () =>
113+
{
114+
using var stream = new MemoryStream(s_bytes);
115+
await _storage.WriteStreamAsync(_document, name, stream);
116+
}));
117+
}
118+
}
119+
120+
return Task.WhenAll(tasks);
121+
}
122+
123+
private class SolutionSizeTracker : ISolutionSizeTracker
124+
{
125+
public long GetSolutionSize(Workspace workspace, SolutionId solutionId) => 0;
126+
}
127+
128+
private class LocationService : IPersistentStorageLocationService
129+
{
130+
public event EventHandler<PersistentStorageLocationChangingEventArgs> StorageLocationChanging { add { } remove { } }
131+
132+
public bool IsSupported(Workspace workspace) => true;
133+
public string TryGetStorageLocation(SolutionId solutionId)
134+
{
135+
// Store the db in a different random temp dir.
136+
var tempDir = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName());
137+
Console.WriteLine("Creating: " + tempDir);
138+
Directory.CreateDirectory(tempDir);
139+
return tempDir;
140+
}
141+
}
142+
}
143+
}

src/Workspaces/Core/Portable/Storage/AbstractPersistentStorageService.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,11 @@ public IChecksummedPersistentStorage GetStorage(Solution solution, bool checkBra
6161
return NoOpPersistentStorage.Instance;
6262
}
6363

64+
return GetStorageWorker(solution);
65+
}
66+
67+
internal IChecksummedPersistentStorage GetStorageWorker(Solution solution)
68+
{
6469
lock (_lock)
6570
{
6671
// Do we already have storage for this?

0 commit comments

Comments
 (0)