performance optimizations

This commit is contained in:
Stanley Dimant
2022-09-26 00:16:05 +02:00
parent afb1cf1429
commit a75ff627b8
8 changed files with 45 additions and 104 deletions

View File

@@ -57,26 +57,12 @@ namespace MareSynchronos
public Dictionary<string, string> ClientSecret { get; set; } = new(); public Dictionary<string, string> ClientSecret { get; set; } = new();
public Dictionary<string, string> CustomServerList { get; set; } = new(); public Dictionary<string, string> CustomServerList { get; set; } = new();
public int MaxLocalCacheInGiB { get; set; } = 20; public int MaxLocalCacheInGiB { get; set; } = 20;
public bool ReverseUserSort { get; set; } = true; public bool ReverseUserSort { get; set; } = true;
public int TimeSpanBetweenScansInSeconds { get; set; } = 30; public int TimeSpanBetweenScansInSeconds { get; set; } = 30;
public bool FileScanPaused { get; set; } = false; public bool FileScanPaused { get; set; } = false;
public bool InitialScanComplete { get; set; } = false; public bool InitialScanComplete { get; set; } = false;
public int MaxParallelScan
{
get => _maxParallelScan;
set
{
_maxParallelScan = value switch
{
< 0 => 1,
> 20 => 10,
_ => value
};
}
}
public bool FullPause { get; set; } = false; public bool FullPause { get; set; } = false;
public Dictionary<string, Dictionary<string, string>> UidServerComments { get; set; } = new(); public Dictionary<string, Dictionary<string, string>> UidServerComments { get; set; } = new();

View File

@@ -1,5 +1,4 @@
using System.IO; using System.IO;
using MareSynchronos.Utils;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
#nullable disable #nullable disable
@@ -12,22 +11,6 @@ namespace MareSynchronos.FileCacheDB
public FileCacheContext() public FileCacheContext()
{ {
DbPath = Path.Combine(Plugin.PluginInterface.ConfigDirectory.FullName, "FileCache.db"); DbPath = Path.Combine(Plugin.PluginInterface.ConfigDirectory.FullName, "FileCache.db");
string oldDbPath = Path.Combine(Plugin.PluginInterface.ConfigDirectory.FullName, "FileCacheDebug.db");
if (!Directory.Exists(Plugin.PluginInterface.ConfigDirectory.FullName))
{
Directory.CreateDirectory(Plugin.PluginInterface.ConfigDirectory.FullName);
}
var veryOldDbPath = Path.Combine(Plugin.PluginInterface.ConfigDirectory.FullName, "..", "FileCacheDebug.db");
if (File.Exists(veryOldDbPath))
{
Logger.Debug("Migrated old path to new path");
File.Move(veryOldDbPath, oldDbPath, true);
}
if (File.Exists(oldDbPath))
{
File.Move(oldDbPath, DbPath, true);
}
Database.EnsureCreated(); Database.EnsureCreated();
} }
@@ -42,7 +25,7 @@ namespace MareSynchronos.FileCacheDB
{ {
if (!optionsBuilder.IsConfigured) if (!optionsBuilder.IsConfigured)
{ {
optionsBuilder.UseSqlite("Data Source=" + DbPath); optionsBuilder.UseSqlite("Data Source=" + DbPath+";Cache=Shared");
} }
} }
@@ -53,8 +36,6 @@ namespace MareSynchronos.FileCacheDB
entity.HasKey(e => new { e.Hash, e.Filepath }); entity.HasKey(e => new { e.Hash, e.Filepath });
entity.ToTable("FileCache"); entity.ToTable("FileCache");
entity.Property(c => c.Version).HasDefaultValue(0).IsRowVersion();
}); });
OnModelCreatingPartial(modelBuilder); OnModelCreatingPartial(modelBuilder);

View File

