[Questions] Faster Methods?

08/29/2011 19:10 Spirited#1
Hey everyone.
So I've been working with pointers and I was wondering what's faster and if their are any alternative methods that might speed up my server.

1. Buffer.BlockCopy VS an unsafe for loop.
2. Encoding.ASCII.GetString VS an unsafe for loop with System.Convert.ToChar
3. Marshal.AllocHGlobal VS ??

I'm in class so I'll check back later.
Cyah.

Sincerely,
Fang
08/29/2011 19:21 BaussHacker#2
First question can be answered here:
Quote:
Originally Posted by CptSky View Post
Managed vs Unmanaged in C#... I know these things, but it will give a better idea of the reality and you'll see that the difference is so little that you can use what you want...

Tested on Windows XP x64, for a copy of 50'000'000 elements and tested 1000 times for precision. All values in ms.
TargetBuffer.BlockCopyArray.Copymemcpy (void*)memcpy (IntPtr)memcpy (void*, fixed byte*)memcpy (fixed byte*, fixed byte*)
x6431.54730.73430.20130.92234.81729.760
x8643.60641.20837.30937.62640.05137.704
Any28.42227.09725.85826.54230.53126.087

Results:
Allocating managed resources takes more times than unmanaged resources.
Byte[] = new Byte[], Byte* = (Byte*)Marshal.AllocHGlobal()

x64 is fastest than x86 on 64 bits OS.

Any CPU is fastest than x64 on 64 bits OS and probably on 32 bits OS (x86).

Array.Copy is fastest than Buffer.BlockCopy.

There is no difference between void* and IntPtr.

There is no difference between fixed pointer and pointer.

Native function memcpy is fastest than Array.Copy.
08/29/2011 19:35 Korvacs#3
Blockcopy's alternative should be memcpy, not a loop, pretty sure blockcopy and arraycopy both use memcpy within the methods.

For converting to a string you could do something like this within a for loop:

Code:
newstring += (char)*((char*)(lpBuffer + x);
Marshal.AllocHGlobal... i guess you could call LocalAlloc directly from kernel32, although i dont believe theres really much difference between the 2 methods, it just nicely wraps the call for you, possibly with some additional checks, i havent looked at the source.
08/29/2011 22:50 InfamousNoone#4
Quote:
Originally Posted by BaussHacker View Post
First question can be answered here:
Buffer.ArrayCopy and Array.Copy need to be JIT-compiled on their first invoke, so performance is lost there. Secondly, if you constantly need to fix-down a pointer (using a fixed statement), it'll obviously result in a slight loss of performance.

Generally speaking, and depending on what your using it for, malloc() (in msvcrt.dll) will do a better job than AllocHGlobal will (in kernel32.dll).

If you need a small (we're talking like, under 255-bytes) temporary pointer-variable until the end of your method, I'd use stackalloc.

You should look at string.ctor(sbyte* ptr, int offset, int length) btw
08/30/2011 00:50 Spirited#5
Quote:
Originally Posted by Korvacs View Post
Blockcopy's alternative should be memcpy, not a loop, pretty sure blockcopy and arraycopy both use memcpy within the methods.

For converting to a string you could do something like this within a for loop:

Code:
client.Username += ((char)*((char*)(ptr + 4 + i)));
Marshal.AllocHGlobal... i guess you could call LocalAlloc directly from kernel32, although i dont believe theres really much difference between the 2 methods, it just nicely wraps the call for you, possibly with some additional checks, i havent looked at the source.
Code:
newstring += (char)*((char*)(lpBuffer + x);
I tried it already. It returns: "慆湡杮g" instead of "Fang"

The only thing that I found that worked with unsafe code was
"client.Username += System.Convert.ToChar(*(ptr + 4 + i));"

I'm guessing it's slightly better (I hope) than Encoding.ASCII.

edit: looking at string.ctor(sbyte* ptr, int offset, int length) now..
08/30/2011 01:05 InfamousNoone#6
Quote:
Originally Posted by Fаng View Post
Code:
newstring += (char)*((char*)(lpBuffer + x);
I tried it already. It returns: "慆湡杮g" instead of "Fang"

The only thing that I found that worked with unsafe code was
"client.Username += System.Convert.ToChar(*(ptr + 4 + i));"

I'm guessing it's slightly better (I hope) than Encoding.ASCII.
A character in C# is 2-bytes (a wchar_t) in C++
A 'char' in C++ is a single-byte value (equivalent to a sbyte in C#)
The string send is a multi-byte string meaning, each character has a value of 1 byte. Therefore, by making a pointer type of C#'s 'char' type (2 bytes), you get these odd unicode characters.

Code:
string result = new string((sbyte*)ptr, offset, length);
08/30/2011 01:31 Spirited#7
Quote:
Originally Posted by InfamousNoone View Post
A character in C# is 2-bytes (a wchar_t) in C++
A 'char' in C++ is a single-byte value (equivalent to a sbyte in C#)
The string send is a multi-byte string meaning, each character has a value of 1 byte. Therefore, by making a pointer type of C#'s 'char' type (2 bytes), you get these odd unicode characters.

Code:
string result = new string((sbyte*)ptr, offset, length);
Just saw your post.
Thanks for the C++ lesson =p
Any other tips on pointers? I'd really like to use them more.
08/30/2011 06:26 InfamousNoone#8
Not really, I could give you an example on how I handle packets. For example,
Code:
    public unsafe struct MsgWalk
    {
        public static readonly short[] DeltaX = { +0, -1, -1, -1, +0, +1, +1, +1, +0 };
        public static readonly short[] DeltaY = { +1, +1, +0, -1, -1, -1, +0, +1, +0 };

        public short Size;
        public MsgTypeId Type;
        public int Direction;
        public int ObjectId;
        public int MovementId;
        public int TimeStamp;
        public int MapId;

        public void Init(int ObjectId, int Direction, int Id)
        {
            this.Size = (short)sizeof(MsgWalk);
            this.Type = MsgTypeId.Walk;
            this.Direction = Direction;
            this.ObjectId = ObjectId;
            this.MovementId = Id;
            this.TimeStamp = DBCore.GetTime();
        }

        public static void Process(User user, void* pMsg)
        {
            MsgWalk* msg = (MsgWalk*)pMsg;
            if (msg->ObjectId != user.Entity.ObjectId)
                return;

            msg->Direction %= 8;

            int x = user.Entity.X, y = user.Entity.Y;
            if (msg->MovementId == 9)
            {
                user.Socket.Disconnect("MsgWalk::Process steed walking not supported");
                return;
            }
            else
            {
                x += DeltaX[msg->Direction];
                y += DeltaY[msg->Direction];
            }

            user.SendRoom(msg, msg->Size);
            user.Move(x, y);
        }
    }
08/30/2011 07:09 Spirited#9
That's how i code packets too (kind of).
What do u think of a packet queue at the receive void?