Skip to content

Commit 40a7897

Browse files
committed
Small performance updates for NVMe SSDs
- Added option to change file scan parallelization to actually making better use of SSD's nature of random access. - Fixed typo and some more Launcher settings explanations.
1 parent 19a8729 commit 40a7897

16 files changed

+260
-92
lines changed

LauncherCore/Classes/ConfigurationFile.cs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -192,6 +192,20 @@ public int DownloaderConcurrentCount
192192
set => this.Set("pso2_downloaderconcurrentcount", value);
193193
}
194194

195+
/// <summary>The number of threads used for file scanning, </summary>
196+
public int FileScannerConcurrentCount
197+
{
198+
get
199+
{
200+
if (this.TryGetRaw("pso2_filescanconcurrentlevel", out var val) && val.ValueKind == System.Text.Json.JsonValueKind.Number)
201+
{
202+
return Math.Clamp((int)val.Value, 1, 16);
203+
}
204+
return 1;
205+
}
206+
set => this.Set("pso2_filescanconcurrentlevel", value);
207+
}
208+
195209
/// <summary>
196210
/// Simple throttle (file per second) way. Inaccurate timer but it works close enough and the off-value ain't much.
197211
/// </summary>

LauncherCore/Classes/PSO2/GameClientUpdater.Downloader.cs

Lines changed: 48 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,24 +2,63 @@
22
using Leayal.Shared;
33
using System;
44
using System.Collections.Concurrent;
5+
using System.Diagnostics.CodeAnalysis;
56
using System.IO;
67
using System.Runtime.CompilerServices;
78
using System.Runtime.InteropServices;
89
using System.Threading;
910
using System.Threading.Tasks;
10-
using System.Xaml.Schema;
1111

