mirror of
https://github.com/space-wizards/RobustToolbox.git
synced 2026-09-15 06:42:25 +02:00
Save 0.4% Windows server CPU.
This commit is contained in:
@@ -94,10 +94,6 @@ namespace Robust.Server
|
||||
private ILogHandler? _logHandler;
|
||||
private IGameLoop _mainLoop = default!;
|
||||
|
||||
private TimeSpan _lastTitleUpdate;
|
||||
private long _lastReceivedBytes;
|
||||
private long _lastSentBytes;
|
||||
|
||||
private string? _shutdownReason;
|
||||
|
||||
private readonly ManualResetEventSlim _shutdownEvent = new(false);
|
||||
@@ -515,28 +511,6 @@ namespace Robust.Server
|
||||
_mainLoop = gameLoop;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the console window title with performance statistics.
|
||||
/// </summary>
|
||||
private void UpdateTitle()
|
||||
{
|
||||
if (!Environment.UserInteractive || System.Console.IsInputRedirected)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// every 1 second update stats in the console window title
|
||||
if ((_time.RealTime - _lastTitleUpdate).TotalSeconds < 1.0)
|
||||
return;
|
||||
|
||||
var netStats = UpdateBps();
|
||||
System.Console.Title = string.Format("FPS: {0:N2} SD: {1:N2}ms | Net: ({2}) | Memory: {3:N0} KiB",
|
||||
Math.Round(_time.FramesPerSecondAvg, 2),
|
||||
_time.RealFrameTimeStdDev.TotalMilliseconds,
|
||||
netStats,
|
||||
Process.GetCurrentProcess().PrivateMemorySize64 >> 10);
|
||||
_lastTitleUpdate = _time.RealTime;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Loads the server settings from the ConfigurationManager.
|
||||
@@ -593,22 +567,9 @@ namespace Robust.Server
|
||||
}
|
||||
}
|
||||
|
||||
private string UpdateBps()
|
||||
{
|
||||
var stats = IoCManager.Resolve<IServerNetManager>().Statistics;
|
||||
|
||||
var bps =
|
||||
$"Send: {(stats.SentBytes - _lastSentBytes) >> 10:N0} KiB/s, Recv: {(stats.ReceivedBytes - _lastReceivedBytes) >> 10:N0} KiB/s";
|
||||
|
||||
_lastSentBytes = stats.SentBytes;
|
||||
_lastReceivedBytes = stats.ReceivedBytes;
|
||||
|
||||
return bps;
|
||||
}
|
||||
|
||||
private void Input(FrameEventArgs args)
|
||||
{
|
||||
_systemConsole.Update();
|
||||
_systemConsole.UpdateInput();
|
||||
|
||||
_network.ProcessPackets();
|
||||
_taskManager.ProcessPendingTasks();
|
||||
@@ -622,7 +583,7 @@ namespace Robust.Server
|
||||
// These are always the same on the server, there is no prediction.
|
||||
_time.LastRealTick = _time.CurTick;
|
||||
|
||||
UpdateTitle();
|
||||
_systemConsole.UpdateTick();
|
||||
|
||||
using (TickUsage.WithLabels("PreEngine").NewTimer())
|
||||
{
|
||||
|
||||
@@ -8,12 +8,14 @@
|
||||
/// <summary>
|
||||
/// process input/output of the console. This needs to be called often.
|
||||
/// </summary>
|
||||
void Update();
|
||||
void UpdateInput();
|
||||
|
||||
/// <summary>
|
||||
/// Prints <paramref name="text" /> to the system console.
|
||||
/// </summary>
|
||||
/// <param name="text">Text to write to the system console.</param>
|
||||
void Print(string text);
|
||||
|
||||
void UpdateTick();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,12 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using Robust.Shared.Asynchronous;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Network;
|
||||
using Robust.Shared.Timing;
|
||||
using Robust.Shared.Utility;
|
||||
using Con = System.Console;
|
||||
|
||||
namespace Robust.Server.Console
|
||||
@@ -12,6 +16,12 @@ namespace Robust.Server.Console
|
||||
[Dependency] private readonly IServerConsoleHost _conShell = default!;
|
||||
[Dependency] private readonly ITaskManager _taskManager = default!;
|
||||
[Dependency] private readonly IBaseServer _baseServer = default!;
|
||||
[Dependency] private readonly IServerNetManager _netManager = default!;
|
||||
[Dependency] private readonly IGameTiming _time = default!;
|
||||
|
||||
//
|
||||
// Command entry stuff.
|
||||
//
|
||||
|
||||
private readonly Dictionary<int, string> commandHistory = new();
|
||||
private string currentBuffer = "";
|
||||
@@ -21,6 +31,20 @@ namespace Robust.Server.Console
|
||||
private int tabCompleteIndex;
|
||||
private ConsoleKey lastKeyPressed = ConsoleKey.NoName;
|
||||
|
||||
//
|
||||
// Title update stuff.
|
||||
//
|
||||
|
||||
// This is ridiculously expensive to fetch for some reason.
|
||||
// I'm gonna just assume that this can't change during the lifetime of the process. I hope.
|
||||
// I want this ridiculous 0.1% CPU usage off my profiler.
|
||||
private readonly bool _userInteractive = Environment.UserInteractive;
|
||||
|
||||
private TimeSpan _lastTitleUpdate;
|
||||
private long _lastReceivedBytes;
|
||||
private long _lastSentBytes;
|
||||
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (Environment.UserInteractive)
|
||||
@@ -37,7 +61,53 @@ namespace Robust.Server.Console
|
||||
}
|
||||
}
|
||||
|
||||
public void Update()
|
||||
public void UpdateTick()
|
||||
{
|
||||
UpdateTitle();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the console window title with performance statistics.
|
||||
/// </summary>
|
||||
private void UpdateTitle()
|
||||
{
|
||||
if (!_userInteractive || System.Console.IsInputRedirected)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// every 1 second update stats in the console window title
|
||||
if ((_time.RealTime - _lastTitleUpdate).TotalSeconds < 1.0)
|
||||
return;
|
||||
|
||||
var netStats = UpdateBps();
|
||||
var process = Process.GetCurrentProcess();
|
||||
var privateSize = process.GetPrivateMemorySize64NotSlowHolyFuckingShitMicrosoft();
|
||||
|
||||
System.Console.WriteLine($"A: {privateSize} B: {process.PrivateMemorySize64}");
|
||||
|
||||
System.Console.Title = string.Format("FPS: {0:N2} SD: {1:N2}ms | Net: ({2}) | Memory: {3:N0} KiB",
|
||||
Math.Round(_time.FramesPerSecondAvg, 2),
|
||||
_time.RealFrameTimeStdDev.TotalMilliseconds,
|
||||
netStats,
|
||||
privateSize >> 10);
|
||||
_lastTitleUpdate = _time.RealTime;
|
||||
}
|
||||
|
||||
private string UpdateBps()
|
||||
{
|
||||
var stats = _netManager.Statistics;
|
||||
|
||||
var bps =
|
||||
$"Send: {(stats.SentBytes - _lastSentBytes) >> 10:N0} KiB/s, Recv: {(stats.ReceivedBytes - _lastReceivedBytes) >> 10:N0} KiB/s";
|
||||
|
||||
_lastSentBytes = stats.SentBytes;
|
||||
_lastReceivedBytes = stats.ReceivedBytes;
|
||||
|
||||
return bps;
|
||||
}
|
||||
|
||||
public void UpdateInput()
|
||||
{
|
||||
if (Con.IsInputRedirected)
|
||||
{
|
||||
@@ -161,7 +231,7 @@ namespace Robust.Server.Console
|
||||
{
|
||||
var currentLineCursor = Con.CursorTop;
|
||||
Con.SetCursorPosition(0, Con.CursorTop);
|
||||
Con.Write(new string(' ', Con.WindowWidth-1));
|
||||
Con.Write(new string(' ', Con.WindowWidth - 1));
|
||||
Con.SetCursorPosition(0, currentLineCursor);
|
||||
}
|
||||
|
||||
@@ -174,7 +244,8 @@ namespace Robust.Server.Console
|
||||
|
||||
if (tabCompleteList.Count == 0)
|
||||
{
|
||||
tabCompleteList = _conShell.RegisteredCommands.Keys.Where(key => key.StartsWith(currentBuffer)).ToList();
|
||||
tabCompleteList = _conShell.RegisteredCommands.Keys.Where(key => key.StartsWith(currentBuffer))
|
||||
.ToList();
|
||||
if (tabCompleteList.Count == 0)
|
||||
{
|
||||
return String.Empty;
|
||||
|
||||
@@ -2,7 +2,7 @@ namespace Robust.Server.Console
|
||||
{
|
||||
internal sealed class SystemConsoleManagerDummy : ISystemConsoleManager
|
||||
{
|
||||
public void Update()
|
||||
public void UpdateInput()
|
||||
{
|
||||
// Nada.
|
||||
}
|
||||
@@ -11,5 +11,10 @@ namespace Robust.Server.Console
|
||||
{
|
||||
// Nada.
|
||||
}
|
||||
|
||||
public void UpdateTick()
|
||||
{
|
||||
// Nada.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,82 @@
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
// ReSharper disable InconsistentNaming
|
||||
// ReSharper disable IdentifierTypo
|
||||
// ReSharper disable CommentTypo
|
||||
#pragma warning disable 649
|
||||
|
||||
namespace Robust.Shared.Utility
|
||||
{
|
||||
internal static unsafe class ProcessExt
|
||||
{
|
||||
// THIS ENTIRE METHOD EXISTS PURELY OUT OF SPITE FOR MICROSOFT.
|
||||
// NOT TO SAVE 0.3% CPU USAGE ON WINDOWS. NAH.
|
||||
// TO SPITE MICROSOFT.
|
||||
// WHAT ABOUT THE 2.2% CPU USAGE IN CONSOLE.KEYAVAILABLE? WHAT ABOUT THAT WHATABOUTISM HUH???
|
||||
// I WANTED TO GO TO BED NOW IT'S 4:20 AM (BASED) GOD DAMNIT.
|
||||
// AS A RESULT, EVERYTHING HERE WILL BE WRITTEN IN *FUCKING* CAPS.
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
public static long GetPrivateMemorySize64NotSlowHolyFuckingShitMicrosoft(this Process process)
|
||||
{
|
||||
if (!OperatingSystem.IsWindows())
|
||||
// IT'S NOT SLOW ON LINUX, *LUCKILY*.
|
||||
// WELL I DIDN'T PROFILE IT BUT THEY DON'T DO A STUPID SEARCH OVER EVERY PROCESS.
|
||||
// IT USES PROCFS, IF YOU'RE CURIOUS.
|
||||
return process.PrivateMemorySize64;
|
||||
|
||||
//
|
||||
// GLASS TOLD ME TO PUT A TL;DR IN SO TL;DR
|
||||
// PROCESS.PRIVATEMEMORYSIZE64 (AND A BUNCH OF OTHERS) ARE SLOW AS BALLS (2+ms for me).
|
||||
// THIS ISN'T.
|
||||
//
|
||||
// AS IT *FUCKING* TURNS OUT.
|
||||
// *MANY* OF THE FIELDS ON PROCESS (THE CLASS, YOU KNOW THE ONE THIS METHOD IS AN EXTENSION FOR) HAVE TO FETCH A "PROCESS INFO".
|
||||
// HOW DO YOU FETCH A PROCESS INFO ON WINDOWS? OH YEAH YOU CALL NTQUERYSYSTEMINFORMATION APPARENTLY.
|
||||
// OH WAIT. THAT RETURNS INFO FOR *EVERY* PROCESS ON THE FUCKING SYSTEM.
|
||||
// SO YES. IF YOU LOOK UP PROCESS.PRIVATEMEMORYSIZE64, IT HAS TO SEARCH THROUGH *EVERY PROCESS ON THE FUCKING SYSTEM* TO FIND THE PROCESS IT'S LOOKING FOR.
|
||||
// THIS TAKES MORE THAN 2 FUCKING MILLISECONDS PER LOOKUP ON MY SYSTEM.
|
||||
// IF THE TITLE BAR ON THE CONSOLE WAS UPDATED EVERY TICK INSTEAD OF ONCE PER FRAME, THIS WOULD LITERALLY BE MOST OF THE IDLE SERVER CPU USAGE.
|
||||
// ***ARE YOU FUCKING KIDDING ME ????***
|
||||
// OH YEAH, NTQUERYSYSTEMINFORMATION IS DOCUMENTED AS AN UNSTABLE API THAT CAN CHANGE AT ANY TIME, APPARENTLY.
|
||||
// .NET FEELS IT'S FINE TO USE THAT (YEAH GUESS THEY'RE NEVER REMOVING IT FROM WINDOWS LMAO)
|
||||
// ARE YOU FUCKING TELLING ME THE .NET TEAM COULDN'T HAVE WALKED TO THE OTHER SIDE OF THE OFFICE AND TOLD THE KERNEL TEAM TO ADD A VERSION OF NTQUERYSYSTEMINFORMATION THAT FETCHES INFO FOR A SINGLE PROCESS?
|
||||
// THEY THOUGHT THIS SHIT WAS FUCKING ACCEPTABLE?
|
||||
// JESUS FUCKING CHRIST.
|
||||
// ANYWAYS, THIS EXTENSION METHOD USES GETPROCESSMEMORYINFO() INSTEAD BECAUSE THAT'S PROBABLY NOT FUCKING MORONIC.
|
||||
//
|
||||
|
||||
PROCESS_MEMORY_COUNTERS_EX counters;
|
||||
|
||||
if (GetProcessMemoryInfo(process.Handle, &counters, sizeof(PROCESS_MEMORY_COUNTERS_EX)) == 0)
|
||||
return 0;
|
||||
|
||||
var count = counters.PrivateUsage;
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
private struct PROCESS_MEMORY_COUNTERS_EX
|
||||
{
|
||||
public int cb;
|
||||
public int PageFaultCount;
|
||||
public nint PeakWorkingSetSize;
|
||||
public nint WorkingSetSize;
|
||||
public nint QuotaPeakPagedPoolUsage;
|
||||
public nint QuotaPagedPoolUsage;
|
||||
public nint QuotaPeakNonPagedPoolUsage;
|
||||
public nint QuotaNonPagedPoolUsage;
|
||||
public nint PagefileUsage;
|
||||
public nint PeakPagefileUsage;
|
||||
public nint PrivateUsage;
|
||||
}
|
||||
|
||||
// ReSharper disable once StringLiteralTypo
|
||||
[DllImport("psapi.dll", SetLastError = true)]
|
||||
private static extern int GetProcessMemoryInfo(
|
||||
IntPtr Process,
|
||||
PROCESS_MEMORY_COUNTERS_EX* ppsmemCounters,
|
||||
int cb);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user