Skip to content

Conversation

@BruceChenQAQ
Copy link
Collaborator

@BruceChenQAQ BruceChenQAQ commented Aug 24, 2022

fix #2118 , fix #2082, fix #2129, fix #2119, fix #2094, fix #2132, fix #2111, fix #2150

Some fixes have been submitted to ConsoleInteractive.

I will record some of the before and after optimization comparisons below.

@BruceChenQAQ BruceChenQAQ marked this pull request as draft August 24, 2022 10:31
@BruceChenQAQ BruceChenQAQ changed the title Bug fix: Cancel chunk load task when switching worlds Bug fix & Performance Optimization Aug 24, 2022
@BruceChenQAQ
Copy link
Collaborator Author

BruceChenQAQ commented Aug 24, 2022

Check Vs WithoutCheck

image

Code
using System.Diagnostics;

const string Format = "{0,7:0.000} ";
const int TotalPasses = 25000;
const int Size = 50;
Stopwatch timer = new();

var functionList = new List<Action> { WithCheck, WithoutCheck };

Console.WriteLine("{0,5}{1,20}{2,20}{3,20}{4,20}", "Run", "Ticks", "ms", "Ticks/Instance", "ms/Instance");

foreach (var item in functionList)
{
    var warmup = Test(item);
    var run = Test(item);

    Console.WriteLine($"{item.Method.Name}:");
    PrintResult("warmup", warmup);
    PrintResult("run", run);
    Console.WriteLine();
}

static void PrintResult(string name, long ticks)
{
    Console.WriteLine("{0,10}{1,20}{2,20}{3,20}{4,20}", name, ticks, string.Format(Format, (decimal)ticks / TimeSpan.TicksPerMillisecond), (decimal)ticks / TotalPasses, (decimal)ticks / TotalPasses / TimeSpan.TicksPerMillisecond);
}

long Test(Action func)
{
    timer.Restart();
    func();
    timer.Stop();
    return timer.ElapsedTicks;
}

static void WithCheck()
{
    ushort[,,] blocks = new ushort[16, 16, 16];

    for (var passes = 0; passes < TotalPasses; passes++)
    {
        for (int chunkY = 0; chunkY < 24; chunkY++)
        {
            for (int blockY = 0; blockY < 16; blockY++)
            {
                for (int blockZ = 0; blockZ < 16; blockZ++)
                {
                    for (int blockX = 0; blockX < 16; blockX++)
                    {
                        if (blockX < 0 || blockX >= 16)
                            throw new ArgumentOutOfRangeException("blockX", "Must be between 0 and " + (16 - 1) + " (inclusive)");
                        if (blockY < 0 || blockY >= 16)
                            throw new ArgumentOutOfRangeException("blockY", "Must be between 0 and " + (16 - 1) + " (inclusive)");
                        if (blockZ < 0 || blockZ >= 16)
                            throw new ArgumentOutOfRangeException("blockZ", "Must be between 0 and " + (16 - 1) + " (inclusive)");

                        blocks[blockX, blockY, blockZ] = (ushort)(blockY * blockZ * blockX);
                    }
                }
            }
        }
    }
}

static void WithoutCheck()
{
    ushort[,,] blocks = new ushort[16, 16, 16];

    for (var passes = 0; passes < TotalPasses; passes++)
    {
        for (int chunkY = 0; chunkY < 24; chunkY++)
        {
            for (int blockY = 0; blockY < 16; blockY++)
            {
                for (int blockZ = 0; blockZ < 16; blockZ++)
                {
                    for (int blockX = 0; blockX < 16; blockX++)
                    {
                        blocks[blockX, blockY, blockZ] = (ushort)(blockY * blockZ * blockX);
                    }
                }
            }
        }
    }
}

@BruceChenQAQ
Copy link
Collaborator Author

  • new ReadNextVarInt
public int ReadNextVarInt(Queue<byte> cache)
{
    int i = 0;
    int j = 0;
    byte b;
    do
    {
        b = cache.Dequeue();
        i |= (b & 127) << j++ * 7;
        if (j > 5) throw new OverflowException("VarInt too big");
    } while ((b & 128) == 128);
    return i;
}