1212
namespace Leayal.PSO2Launcher.Core.Classes.PSO2
1313
{
1414
partial class GameClientUpdater
1515
{
16+
/// <summary>
17+
///
18+
/// </summary>
19+
/// <param name="collections"></param>
20+
/// <param name="item">A <seealso cref="DownloadItem"/> if an item is taken from any collection, otherwise <see langword="null"/></param>
21+
/// <param name="cancellationToken"></param>
22+
/// <returns><see langword="true"/> if any of the <paramref name="collections"/> are still not completed, or <see langword="false"/> if all the collections are completed and will never be able to yield any items.</returns>
23+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
24+
static bool TryTakeFromAnyBlockingCollection(BlockingCollection<DownloadItem>[] collections, out DownloadItem? item, CancellationToken cancellationToken)
25+
{
26+
// Because BlockingCollection<T>.TryTakeFromAny is still not clear enough for me.
27+
int completedCount = 0, collectionCount = collections.Length;
28+
for (int i = 0; i < collectionCount; i++)
29+
{
30+
var current = collections[i];
31+
if (current == null)
32+
{
33+
completedCount++;
34+
}
35+
else
36+
{
37+
var isItemTaken = current.TryTake(out item, 0, cancellationToken);
38+
if (isItemTaken)
39+
{
40+
return true;
41+
}
42+
else
43+
{
44+
item = null;
45+
if (current.IsCompleted) completedCount++;
46+
}
47+
}
48+
}
49+
50+
item = null;
51+
return (completedCount != collectionCount);
52+
}
53+
1654
[MethodImpl(MethodImplOptions.AggressiveOptimization)]
17-
private async Task InnerDownloadSingleFile(int id, BlockingCollection<DownloadItem> pendingFiles, IFileCheckHashCache duhB, DownloadFinishCallback onFinished, CancellationToken cancellationToken)
55+
private async Task InnerDownloadSingleFile(int id, BlockingCollection<DownloadItem>[] pendingFiles, IFileCheckHashCache duhB, DownloadFinishCallback onFinished, CancellationToken cancellationToken)
1856
{
1957
// var downloadbuffer = new byte[4096];
2058
// var downloadbuffer = new byte[1024 * 1024]; // Increase buffer size to 1MB due to async's overhead.
2159
byte[] downloadbuffer;
22-
var amISnail = this.SnailMode;
60+
// var amISnail = this.SnailMode;
61+
const bool amISnail = false; // For not, let's not doing anything of this.
2362
int chunkCount;
2463
if (amISnail)
2564
{
@@ -38,12 +77,14 @@ private async Task InnerDownloadSingleFile(int id, BlockingCollection<DownloadIt
3877
}
3978
try
4079
{
80+
4181
using (var md5engine = System.Security.Cryptography.IncrementalHash.CreateHash(System.Security.Cryptography.HashAlgorithmName.MD5))
4282
{
43-
// GetConsumingEnumerable() blocks the thread. No good now.
44-
while (!pendingFiles.IsCompleted && !cancellationToken.IsCancellationRequested)
83+
while (!cancellationToken.IsCancellationRequested)
4584
{
46-
if (pendingFiles.TryTake(out var downloadItem))
85+
// GetConsumingEnumerable() blocks the thread. No good now.
86+
var isAllCompleted = !TryTakeFromAnyBlockingCollection(pendingFiles, out var downloadItem, cancellationToken);
87+
if (downloadItem != null)
4788
{
4889
if (cancellationToken.IsCancellationRequested)
4990
{
@@ -306,7 +347,7 @@ private async Task InnerDownloadSingleFile(int id, BlockingCollection<DownloadIt
306347
}
307348
else
308349
{
309-
if (pendingFiles.IsAddingCompleted)
350+
if (isAllCompleted)
310351
{
311352
// Exit loop because the collection is marked as completed adding (can no longer add item into the queue).
312353
// So if we can't dequeue an item, that means there's no more work to do **for this Downloader Task**, hence stop the loop and complete this Task.

LauncherCore/Classes/PSO2/GameClientUpdater.FileScan.cs

Lines changed: 10 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -274,7 +274,7 @@ static void TryDelEmptyBackupFolder(string path)
274274
}
275275

276276
[MethodImpl(MethodImplOptions.AggressiveOptimization)]
277-
private async Task InnerScanForFilesNeedToDownload(BlockingCollection<DownloadItem> pendingFiles, string dir_pso2bin, string? dir_classic_data, PSO2TweakerHashCache? tweakerHashCache, GameClientSelection selection, FileScanFlags fScanReboot, FileScanFlags fScanClassic, IFileCheckHashCache duhB, PatchListBase headacheMatterAgain, InnerDownloadQueueAddCallback onDownloadQueueAdd, CancellationToken cancellationToken)
277+
private async Task InnerScanForFilesNeedToDownload(BlockingCollection<DownloadItem> pendingFiles, UglyWrapper_Obj_ScannerProgress scannerProgress, string dir_pso2bin, string? dir_classic_data, PSO2TweakerHashCache? tweakerHashCache, GameClientSelection selection, FileScanFlags fScanReboot, FileScanFlags fScanClassic, IFileCheckHashCache duhB, IReadOnlyCollection<PatchListItem> headacheMatterAgain, InnerDownloadQueueAddCallback onDownloadQueueAdd, CancellationToken cancellationToken)
278278
{
279279
if (fScanClassic == FileScanFlags.None)
280280
{
@@ -296,37 +296,6 @@ private async Task InnerScanForFilesNeedToDownload(BlockingCollection<DownloadIt
296296
}
297297

298298
// var t_alwaysList = this.webclient.GetPatchListAlwaysAsync(patchInfoRoot, cancellationToken);
299-
var tasksOfLists = new List<Task<PatchListMemory>>(4);
300-
// Begin file check
301-
302-
/*
303-
static long GetFileSize(ref FileStream fs, string filename)
304-
{
305-
if (fs == null)
306-
{
307-
fs = OpenToScan(filename);
308-
}
309-
return fs.Length;
310-
}
311-
312-
static Task<string> GetFileMD5(ref FileStream fs, MD5 hashal, string filename, in CancellationToken cancellationToken)
313-
{
314-
if (fs == null)
315-
{
316-
fs = OpenToScan(filename);
317-
}
318-
hashal.Initialize();
319-
return ___GetFileMD5(fs, hashal, cancellationToken);
320-
}
321-
322-
static async Task<string> ___GetFileMD5(FileStream fs, MD5 hashal, CancellationToken cancellationToken)
323-
{
324-
var buffer = await hashal.ComputeHashAsync(fs, cancellationToken);
325-
return Convert.ToHexString(buffer);
326-
}
327-
*/
328-
329-
int processedFiles = 0;
330299

331300
[MethodImpl(MethodImplOptions.AggressiveInlining)]
332301
static bool IsHashTableFile(PatchListItem item) => (MemoryExtensions.Equals(item.GetSpanFilenameWithoutAffix(), __Filename_HashTableRelativePath, StringComparison.OrdinalIgnoreCase) || MemoryExtensions.Equals(item.GetSpanFilenameWithoutAffix(), __Filename_HashTableRelativePath.AsSpan(11), StringComparison.OrdinalIgnoreCase));
@@ -401,7 +370,7 @@ static async Task<ReadOnlyMemory<byte>> Md5ComputeHash(IncrementalHash engine, F
401370
string hashBuffer = new string(char.MinValue, 32);
402371
try
403372
{
404-
foreach (var patchItem in headacheMatterAgain)
373+
foreach (var patchItem in ((headacheMatterAgain is BlockingCollection<PatchListItem> syncCollection) ? syncCollection.GetConsumingEnumerable() : headacheMatterAgain))
405374
{
406375
if (cancellationToken.IsCancellationRequested)
407376
{
@@ -479,7 +448,9 @@ static async Task<ReadOnlyMemory<byte>> Md5ComputeHash(IncrementalHash engine, F
479448
}
480449
}
481450
}
482-
this.OnFileCheckReport(Interlocked.Increment(ref processedFiles));
451+
452+
scannerProgress.IncreaseProgressByOne();
453+
// this.OnFileCheckReport(Interlocked.Increment(ref processedFiles));
483454

484455
if (throttleWaiter != null)
485456
{
@@ -525,7 +496,8 @@ static async Task<ReadOnlyMemory<byte>> Md5ComputeHash(IncrementalHash engine, F
525496
AddItemToQueue(pendingFiles, onDownloadQueueAdd, patchItem, localFilePath, dir_pso2bin, dir_classic_data, cancellationToken);
526497
}
527498

528-
this.OnFileCheckReport(Interlocked.Increment(ref processedFiles));
499+
scannerProgress.IncreaseProgressByOne();
500+
// this.OnFileCheckReport(Interlocked.Increment(ref processedFiles));
529501

530502
if (throttleWaiter != null)
531503
{
@@ -633,7 +605,9 @@ static async Task<ReadOnlyMemory<byte>> Md5ComputeHash(IncrementalHash engine, F
633605
fs?.Dispose();
634606
}
635607
}
636-
this.OnFileCheckReport(Interlocked.Increment(ref processedFiles));
608+
609+
scannerProgress.IncreaseProgressByOne();
610+
// this.OnFileCheckReport(Interlocked.Increment(ref processedFiles));
637611

638612
if (throttleWaiter != null)
639613
{

0 commit comments

Comments
 (0)