Games: Entities: GetProcProperties: Early exit if source has no proc properties#200
Games: Entities: GetProcProperties: Early exit if source has no proc properties#200luigi311 wants to merge 1 commit intoCrypto137:masterfrom
Conversation
…perties Signed-off-by: Luis Garcia <git@luigi311.com>
|
ObjectPoolManager.Instance.Get() doesn't allocate new instances outside of the initial call for a game thread contrary to what the comment you added to GetProcProperties() says. With this in mind, the performance benefit of these changes become potentially not worth the extra complexity being introduced. Please do a benchmark, ideally using BenchmarkDotNet, to demonstrate whether there is a meaningful performance benefit to these changes. If there is evidence that these changes have a meaningful benefit worth the tradeoff, instead of introducing a HasPropertyInRange() method to PropertyCollection I would prefer to keep it all contained in GetProcProperties(). |
|
thanks for pointing the right way ill take a look at that BenchmarkDotNet tool, im not too familiar with dotnet so its nice to be shown that as a way to check impact instead of going the linux perf method i was utilizing. Is there an example of it being used somewhere with this project already? |
Due to how data-driven a lot of the code is, setting up BenchmarkDotNet may be very difficult in cases where everything can't be easily mocked. The second best alternative is using the Stopwatch class. Here is an example of a quick and dirty BenchmarkDotNet thing I did a while back (sorry for the crappy code, this is just some napkin stuff I still have around, you would probably be better off looking up official docs): [SimpleJob(RuntimeMoniker.Net80)]
[MemoryDiagnoser]
public class MuxHeaderBenchmark
{
private const int NumIterations = 100000;
private readonly byte[] _bytes = new byte[6];
private (ushort, int, byte) _header;
public MuxHeaderBenchmark()
{
for (int i = 0; i < 6; i++)
_bytes[i] = (byte)(i + 1);
}
[Benchmark]
public void ReadMemoryMarshal()
{
for (int i = 0; i < NumIterations; i++)
{
using MemoryStream ms = new(_bytes);
_header = ReadMemoryMarshal(ms);
}
}
[Benchmark]
public void ReadSpan()
{
for (int i = 0; i < NumIterations; i++)
{
using MemoryStream ms = new(_bytes);
_header = ReadSpan(ms);
}
}
[Benchmark]
public void ReadIndividualBytes()
{
for (int i = 0; i < NumIterations; i++)
{
using MemoryStream ms = new(_bytes);
_header = ReadIndividualBytes(ms);
}
}
public static (ushort, int, byte) ReadMemoryMarshal(Stream stream)
{
// Read bytes from the stream
Span<byte> bytes = stackalloc byte[8];
stream.Read(bytes[..6]);
// Reinterpret cast packed data as a ulong
ulong bits = MemoryMarshal.Cast<byte, ulong>(bytes)[0];
// Unpack data
ushort muxId = (ushort)(bits & 0xFFFF);
int dataSize = (int)(bits >> 16 & 0xFFFFFF);
byte command = (byte)(bits >> 40 & 0xFF);
return (muxId, dataSize, command);
}
public static (ushort, int, byte) ReadSpan(Stream stream)
{
// Read bytes from the stream
Span<byte> bytes = stackalloc byte[8];
stream.Read(bytes[..6]);
ulong bits =
bytes[0] |
(ulong)bytes[1] << 8 |
(ulong)bytes[2] << 16 |
(ulong)bytes[3] << 24 |
(ulong)bytes[4] << 32 |
(ulong)bytes[5] << 40;
// Unpack data
ushort muxId = (ushort)(bits & 0xFFFF);
int dataSize = (int)(bits >> 16 & 0xFFFFFF);
byte command = (byte)(bits >> 40 & 0xFF);
return (muxId, dataSize, command);
}
public static (ushort, int, byte) ReadIndividualBytes(Stream stream)
{
// Unpack data
ushort muxId = (ushort)(stream.ReadByte() | stream.ReadByte() << 8);
int dataSize = stream.ReadByte() | stream.ReadByte() << 8 | stream.ReadByte() << 16;
byte command = (byte)stream.ReadByte();
return (muxId, dataSize, command);
}
} |
Adds early exit condition on GetProcProperties to reduce property copies when there is no need. this was tested by going through the opening missions on two new accounts up until you get the xp orb from ultron.
Samples Before
Samples After