Skip to content

Commit 6e53912

Browse files
committed
Code analysis fixes
1 parent b10302b commit 6e53912

12 files changed

+70
-48
lines changed

.editorconfig

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
[*.cs]
2+
3+
# IDE0004: Remove Unnecessary Cast
4+
dotnet_diagnostic.IDE0004.severity = warning

AllProjects.sln

+1
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ EndProject
1313
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{E866D3C8-0885-4C79-BA12-9DE39C922925}"
1414
ProjectSection(SolutionItems) = preProject
1515
.codecov.yml = .codecov.yml
16+
.editorconfig = .editorconfig
1617
appveyor.yml = appveyor.yml
1718
LICENSE = LICENSE
1819
README.md = README.md

Portent.Benchmark/DawgBenchmark.cs

+5-2
Original file line numberDiff line numberDiff line change
@@ -84,8 +84,11 @@ public static Dawg CreateDictionary(string corpusPath, string savePath)
8484
}
8585
}
8686

87-
var compressedGraph = builder.AsCompressedSparseRows();
88-
compressedGraph.Save(savePath);
87+
using (var compressedGraph = builder.AsCompressedSparseRows())
88+
{
89+
compressedGraph.Save(savePath);
90+
}
91+
8992
using var dawgStream = File.OpenRead(savePath);
9093
return new Dawg(dawgStream);
9194
}

Portent.Benchmark/Portent.Benchmark.csproj

+1-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
<PlatformTarget>x64</PlatformTarget>
88
<NullableContextOptions>enable</NullableContextOptions>
99
<Nullable>enable</Nullable>
10-
<DefineConstants>DEBUG;TRACE;BIT64</DefineConstants>
10+
<DefineConstants>DEBUG;TRACE;</DefineConstants>
1111
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
1212
</PropertyGroup>
1313

Portent.Benchmark/Program.cs

+2-6
Original file line numberDiff line numberDiff line change
@@ -11,21 +11,18 @@ public static void Main()
1111
{
1212
if (Debugger.IsAttached)
1313
{
14-
RunForProfiler();
14+
PrintResultCounts();
1515
}
1616
else
1717
{
1818
BenchmarkRunner.Run<DawgBenchmark>();
1919
}
2020
}
2121

22-
private static void RunForProfiler()
22+
private static void PrintResultCounts()
2323
{
24-
Console.WriteLine("reading");
2524
using var benchmark = new DawgBenchmark();
26-
Console.WriteLine("setup for run");
2725
benchmark.SetupForRun();
28-
Console.WriteLine("verify correctness");
2926
if (!benchmark.VerifyDawgCorrectness())
3027
{
3128
throw new InvalidOperationException();
@@ -40,7 +37,6 @@ private static void RunForProfiler()
4037
Console.WriteLine(benchmark.GetTotalResults());
4138
}
4239

43-
Console.WriteLine("Done, press {ENTER} to continue...");
4440
Console.ReadLine();
4541
}
4642
}

Portent/Allocation/Native/PrivilegeHolder.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
2929
using System.Runtime.CompilerServices;
3030
using System.Runtime.InteropServices;
3131
using System.Threading;
32-
32+
#pragma warning disable S3453 // Classes should not have only "private" constructors
3333
namespace Portent
3434
{
3535
/// <summary>

Portent/Collections/SuggestItemCollection.cs

+7-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77

88
namespace Portent
99
{
10-
internal sealed class SuggestItemCollection : IEnumerable<SuggestItem>
10+
internal sealed class SuggestItemCollection : IEnumerable<SuggestItem>, IDisposable
1111
{
1212
public SuggestItemCollection(int count)
1313
{
@@ -123,5 +123,11 @@ public void Dispose()
123123
//Empty - Required by IEnumerator<T>, but nothing to dispose.
124124
}
125125
}
126+
127+
public void Dispose()
128+
{
129+
// So useless
130+
_myEnumerator.Dispose();
131+
}
126132
}
127133
}

Portent/Dawg.cs