2022-08-24_222002


  • old ReadNextVarInt
string rawData = BitConverter.ToString(cache.ToArray());
int i = 0;
int j = 0;
int k = 0;
while (true)
{
    k = ReadNextByte(cache);
    i |= (k & 0x7F) << j++ * 7;
    if (j > 5) throw new OverflowException("VarInt too big " + rawData);
    if ((k & 0x80) != 128) break;
}
return i;

2022-08-24_221809

@BruceChenQAQ
Copy link
Collaborator Author

BruceChenQAQ commented Aug 24, 2022

Before: 65 seconds until chunks is fully loaded. Memory usage: 260MiB

2022-08-25_014816


After: 17 seconds until chunks is fully loaded. Memory usage: 138MiB

2022-08-25_013319

@BruceChenQAQ
Copy link
Collaborator Author

I removed the locks from Chunk.cs and ChunkColumn.cs and now they should still be thread safe. There may be some oversight, so if you find something that might be wrong, please point it out in the comments.
:)

@BruceChenQAQ BruceChenQAQ marked this pull request as ready for review August 24, 2022 17:59
@BruceChenQAQ
Copy link
Collaborator Author

BruceChenQAQ commented Aug 25, 2022

9.7 seconds until chunks is fully loaded. Memory usage: 73MiB

image


Before joining a server:
image

After joining a server:
image

@BruceChenQAQ
Copy link
Collaborator Author

BruceChenQAQ commented Aug 27, 2022

5.1 seconds until chunks fully loaded.

image

@BruceChenQAQ
Copy link
Collaborator Author

BruceChenQAQ commented Aug 28, 2022

2.8 seconds & 58MiB

Now using the AES-NI instruction set for greater throughput and lower CPU usage than the previous version using BouncyCastle.

On devices that do not support this instruction set (SSE2 and AES-NI), the AES implementation from the standard library will be used and parallel processing will be enabled.

public override int Read(byte[] buffer, int outOffset, int required)
{
    if (inStreamEnded)
        return 0;

    Span<byte> blockOutput = FastAes != null ? stackalloc byte[blockSize] : null;

    byte[] inputBuf = new byte[blockSize + required];
    Array.Copy(ReadStreamIV, inputBuf, blockSize);

    for (int readed = 0, curRead; readed < required; readed += curRead)
    {
        curRead = BaseStream.Read(inputBuf, blockSize + readed, required - readed);
        if (curRead == 0)
        {
            inStreamEnded = true;
            return readed;
        }

        int processEnd = readed + curRead;
        if (FastAes != null)
        {
            for (int idx = readed; idx < processEnd; idx++)
            {
                ReadOnlySpan<byte> blockInput = new(inputBuf, idx, blockSize);
                FastAes.EncryptEcb(blockInput, blockOutput);
                buffer[outOffset + idx] = (byte)(blockOutput[0] ^ inputBuf[idx + blockSize]);
            }
        }
        else
        {
            OrderablePartitioner<Tuple<int, int>> rangePartitioner = curRead <= 256 ?
                Partitioner.Create(readed, processEnd, 32) : Partitioner.Create(readed, processEnd);
            Parallel.ForEach(rangePartitioner, (range, loopState) =>
            {
                Span<byte> blockOutput = stackalloc byte[blockSize];
                for (int idx = range.Item1; idx < range.Item2; idx++)
                {
                    ReadOnlySpan<byte> blockInput = new(inputBuf, idx, blockSize);
                    Aes!.EncryptEcb(blockInput, blockOutput, PaddingMode.None);
                    buffer[outOffset + idx] = (byte)(blockOutput[0] ^ inputBuf[idx + blockSize]);
                }
            });
        }
    }

    Array.Copy(inputBuf, required, ReadStreamIV, 0, blockSize);

    return required;
}

image


Before joining a server:
image

After joining a server:
image

@BruceChenQAQ BruceChenQAQ mentioned this pull request Aug 28, 2022
4 tasks
@BruceChenQAQ BruceChenQAQ mentioned this pull request Aug 28, 2022
4 tasks
@milutinke
Copy link
Member

milutinke commented Sep 1, 2022

Can you rename the protocolversion to protocolVersion so it matches the master, so there is no conflicts?