@@ -115,13 +115,10 @@ public class FileDbManager
private FileCache? GetValidatedFileCache(FileCacheEntity e) private FileCache? GetValidatedFileCache(FileCacheEntity e)
{ {
var fileCache = new FileCache(e); var fileCache = new FileCache(e);
var resulingFileCache = MigrateLegacy(fileCache); var resulingFileCache = MigrateLegacy(fileCache);
if (resulingFileCache == null) return null; if (resulingFileCache == null) return null;
resulingFileCache = ReplacePathPrefixes(resulingFileCache); resulingFileCache = ReplacePathPrefixes(resulingFileCache);
resulingFileCache = Validate(resulingFileCache); resulingFileCache = Validate(resulingFileCache);
return resulingFileCache; return resulingFileCache;
} }
@@ -178,11 +175,11 @@ public class FileDbManager
private FileCache ReplacePathPrefixes(FileCache fileCache) private FileCache ReplacePathPrefixes(FileCache fileCache)
{ {
if (fileCache.OriginalFilepath.Contains(PenumbraPrefix)) if (fileCache.OriginalFilepath.StartsWith(PenumbraPrefix))
{ {
fileCache.SetResolvedFilePath(fileCache.OriginalFilepath.Replace(PenumbraPrefix, _ipcManager.PenumbraModDirectory())); fileCache.SetResolvedFilePath(fileCache.OriginalFilepath.Replace(PenumbraPrefix, _ipcManager.PenumbraModDirectory()));
} }
else if (fileCache.OriginalFilepath.Contains(CachePrefix)) else if (fileCache.OriginalFilepath.StartsWith(CachePrefix))
{ {
fileCache.SetResolvedFilePath(fileCache.OriginalFilepath.Replace(CachePrefix, _configuration.CacheFolder)); fileCache.SetResolvedFilePath(fileCache.OriginalFilepath.Replace(CachePrefix, _configuration.CacheFolder));
} }

View File

@@ -1,5 +1,4 @@
using System; using System;
using System.Collections.Concurrent;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
@@ -84,7 +83,7 @@ public class PeriodicFileScanner : IDisposable
if (!_pluginConfiguration.FileScanPaused || isForced) if (!_pluginConfiguration.FileScanPaused || isForced)
{ {
isForced = false; isForced = false;
await PeriodicFileScan(token); PeriodicFileScan(token);
} }
_timeUntilNextScan = TimeSpan.FromSeconds(timeBetweenScans); _timeUntilNextScan = TimeSpan.FromSeconds(timeBetweenScans);
while (_timeUntilNextScan.TotalSeconds >= 0) while (_timeUntilNextScan.TotalSeconds >= 0)
@@ -130,7 +129,7 @@ public class PeriodicFileScanner : IDisposable
return true; return true;
} }
private async Task PeriodicFileScan(CancellationToken ct) private void PeriodicFileScan(CancellationToken ct)
{ {
TotalFiles = 1; TotalFiles = 1;
var penumbraDir = _ipcManager.PenumbraModDirectory(); var penumbraDir = _ipcManager.PenumbraModDirectory();
@@ -153,68 +152,69 @@ public class PeriodicFileScanner : IDisposable
Logger.Debug("Getting files from " + penumbraDir + " and " + _pluginConfiguration.CacheFolder); Logger.Debug("Getting files from " + penumbraDir + " and " + _pluginConfiguration.CacheFolder);
string[] ext = { ".mdl", ".tex", ".mtrl", ".tmb", ".pap", ".avfx", ".atex", ".sklb", ".eid", ".phyb", ".scd", ".skp" }; string[] ext = { ".mdl", ".tex", ".mtrl", ".tmb", ".pap", ".avfx", ".atex", ".sklb", ".eid", ".phyb", ".scd", ".skp" };
var scannedFiles = new ConcurrentDictionary<string, bool>( var penumbraFiles = Directory.EnumerateDirectories(penumbraDir)
Directory.EnumerateFiles(penumbraDir, "*.*", SearchOption.AllDirectories) .SelectMany(d => Directory.EnumerateFiles(d, "*.*", SearchOption.AllDirectories)
.Select(s => new FileInfo(s)) .Select(s => new FileInfo(s))
.Where(f => ext.Contains(f.Extension) && !f.FullName.Contains(@"\bg\") && !f.FullName.Contains(@"\bgcommon\") && !f.FullName.Contains(@"\ui\")) .Where(f => ext.Contains(f.Extension) && !f.FullName.Contains(@"\bg\") && !f.FullName.Contains(@"\bgcommon\") && !f.FullName.Contains(@"\ui\"))
.Select(f => f.FullName.ToLowerInvariant()) .Select(f => f.FullName.ToLowerInvariant())).ToList();
.Concat(Directory.EnumerateFiles(_pluginConfiguration.CacheFolder, "*.*", SearchOption.AllDirectories)
var cacheFiles = Directory.EnumerateFiles(_pluginConfiguration.CacheFolder, "*.*", SearchOption.TopDirectoryOnly)
.Where(f => new FileInfo(f).Name.Length == 40) .Where(f => new FileInfo(f).Name.Length == 40)
.Select(s => s.ToLowerInvariant())) .Select(s => s.ToLowerInvariant());
.Select(p => new KeyValuePair<string, bool>(p, false)).ToList());
var scannedFiles = new Dictionary<string, bool>(penumbraFiles.Concat(cacheFiles).Select(c => new KeyValuePair<string, bool>(c, false)));
List<FileCacheEntity> fileDbEntries; List<FileCacheEntity> fileDbEntries;
using (var db = new FileCacheContext()) using (var db = new FileCacheContext())
{ {
fileDbEntries = await db.FileCaches.ToListAsync(cancellationToken: ct); fileDbEntries = db.FileCaches.AsNoTracking().ToList();
} }
TotalFiles = scannedFiles.Count; TotalFiles = scannedFiles.Count;
Logger.Debug("Database contains " + fileDbEntries.Count + " files, local system contains " + TotalFiles); Logger.Debug("Database contains " + fileDbEntries.Count + " files, local system contains " + TotalFiles);
// scan files from database // scan files from database
Parallel.ForEach(fileDbEntries.ToList(), new ParallelOptions() var cpuCount = (int)(Environment.ProcessorCount / 2.0f);
foreach (var chunk in fileDbEntries.Chunk(cpuCount))
{ {
MaxDegreeOfParallelism = _pluginConfiguration.MaxParallelScan, Task[] tasks = chunk.Select(c => Task.Run(() =>
CancellationToken = ct,
},
dbEntry =>
{
if (ct.IsCancellationRequested) return;
try
{ {
var file = _fileDbManager.ValidateFileCache(dbEntry); var file = _fileDbManager.ValidateFileCache(c);
if (file != null && scannedFiles.ContainsKey(file.Filepath)) if (file != null)
{ {
scannedFiles[file.Filepath] = true; scannedFiles[file.Filepath] = true;
} }
}
catch (Exception ex)
{
Logger.Warn(ex.Message);
Logger.Warn(ex.StackTrace);
}
Interlocked.Increment(ref currentFileProgress); Interlocked.Increment(ref currentFileProgress);
}); })).ToArray();
Task.WaitAll(tasks, ct);
Thread.Sleep(3);
if (ct.IsCancellationRequested) return;
}
Logger.Debug("Scanner validated existing db files"); Logger.Debug("Scanner validated existing db files");
if (ct.IsCancellationRequested) return; if (ct.IsCancellationRequested) return;
// scan new files // scan new files
Parallel.ForEach(scannedFiles.Where(c => c.Value == false), new ParallelOptions() foreach (var chunk in scannedFiles.Where(c => c.Value == false).Chunk(cpuCount))
{ {
MaxDegreeOfParallelism = _pluginConfiguration.MaxParallelScan, Task[] tasks = chunk.Select(c => Task.Run(() =>
CancellationToken = ct {
}, var entry = _fileDbManager.CreateFileEntry(c.Key);
file => if (entry == null) _ = _fileDbManager.CreateCacheEntry(c.Key);
{
if (ct.IsCancellationRequested) return;
var entry = _fileDbManager.CreateFileEntry(file.Key); Interlocked.Increment(ref currentFileProgress);
if (entry == null) _ = _fileDbManager.CreateCacheEntry(file.Key); })).ToArray();
Interlocked.Increment(ref currentFileProgress);
}); Task.WaitAll(tasks, ct);
Thread.Sleep(3);
if (ct.IsCancellationRequested) return;
}
Logger.Debug("Scanner added new files to db"); Logger.Debug("Scanner added new files to db");

View File

@@ -23,7 +23,6 @@ namespace MareSynchronos
private const string CommandName = "/mare"; private const string CommandName = "/mare";
private readonly ApiController _apiController; private readonly ApiController _apiController;
private readonly CommandManager _commandManager; private readonly CommandManager _commandManager;
private readonly Framework _framework;
private readonly Configuration _configuration; private readonly Configuration _configuration;
private readonly PeriodicFileScanner _fileCacheManager; private readonly PeriodicFileScanner _fileCacheManager;
private readonly IntroUi _introUi; private readonly IntroUi _introUi;
@@ -49,7 +48,6 @@ namespace MareSynchronos
Logger.Debug("Launching " + Name); Logger.Debug("Launching " + Name);
PluginInterface = pluginInterface; PluginInterface = pluginInterface;
_commandManager = commandManager; _commandManager = commandManager;
_framework = framework;
_configuration = PluginInterface.GetPluginConfig() as Configuration ?? new Configuration(); _configuration = PluginInterface.GetPluginConfig() as Configuration ?? new Configuration();
_configuration.Initialize(PluginInterface); _configuration.Initialize(PluginInterface);
_configuration.Migrate(); _configuration.Migrate();
@@ -65,11 +63,6 @@ namespace MareSynchronos
_dalamudUtil = new DalamudUtil(clientState, objectTable, framework, condition); _dalamudUtil = new DalamudUtil(clientState, objectTable, framework, condition);
_ipcManager = new IpcManager(PluginInterface, _dalamudUtil); _ipcManager = new IpcManager(PluginInterface, _dalamudUtil);
// Compatibility for FileSystemWatchers under OSX
if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
Environment.SetEnvironmentVariable("MONO_MANAGED_WATCHER", "enabled");
_fileDialogManager = new FileDialogManager(); _fileDialogManager = new FileDialogManager();
_fileDbManager = new FileDbManager(_ipcManager, _configuration); _fileDbManager = new FileDbManager(_ipcManager, _configuration);
_apiController = new ApiController(_configuration, _dalamudUtil, _fileDbManager); _apiController = new ApiController(_configuration, _dalamudUtil, _fileDbManager);

View File

@@ -220,10 +220,6 @@ namespace MareSynchronos.UI
if (!_fileCacheManager.IsScanRunning && !string.IsNullOrEmpty(_pluginConfiguration.CacheFolder) && _uiShared.HasValidPenumbraModPath && Directory.Exists(_pluginConfiguration.CacheFolder)) if (!_fileCacheManager.IsScanRunning && !string.IsNullOrEmpty(_pluginConfiguration.CacheFolder) && _uiShared.HasValidPenumbraModPath && Directory.Exists(_pluginConfiguration.CacheFolder))
{ {
UiShared.TextWrapped("You can adjust how many parallel threads will be used for scanning. Mind that ultimately it will depend on the amount of mods, your disk speed and your CPU. " +
"More is not necessarily better, the default of 10 should be fine for most cases.");
_uiShared.DrawParallelScansSetting();
if (ImGui.Button("Start Scan##startScan")) if (ImGui.Button("Start Scan##startScan"))
{ {
_fileCacheManager.InvokeScan(true); _fileCacheManager.InvokeScan(true);

View File

@@ -586,7 +586,6 @@ namespace MareSynchronos.UI
private void DrawFileCacheSettings() private void DrawFileCacheSettings()
{ {
_uiShared.DrawFileScanState(); _uiShared.DrawFileScanState();
_uiShared.DrawParallelScansSetting();
_uiShared.DrawTimeSpanBetweenScansSetting(); _uiShared.DrawTimeSpanBetweenScansSetting();
_uiShared.DrawCacheDirectorySetting(); _uiShared.DrawCacheDirectorySetting();
ImGui.Text($"Local cache size: {UiShared.ByteToString(_uiShared.FileCacheSize)}"); ImGui.Text($"Local cache size: {UiShared.ByteToString(_uiShared.FileCacheSize)}");

View File

@@ -520,17 +520,6 @@ namespace MareSynchronos.UI
_cacheScanner.InvokeScan(true); _cacheScanner.InvokeScan(true);
} }
public void DrawParallelScansSetting()
{
var parallelScans = _pluginConfiguration.MaxParallelScan;
if (ImGui.SliderInt("File scan parallelism##parallelism", ref parallelScans, 1, 20))
{
_pluginConfiguration.MaxParallelScan = parallelScans;
_pluginConfiguration.Save();
}
DrawHelpText("Decrease to lessen load of file scans. File scans will take longer to execute with less parallel threads.");
}
public void DrawTimeSpanBetweenScansSetting() public void DrawTimeSpanBetweenScansSetting()
{ {
var timeSpan = _pluginConfiguration.TimeSpanBetweenScansInSeconds; var timeSpan = _pluginConfiguration.TimeSpanBetweenScansInSeconds;