+29-19
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
using System;
22
using System.Collections.Generic;
3+
using System.Diagnostics.CodeAnalysis;
34
using System.Diagnostics.Contracts;
45
using System.IO;
56
using System.Linq;
@@ -14,16 +15,11 @@
1415

1516
// ReSharper disable BuiltInTypeReferenceStyle
1617

17-
#if BIT64
1818
// Use nint when referring to pointer values, long when referring to 64 bit values.
1919
using nint = System.Int64;
2020
using nuint = System.UInt64;
2121
// ReSharper disable SuggestVarOrType_BuiltInTypes
2222
// ReSharper disable SuggestVarOrType_Elsewhere
23-
#else
24-
using nint = System.Int32;
25-
using nuint = System.UInt32;
26-
#endif
2723

2824
namespace Portent
2925
{
@@ -48,9 +44,11 @@ public IEnumerable<SuggestItem> Lookup(in string word, uint maxEdits)
4844
_compoundResultCollection.Clear();
4945
var wordLength = (uint) word.Length;
5046

51-
// TODO: Align these allocated chunks together.
47+
#pragma warning disable S1135 // Track uses of "TODO" tags
48+
// TODO: Align these allocated chunks together.
5249
// + 1 for the (char)0 at the start.
5350
var inputLength = MemoryAlignmentHelper.GetCacheAlignedSize<char>(wordLength + 1);
51+
#pragma warning restore S1135 // Track uses of "TODO" tags
5452
var inputBytes = stackalloc byte[inputLength];
5553
var input = MemoryAlignmentHelper.GetCacheAlignedStart<char>(inputBytes);
5654
*input = (char) 0;
@@ -81,7 +79,7 @@ public IEnumerable<SuggestItem> Lookup(in string word, uint maxEdits)
8179
var run = stackalloc Run[1];
8280
run[0] = new Run(input, toCache, wordLength, maxPlusOne);
8381
// TODO: There's got to be a simpler way of doing this. Not a high priority though.
84-
var hasRun = stackalloc bool[(int) rootLast - (int) rootFirst];
82+
var hasRun = stackalloc bool[(int)(rootLast - rootFirst)];
8583
for (nint i = 0; i < rootLast - rootFirst; i++)
8684
{
8785
hasRun[i] = false;
@@ -131,6 +129,7 @@ public IEnumerable<SuggestItem> Lookup(in string word, uint maxEdits)
131129

132130
[LocalsInit(false)]
133131
[MethodImpl(MethodImplOptions.AggressiveOptimization)]
132+
[SuppressMessage("Major Code Smell", "S907:\"goto\" statement should not be used")]
134133
private static void Search5(Job me)
135134
{
136135
Run* run = me._run;
@@ -159,7 +158,6 @@ private static void Search5(Job me)
159158

160159
var allBytes = stackalloc byte[(int)bytesRequiredTotal];
161160

162-
//var builderAlloc = stackalloc byte[builderByteLength];
163161
var builderStart = MemoryAlignmentHelper.GetCacheAlignedStart<char>(allBytes);
164162
*builderStart = (char)0;
165163
builderStart++;
@@ -179,7 +177,7 @@ private static void Search5(Job me)
179177
// Row 1 is the first character and so on...
180178
// And we sneak in an extra row before row0, to ensure that the transposition row offset has room.
181179
closureArg._transpositionRow = (uint*) 0;
182-
closureArg._row0 = editMatrix;// + twiceMaxPlusOne;
180+
closureArg._row0 = editMatrix;
183181
editMatrix--;
184182
nint x = 0;
185183
nint diagonal = 0;
@@ -197,6 +195,7 @@ static void MatchCharacter(ref Search3Closure closure, nuint skip)
197195
nuint builderDepth = closure._builderDepth;
198196
uint* previousRow = closure._row0;
199197
uint mp1 = closure._maxPlusOne;
198+
200199
// This is the value for the column directly before our diagonal stripe.
201200
// For the early rows, it's the 0'th column. After that, the value is > maxPlusOne anyways.
202201
// TODO: Potentially a place to cut down on an addition or two, if it fits in the branching done above.
@@ -209,8 +208,8 @@ static void MatchCharacter(ref Search3Closure closure, nuint skip)
209208
char* firstWithOffset = closure._first;
210209
uint to = closure._wordLength;
211210

212-
//char previousWordCharacter;
213211
uint previousRowPreviousColumn;
212+
214213
// Very predictable branching
215214
if (wordArrayOffset > 0)
216215
{
@@ -224,7 +223,7 @@ static void MatchCharacter(ref Search3Closure closure, nuint skip)
224223
}
225224
else
226225
{
227-
//TODO: I could move this out of the if-else and make the if do ++ instead of +=. Does this affect latency?
226+
//TODO: Test alternative: move this outside of the conditional, and change the previous addition to +1?
228227
transpositionRow++;
229228
}
230229

@@ -240,6 +239,7 @@ static void MatchCharacter(ref Search3Closure closure, nuint skip)
240239
// I tried to arrange these so that the order of operations leaves them as far away as possible from ops affecting their values.
241240
// To reduce latency.
242241
closure._transpositionRow = transpositionRow;
242+
243243
// TODO: is this one predictable?
244244
if (t2 < to)
245245
{
@@ -250,7 +250,6 @@ static void MatchCharacter(ref Search3Closure closure, nuint skip)
250250

251251
// Save a read by combining both characters into one register. Used ulong because comparison is best done between two int32
252252
// Shift the previous-previous character into the high bits, putting the previous character into the bottom 2.
253-
// 00ab -> //b00a | so (uint)edgeCharacter == 0a, (uint) (edgeCharacter >> 48) == 0b
254253
// Make sure it's unsigned so that >> 48 will shift 0's
255254
ulong edgeCharacter = MathUtils.RotateRight((ulong) *(uint*) (closure._builderStart + builderDepth - 1), 16);
256255

@@ -260,16 +259,12 @@ static void MatchCharacter(ref Search3Closure closure, nuint skip)
260259
do
261260
{
262261
uint previousRowCurrentColumn = previousRow[j];
263-
264262
ulong wordCharacter = MathUtils.RotateRight((ulong) *(uint*) (firstWithOffset + j - 1), 16);
265-
//uint wordCharacter = *(uint*) (firstWithOffset + j - 1);
266-
267263
if ((uint) edgeCharacter != (uint) wordCharacter)
268264
{
269265
// Non-branching Min() call here because it's not predictable.
270266
currentRowPreviousColumn = MathUtils.Min(currentRowPreviousColumn, previousRowCurrentColumn);
271267
uint diagonalEntry;
272-
//if ((uint) edgeCharacter != (uint) (wordCharacter >> 48) || (uint) (edgeCharacter >> 48) != (uint) wordCharacter)
273268
// ReSharper disable once ConvertIfStatementToConditionalTernaryExpression
274269
if (edgeCharacter != ((wordCharacter >> 48) | (wordCharacter << 48)))
275270
{
@@ -291,13 +286,14 @@ static void MatchCharacter(ref Search3Closure closure, nuint skip)
291286
}
292287

293288
currentRow[j] = currentRowPreviousColumn;
294-
// Doing this here removes a pause between ++ and < in (++j < to)
289+
290+
// Doing this here to reduce latency between the add and the conditional
295291
++j;
296292

297293
// Will make `any` negative if currentRowPreviousColumn < maxErrors
298294
any |= (int) currentRowPreviousColumn - (int) maxPlusOne;
299295
previousRowPreviousColumn = previousRowCurrentColumn;
300-
} while ((uint)j < to);
296+
} while ((uint) j < to);
301297

302298
if (any >= 0)
303299
{
@@ -315,6 +311,7 @@ static void MatchCharacter(ref Search3Closure closure, nuint skip)
315311
string stringResult = new string(bs, 0, (int)newDepth);
316312
ulong count = GetWordCount(bs, newDepth, closure._graph);
317313
closure._results.Add(stringResult, count);
314+
318315
// If we followed this branch, the register was overwritten.
319316
// Fetch the value from the correct place instead of having the compiler store it on the stack.
320317
maxPlusOne = closure._maxPlusOne;
@@ -376,7 +373,9 @@ static void MatchCharacter(ref Search3Closure closure, nuint skip)
376373
ulong temp = *(ulong*) (closure._graph._firstChildEdgeIndex + (uint) closure._node);
377374
// This one is used an a pointer offset, so it can be nuint. Also, taking only the lower 32 bits.
378375
// ReSharper disable once RedundantCast - Just being explicit and clear
376+
#pragma warning disable IDE0004, S1905 // Remove Unnecessary Cast
379377
nuint childEdge = (nuint) (uint) temp;
378+
#pragma warning restore IDE0004, S1905 // Remove Unnecessary Cast
380379
// Upper 32 bits. Not used as an offset, so keep as uint.
381380
uint childEdgeEnd = (uint) (temp >> 32);
382381

@@ -386,7 +385,6 @@ static void MatchCharacter(ref Search3Closure closure, nuint skip)
386385
}
387386

388387
// TODO: We have some extra registers to store stuff in, and they're getting push-popped anyways. Load things and prevent re-reading from memory.
389-
// TODO: make a BIT64 distinction. We don't have all those extra registers in 32 bit mode.
390388
uint* row0 = closure._row0;
391389
closure._builderDepth = newDepth;
392390
// casting to byte* because otherwise it was doing both shl 1 and shl 2 to double _maxPlusOne and then convert it to pointer moves
@@ -416,6 +414,7 @@ static void MatchCharacter(ref Search3Closure closure, nuint skip)
416414
MatchCharacter(ref closureArg, 0);
417415
}
418416

417+
#pragma warning disable S3898 //Value types should implement "IEquatable<T>"
419418
private readonly ref struct Run
420419
{
421420
public readonly char* _input;
@@ -431,6 +430,7 @@ public Run(char* input, uint* toCache, uint wordLength, uint maxPlusOne)
431430
_maxPlusOne = maxPlusOne;
432431
}
433432
}
433+
#pragma warning restore S3898 //Value types should implement "IEquatable<T>"
434434

435435
private class Job
436436
{
@@ -458,6 +458,7 @@ private void SearchPrivate()
458458
}
459459

460460
[StructLayout(LayoutKind.Explicit)]
461+
#pragma warning disable S3898 //Value types should implement "IEquatable<T>"
461462
private readonly struct DawgGraph
462463
{
463464
[FieldOffset(0x00)] //48
@@ -488,6 +489,7 @@ public DawgGraph(ushort* reachableTerminalNodes, ulong* wordCounts, uint* firstC
488489
_rootNodeIndex = rootNodeIndex;
489490
}
490491
}
492+
#pragma warning restore S3898 //Value types should implement "IEquatable<T>"
491493

492494
[Pure]
493495
[MethodImpl(MethodImplOptions.AggressiveOptimization)]
@@ -507,12 +509,16 @@ private static ulong GetWordCount(char* word, nuint length, in DawgGraph dawg)
507509
char target = *word;
508510
word++;
509511
// ReSharper disable once RedundantCast - Just being explicit and clear
512+
#pragma warning disable IDE0004 // Remove Unnecessary Cast
510513
nuint i = (nuint) edgeChildIndex[currentNode];
514+
#pragma warning restore IDE0004 // Remove Unnecessary Cast
511515
uint lastChildIndex = edgeChildIndex[currentNode+1];
512516
do
513517
{
514518
// ReSharper disable once RedundantCast - Just being explicit and clear
519+
#pragma warning disable IDE0004, S1905 // Remove Unnecessary Cast
515520
currentNode = (nint)edgeNodeIndex[i];
521+
#pragma warning restore IDE0004, S1905 // Remove Unnecessary Cast
516522
char currentEdgeChar = characters[i];
517523
i++;
518524
if (currentEdgeChar != target)
@@ -541,6 +547,7 @@ private static ulong GetWordCount(char* word, nuint length, in DawgGraph dawg)
541547
}
542548

543549
[StructLayout(LayoutKind.Explicit)]
550+
#pragma warning disable S3898 //Value types should implement "IEquatable<T>"
544551
private ref struct Search3Closure
545552
{
546553
#region CacheLine1
@@ -582,6 +589,7 @@ private ref struct Search3Closure
582589
public SuggestItemCollection _results;
583590
#endregion CacheLine3
584591
}
592+
#pragma warning restore S3898 //Value types should implement "IEquatable<T>"
585593

586594
public uint WordCount { get; }
587595

@@ -695,7 +703,9 @@ public int GetIndex(in string word)
695703
#pragma warning restore S907 // "goto" statement should not be used
696704
}
697705

706+
#pragma warning disable S907 // "goto" statement should not be used
698707
goto singleReturnPoint;
708+
#pragma warning restore S907 // "goto" statement should not be used
699709

700710
nextIteration:
701711
#pragma warning disable S1116 // Empty statements should be removed - This statement is important. It allows the label/goto to work.

Portent/Graph/CompressedSparseRowGraph.cs

+9-7
Original file line numberDiff line numberDiff line change
@@ -39,17 +39,11 @@ public CompressedSparseRowPointerGraph(Stream stream)
3939
EdgeToNodeIndex = MemoryChunk.GetArrayAligned<int>(edgeToNodeIndexCount);
4040
stream.ReadCompressed(EdgeToNodeIndex, edgeToNodeIndexCount);
4141

42-
GCSettings.LargeObjectHeapCompactionMode = GCLargeObjectHeapCompactionMode.CompactOnce;
43-
GC.Collect();
44-
4542
var byteLength = stream.ReadCompressedUInt();
4643
var charLength = stream.ReadCompressedUInt();
4744
EdgeCharacter = MemoryChunk.GetArrayAligned<char>(charLength);
4845
stream.ReadUtf8(EdgeCharacter, byteLength, charLength);
4946

50-
GCSettings.LargeObjectHeapCompactionMode = GCLargeObjectHeapCompactionMode.CompactOnce;
51-
GC.Collect();
52-
5347
var reachableTerminalNodesCount = stream.ReadCompressedUInt();
5448
ReachableTerminalNodes = MemoryChunk.GetArrayAligned<ushort>(reachableTerminalNodesCount);
5549
stream.ReadCompressed(ReachableTerminalNodes, reachableTerminalNodesCount);
@@ -62,7 +56,7 @@ public CompressedSparseRowPointerGraph(Stream stream)
6256
}
6357
}
6458

65-
public sealed class CompressedSparseRowGraph
59+
public sealed class CompressedSparseRowGraph : IDisposable
6660
{
6761
internal CompressedSparseRowGraph(int rootNodeIndex, uint[] firstChildEdgeIndex, int[] edgeToNodeIndex, char[] edgeCharacter, ushort[] reachableTerminalNodes, ulong[] data, Dictionary<string, ulong> wordCounts)
6862
: this(rootNodeIndex, firstChildEdgeIndex, edgeToNodeIndex, edgeCharacter, reachableTerminalNodes, data)
@@ -150,5 +144,13 @@ public void Save(string path)
150144
stream.WriteCompressed(ReachableTerminalNodes);
151145
stream.WriteCompressed(WordCounts);
152146
}
147+
148+
public void Dispose()
149+
{
150+
GCSettings.LargeObjectHeapCompactionMode = GCLargeObjectHeapCompactionMode.CompactOnce;
151+
#pragma warning disable S1215 // "GC.Collect" should not be called
152+
GC.Collect();
153+
#pragma warning restore S1215 // "GC.Collect" should not be called
154+
}
153155
}
154156
}

0 commit comments

Comments
 (0)