@BruceChenQAQ
Copy link
Collaborator Author

Okay, renamed.

@ORelio
Copy link
Member

ORelio commented Sep 3, 2022

@BruceChenQAQ Thanks for your work on this. You seems to coordinate well with @milutinke so I'm adding you as well as maintainer so you can work more efficiently on the code and merge this PR when appropriate 👍

@milutinke
Copy link
Member

milutinke commented Sep 3, 2022

Pull from the master the web UI can't work with this much conflics, I'll do testing again on multiple servers, especially with terrain handling.
Btw, awesome job, this is going to improve MCC so much.

@BruceChenQAQ
Copy link
Collaborator Author

BruceChenQAQ commented Sep 4, 2022

Conflict resolution has been completed, I'll do some testing later.
There are still some issue found with the handling of MessageHeader in 1.19.2
Edit: Fixed.

@BruceChenQAQ
Copy link
Collaborator Author

BruceChenQAQ commented Sep 4, 2022

I tested terrain handling in an official server and a paper server respectively, and they worked fine.

@milutinke Let's merge it if all functions are working properly.

@milutinke
Copy link
Member

milutinke commented Sep 4, 2022

Is this Console Interactive bug?
image

Edit:
The only remaining things are the bug above and not being able to path-find when in 2x1 tunnel.
Everything else works.

@BruceChenQAQ
Copy link
Collaborator Author

BruceChenQAQ commented Sep 5, 2022

Is this Console Interactive bug?

Partially yes, this should be fixed in b26949e and breadbyte/ConsoleInteractive#14 and breadbyte/ConsoleInteractive#12.


Edit: The only remaining things are the bug above and not being able to path-find when in 2x1 tunnel. Everything else works.

I tested that it is pathfinding in the tunnel, could it be that the coordinates are wrong?
The coordinates of the Block row should be used, or the coordinates of the XYZ row should be rounded down (-42.1 -> -43, +43.9 -> +43)
Maybe it should be reminded in the documentation.

image

@milutinke
Copy link
Member

Is this Console Interactive bug?

Partially yes, this should be fixed in b26949e and breadbyte/ConsoleInteractive#14 and breadbyte/ConsoleInteractive#12.

Edit: The only remaining things are the bug above and not being able to path-find when in 2x1 tunnel. Everything else works.

I tested that it is pathfinding in the tunnel, could it be that the coordinates are wrong? The coordinates of the Block row should be used, or the coordinates of the XYZ row should be rounded down (-42.1 -> -43, +43.9 -> +43) Maybe it should be reminded in the documentation.

image

image

@BruceChenQAQ
Copy link
Collaborator Author

@milutinke Try using /move 35 71 -35

@milutinke
Copy link
Member

/move 35 71 -35

That works.
It's not really intuitive.

@BruceChenQAQ
Copy link
Collaborator Author

/move 35 71 -35

That works. It's not really intuitive.

Yes, I also encountered this problem when I started testing.
Because people are used to rounding to zero, rather than strictly rounding down.

@BruceChenQAQ
Copy link
Collaborator Author

Perhaps it would be better to upgrade the move command to accept floating point and relative coordinate representations.

@milutinke milutinke merged commit e56139a into MCCTeam:master Sep 5, 2022
@milutinke
Copy link
Member

Perhaps it would be better to upgrade the move command to accept floating point and relative coordinate representations.

The message appeared after the merge xD
That could be done in a separate PR.

@ORelio
Copy link
Member

ORelio commented Sep 5, 2022

Good job 🎉

@breadbyte
Copy link
Member

breadbyte commented Sep 5, 2022

Updated the ConsoleInteractive submodule as well, as it wasn't included in this PR (BruceChenQAQ added in fixes too)

[The recent build is failing because there seems to be a network issue with the github action runner, will try again later.]

@BruceChenQAQ BruceChenQAQ deleted the bugfix branch September 5, 2022 12:19
@BruceChenQAQ
Copy link
Collaborator Author

The message appeared after the merge xD
That could be done in a separate PR.

Implemented in #2154, it will now move to the exact coordinates entered.
:)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

a:bugfix Fixed a bug

Projects

None yet

4 participants