diff --git a/Dalamud/Configuration/Internal/DalamudConfiguration.cs b/Dalamud/Configuration/Internal/DalamudConfiguration.cs
index 22fb2f448..850aeb7e9 100644
--- a/Dalamud/Configuration/Internal/DalamudConfiguration.cs
+++ b/Dalamud/Configuration/Internal/DalamudConfiguration.cs
@@ -4,6 +4,7 @@ using System.Globalization;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
+using System.Threading.Tasks;
using Dalamud.Game.Text;
using Dalamud.Interface;
@@ -45,6 +46,8 @@ internal sealed class DalamudConfiguration : IInternalDisposableService
[JsonIgnore]
private bool isSaveQueued;
+ private Task? writeTask;
+
///
/// Delegate for the event that occurs when the dalamud configuration is saved.
///
@@ -560,6 +563,9 @@ internal sealed class DalamudConfiguration : IInternalDisposableService
{
// Make sure that we save, if a save is queued while we are shutting down
this.Update();
+
+ // Wait for the write task to finish
+ this.writeTask?.Wait();
}
///
@@ -614,8 +620,22 @@ internal sealed class DalamudConfiguration : IInternalDisposableService
if (this.configPath is null)
throw new InvalidOperationException("configPath is not set.");
- Service.Get().WriteAllText(
- this.configPath, JsonConvert.SerializeObject(this, SerializerSettings));
+ // Wait for previous write to finish
+ this.writeTask?.Wait();
+
+ this.writeTask = Task.Run(() =>
+ {
+ Service.Get().WriteAllText(
+ this.configPath,
+ JsonConvert.SerializeObject(this, SerializerSettings));
+ }).ContinueWith(t =>
+ {
+ if (t.IsFaulted)
+ {
+ Log.Error(t.Exception, "Failed to save DalamudConfiguration to {Path}", this.configPath);
+ }
+ });
+
this.DalamudConfigurationSaved?.Invoke(this);
}
}
diff --git a/Dalamud/Interface/Internal/Windows/Data/DataWindow.cs b/Dalamud/Interface/Internal/Windows/Data/DataWindow.cs
index 7678b395e..f3ec882fc 100644
--- a/Dalamud/Interface/Internal/Windows/Data/DataWindow.cs
+++ b/Dalamud/Interface/Internal/Windows/Data/DataWindow.cs
@@ -60,6 +60,7 @@ internal class DataWindow : Window, IDisposable
new ToastWidget(),
new UiColorWidget(),
new UldWidget(),
+ new VfsWidget(),
};
private readonly IOrderedEnumerable orderedModules;
diff --git a/Dalamud/Interface/Internal/Windows/Data/Widgets/VfsWidget.cs b/Dalamud/Interface/Internal/Windows/Data/Widgets/VfsWidget.cs
new file mode 100644
index 000000000..019d743bc
--- /dev/null
+++ b/Dalamud/Interface/Internal/Windows/Data/Widgets/VfsWidget.cs
@@ -0,0 +1,102 @@
+using System.Diagnostics;
+using System.IO;
+
+using Dalamud.Configuration.Internal;
+using Dalamud.Storage;
+using ImGuiNET;
+using Serilog;
+
+namespace Dalamud.Interface.Internal.Windows.Data.Widgets;
+
+///
+/// Widget for displaying configuration info.
+///
+internal class VfsWidget : IDataWindowWidget
+{
+ private int numBytes = 1024;
+ private int reps = 1;
+
+ ///
+ public string[]? CommandShortcuts { get; init; } = { "vfs" };
+
+ ///
+ public string DisplayName { get; init; } = "VFS Performance";
+
+ ///
+ public bool Ready { get; set; }
+
+ ///
+ public void Load()
+ {
+ this.Ready = true;
+ }
+
+ ///
+ public void Draw()
+ {
+ var service = Service.Get();
+ var dalamud = Service.Get();
+
+ ImGui.InputInt("Num bytes", ref this.numBytes);
+ ImGui.InputInt("Reps", ref this.reps);
+
+ var path = Path.Combine(dalamud.StartInfo.WorkingDirectory!, "test.bin");
+
+ if (ImGui.Button("Write"))
+ {
+ Log.Information("=== WRITING ===");
+ var data = new byte[this.numBytes];
+ var stopwatch = new Stopwatch();
+ var acc = 0L;
+
+ for (var i = 0; i < this.reps; i++)
+ {
+ stopwatch.Restart();
+ service.WriteAllBytes(path, data);
+ stopwatch.Stop();
+ acc += stopwatch.ElapsedMilliseconds;
+ Log.Information("Turn {Turn} took {Ms}ms", i, stopwatch.ElapsedMilliseconds);
+ }
+
+ Log.Information("Took {Ms}ms in total", acc);
+ }
+
+ if (ImGui.Button("Read"))
+ {
+ Log.Information("=== READING ===");
+ var stopwatch = new Stopwatch();
+ var acc = 0L;
+
+ for (var i = 0; i < this.reps; i++)
+ {
+ stopwatch.Restart();
+ service.ReadAllBytes(path);
+ stopwatch.Stop();
+ acc += stopwatch.ElapsedMilliseconds;
+ Log.Information("Turn {Turn} took {Ms}ms", i, stopwatch.ElapsedMilliseconds);
+ }
+
+ Log.Information("Took {Ms}ms in total", acc);
+ }
+
+ if (ImGui.Button("Test Config"))
+ {
+ var config = Service.Get();
+
+ Log.Information("=== READING ===");
+ var stopwatch = new Stopwatch();
+ var acc = 0L;
+
+ for (var i = 0; i < this.reps; i++)
+ {
+ stopwatch.Restart();
+ config.ForceSave();
+ stopwatch.Stop();
+ acc += stopwatch.ElapsedMilliseconds;
+ Log.Information("Turn {Turn} took {Ms}ms", i, stopwatch.ElapsedMilliseconds);
+ }
+
+ Log.Information("Took {Ms}ms in total", acc);
+ }
+ }
+}