performance optimizations
This commit is contained in:
		| @@ -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(); | ||||||
|   | |||||||
| @@ -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); | ||||||
|   | |||||||
| @@ -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)); | ||||||
|         } |         } | ||||||
|   | |||||||
| @@ -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"); | ||||||
|  |  | ||||||
|   | |||||||
| @@ -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); | ||||||
|   | |||||||
| @@ -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); | ||||||
|   | |||||||
| @@ -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)}"); | ||||||
|   | |||||||
| @@ -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; | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user
	 Stanley Dimant
					Stanley Dimant