From 7b60511d55589f981287e45bd5b3ed5e18cea903 Mon Sep 17 00:00:00 2001 From: aromaa Date: Wed, 3 Apr 2024 18:00:01 +0300 Subject: [PATCH 1/2] Improve FastParseCommand readability --- libs/server/Resp/RespCommand.cs | 286 +++++++------------------------- 1 file changed, 59 insertions(+), 227 deletions(-) diff --git a/libs/server/Resp/RespCommand.cs b/libs/server/Resp/RespCommand.cs index 105b6a9f8c..3a34ba86f0 100644 --- a/libs/server/Resp/RespCommand.cs +++ b/libs/server/Resp/RespCommand.cs @@ -204,12 +204,12 @@ private RespCommand FastParseCommand(out int count) count = ptr[1] - '1'; Debug.Assert(count is >= 0 and < 9); - var oldReadHead = readHead; - // Extract length of the first string header var length = ptr[5] - '0'; Debug.Assert(length is > 0 and <= 9); + var oldReadHead = readHead; + // Ensure that the complete command string is contained in the package. Otherwise exit early. // Include 10 bytes to account for array and command string headers, and terminator // 10 bytes = "*_\r\n$_\r\n" (8 bytes) + "\r\n" (2 bytes) at end of command name @@ -226,235 +226,67 @@ private RespCommand FastParseCommand(out int count) // // Only check against commands with the correct count and length. - // Note: Cases are encoded as 0x{count}{length} for readability. - byte hash = (byte)((count << 4) | length); - switch (hash) - { - case 0x04: - if (lastWord == MemoryMarshal.Read("\r\nPING\r\n"u8)) - { - return RespCommand.PING; - } - else if (lastWord == MemoryMarshal.Read("\r\nEXEC\r\n"u8)) - { - return RespCommand.EXEC; - } - break; - case 0x05: - if (lastWord == MemoryMarshal.Read("\nMULTI\r\n"u8)) - { - return RespCommand.MULTI; - } - break; - case 0x06: - if (lastWord == MemoryMarshal.Read("ASKING\r\n"u8)) - { - return RespCommand.ASKING; - } - break; - - case 0x07: - if (lastWord == MemoryMarshal.Read("ISCARD\r\n"u8) && ptr[8] == 'D') - { - return RespCommand.DISCARD; - } - else if (lastWord == MemoryMarshal.Read("NWATCH\r\n"u8) && ptr[8] == 'U') - { - return RespCommand.UNWATCH; - } - break; - - case 0x08: - if (lastWord == MemoryMarshal.Read("ADONLY\r\n"u8) && *(ushort*)(ptr + 8) == MemoryMarshal.Read("RE"u8)) - { - return RespCommand.READONLY; - } - break; - - case 0x09: - if (lastWord == MemoryMarshal.Read("DWRITE\r\n"u8) && *(uint*)(ptr + 8) == MemoryMarshal.Read("READ"u8)) - { - return RespCommand.READWRITE; - } - break; - - case 0x13: - if (lastWord == MemoryMarshal.Read("3\r\nGET\r\n"u8)) - { - return RespCommand.GET; - } - else if (lastWord == MemoryMarshal.Read("3\r\nDEL\r\n"u8)) - { - return RespCommand.DEL; - } - else if (lastWord == MemoryMarshal.Read("3\r\nTTL\r\n"u8)) - { - return RespCommand.TTL; - } - break; - - case 0x14: - if (lastWord == MemoryMarshal.Read("\r\nINCR\r\n"u8)) - { - return RespCommand.INCR; - } - else if (lastWord == MemoryMarshal.Read("\r\nPTTL\r\n"u8)) - { - return RespCommand.PTTL; - } - else if (lastWord == MemoryMarshal.Read("\r\nDECR\r\n"u8)) - { - return RespCommand.DECR; - } - break; - - case 0x16: - if (lastWord == MemoryMarshal.Read("EXISTS\r\n"u8)) - { - return RespCommand.EXISTS; - } - else if (lastWord == MemoryMarshal.Read("GETDEL\r\n"u8)) - { - return RespCommand.GETDEL; - } - break; - - case 0x17: - if (lastWord == MemoryMarshal.Read("ERSIST\r\n"u8) && ptr[8] == 'P') - { - return RespCommand.PERSIST; - } - else if (lastWord == MemoryMarshal.Read("FCOUNT\r\n"u8) && ptr[8] == 'P') - { - return RespCommand.PFCOUNT; - } - break; - - case 0x23: - if (lastWord == MemoryMarshal.Read("3\r\nSET\r\n"u8)) - { - return RespCommand.SET; - } - break; - - case 0x25: - if (lastWord == MemoryMarshal.Read("\nPFADD\r\n"u8)) - { - return RespCommand.PFADD; - } - break; - - case 0x26: - if (lastWord == MemoryMarshal.Read("INCRBY\r\n"u8)) - { - return RespCommand.INCRBY; - } - else if (lastWord == MemoryMarshal.Read("DECRBY\r\n"u8)) - { - return RespCommand.DECRBY; - } - else if (lastWord == MemoryMarshal.Read("RENAME\r\n"u8)) - { - return RespCommand.RENAME; - } - else if (lastWord == MemoryMarshal.Read("GETBIT\r\n"u8)) - { - return RespCommand.GETBIT; - } - else if (lastWord == MemoryMarshal.Read("APPEND\r\n"u8)) - { - return RespCommand.APPEND; - } - break; - - case 0x27: - if (lastWord == MemoryMarshal.Read("UBLISH\r\n"u8) && ptr[8] == 'P') - { - return RespCommand.PUBLISH; - } - - else if (lastWord == MemoryMarshal.Read("FMERGE\r\n"u8) && ptr[8] == 'P') - { - return RespCommand.PFMERGE; - } - break; - - case 0x35: - if (lastWord == MemoryMarshal.Read("\nSETEX\r\n"u8)) - { - return RespCommand.SETEX; - } - break; - - case 0x36: - if (lastWord == MemoryMarshal.Read("PSETEX\r\n"u8)) - { - return RespCommand.PSETEX; - } - else if (lastWord == MemoryMarshal.Read("SETBIT\r\n"u8)) - { - return RespCommand.SETBIT; - } - break; - - case 0x38: - if (lastWord == MemoryMarshal.Read("TRANGE\r\n"u8) && *(ushort*)(ptr + 8) == MemoryMarshal.Read("SE"u8)) - { - return RespCommand.SETRANGE; - } - else if (lastWord == MemoryMarshal.Read("TRANGE\r\n"u8) && *(ushort*)(ptr + 8) == MemoryMarshal.Read("GE"u8)) - { - return RespCommand.GETRANGE; - } - break; - } - // - // Fast path for commands with few, but variable, #arguments (command array size < 10) - // + return ((count << 4) | length) switch + { + // Commands without arguments + 4 when lastWord == MemoryMarshal.Read("\r\nPING\r\n"u8) => RespCommand.PING, + 4 when lastWord == MemoryMarshal.Read("\r\nEXEC\r\n"u8) => RespCommand.EXEC, + 5 when lastWord == MemoryMarshal.Read("\nMULTI\r\n"u8) => RespCommand.MULTI, + 6 when lastWord == MemoryMarshal.Read("ASKING\r\n"u8) => RespCommand.ASKING, + 7 when lastWord == MemoryMarshal.Read("ISCARD\r\n"u8) && ptr[8] == 'D' => RespCommand.DISCARD, + 7 when lastWord == MemoryMarshal.Read("NWATCH\r\n"u8) && ptr[8] == 'U' => RespCommand.UNWATCH, + 8 when lastWord == MemoryMarshal.Read("ADONLY\r\n"u8) && *(ushort*)(ptr + 8) == MemoryMarshal.Read("RE"u8) => RespCommand.READONLY, + 9 when lastWord == MemoryMarshal.Read("DWRITE\r\n"u8) && *(uint*)(ptr + 8) == MemoryMarshal.Read("READ"u8) => RespCommand.READWRITE, + + // Commands with fixed amount of arguments + (1 << 4) | 3 when lastWord == MemoryMarshal.Read("3\r\nGET\r\n"u8) => RespCommand.GET, + (1 << 4) | 3 when lastWord == MemoryMarshal.Read("3\r\nDEL\r\n"u8) => RespCommand.DEL, + (1 << 4) | 3 when lastWord == MemoryMarshal.Read("3\r\nTTL\r\n"u8) => RespCommand.TTL, + (1 << 4) | 4 when lastWord == MemoryMarshal.Read("\r\nINCR\r\n"u8) => RespCommand.INCR, + (1 << 4) | 4 when lastWord == MemoryMarshal.Read("\r\nPTTL\r\n"u8) => RespCommand.PTTL, + (1 << 4) | 4 when lastWord == MemoryMarshal.Read("\r\nDECR\r\n"u8) => RespCommand.DECR, + (1 << 4) | 4 when lastWord == MemoryMarshal.Read("EXISTS\r\n"u8) => RespCommand.EXISTS, + (1 << 4) | 6 when lastWord == MemoryMarshal.Read("GETDEL\r\n"u8) => RespCommand.GETDEL, + (1 << 4) | 7 when lastWord == MemoryMarshal.Read("ERSIST\r\n"u8) && ptr[8] == 'P' => RespCommand.PERSIST, + (1 << 4) | 7 when lastWord == MemoryMarshal.Read("PFCOUNT\r\n"u8) && ptr[8] == 'P' => RespCommand.PFCOUNT, + (2 << 4) | 3 when lastWord == MemoryMarshal.Read("3\r\nSET\r\n"u8) => RespCommand.SET, + (2 << 4) | 5 when lastWord == MemoryMarshal.Read("\nPFADD\r\n"u8) => RespCommand.PFADD, + (2 << 4) | 6 when lastWord == MemoryMarshal.Read("INCRBY\r\n"u8) => RespCommand.INCRBY, + (2 << 4) | 6 when lastWord == MemoryMarshal.Read("DECRBY\r\n"u8) => RespCommand.DECRBY, + (2 << 4) | 6 when lastWord == MemoryMarshal.Read("RENAME\r\n"u8) => RespCommand.RENAME, + (2 << 4) | 6 when lastWord == MemoryMarshal.Read("GETBIT\r\n"u8) => RespCommand.GETBIT, + (2 << 4) | 6 when lastWord == MemoryMarshal.Read("APPEND\r\n"u8) => RespCommand.APPEND, + (2 << 4) | 7 when lastWord == MemoryMarshal.Read("UBLISH\r\n"u8) && ptr[8] == 'P' => RespCommand.PUBLISH, + (2 << 4) | 7 when lastWord == MemoryMarshal.Read("FMERGE\r\n"u8) && ptr[8] == 'P' => RespCommand.PFMERGE, + (3 << 4) | 5 when lastWord == MemoryMarshal.Read("\nSETEX\r\n"u8) => RespCommand.SETEX, + (3 << 4) | 6 when lastWord == MemoryMarshal.Read("PSETEX\r\n"u8) => RespCommand.PSETEX, + (3 << 4) | 6 when lastWord == MemoryMarshal.Read("SETBIT\r\n"u8) => RespCommand.SETBIT, + (3 << 4) | 8 when lastWord == MemoryMarshal.Read("TRANGE\r\n"u8) && *(ushort*)(ptr + 8) == MemoryMarshal.Read("SE"u8) => RespCommand.SETRANGE, + (3 << 4) | 8 when lastWord == MemoryMarshal.Read("TRANGE\r\n"u8) && *(ushort*)(ptr + 8) == MemoryMarshal.Read("GE"u8) => RespCommand.GETRANGE, + + _ => ((length << 4) | count) switch + { + // Commands with dynamic amount of arguments + >= ((3 << 4) | 3) and <= ((3 << 4) | 6) when lastWord == MemoryMarshal.Read("3\r\nSET\r\n"u8) => RespCommand.SETEXNX, + >= ((6 << 4) | 0) and <= ((6 << 4) | 9) when lastWord == MemoryMarshal.Read("RUNTXP\r\n"u8) => RespCommand.RUNTXP, + >= ((6 << 4) | 2) and <= ((6 << 4) | 3) when lastWord == MemoryMarshal.Read("EXPIRE\r\n"u8) => RespCommand.EXPIRE, + >= ((6 << 4) | 2) and <= ((6 << 4) | 5) when lastWord == MemoryMarshal.Read("BITPOS\r\n"u8) => RespCommand.BITPOS, + >= ((7 << 4) | 2) and <= ((7 << 4) | 3) when lastWord == MemoryMarshal.Read("EXPIRE\r\n"u8) && ptr[8] == 'P' => RespCommand.PEXPIRE, + >= ((8 << 4) | 1) and <= ((8 << 4) | 4) when lastWord == MemoryMarshal.Read("TCOUNT\r\n"u8) && *(ushort*)(ptr + 8) == MemoryMarshal.Read("BI"u8) => RespCommand.BITCOUNT, + + _ => MatchedNone(this, oldReadHead) + } + }; - switch (length) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + static RespCommand MatchedNone(RespServerSession session, int oldReadHead) { - case 3: - if (((count >= 3) && (count <= 6)) && lastWord == MemoryMarshal.Read("3\r\nSET\r\n"u8)) - { - return RespCommand.SETEXNX; - } - break; - - case 6: - if (lastWord == MemoryMarshal.Read("RUNTXP\r\n"u8)) - { - return RespCommand.RUNTXP; - } - else if (((count == 2) || (count == 3)) && lastWord == MemoryMarshal.Read("EXPIRE\r\n"u8)) - { - return RespCommand.EXPIRE; - } - else if ((count > 1 && count < 6) && lastWord == MemoryMarshal.Read("BITPOS\r\n"u8)) - { - return RespCommand.BITPOS; - } - break; - - case 7: - - if (((count == 2) || (count == 3)) && lastWord == MemoryMarshal.Read("EXPIRE\r\n"u8) && ptr[8] == 'P') - { - return RespCommand.PEXPIRE; - } - break; - - case 8: - if ((count > 0 && count < 5) && lastWord == MemoryMarshal.Read("TCOUNT\r\n"u8) /* "BITCOUNT" */ && *(ushort*)(ptr + 8) == MemoryMarshal.Read("BI"u8)) - { - return RespCommand.BITCOUNT; - } - break; - } + // Backup the read head, if we didn't find a command and need to continue in the more expensive parsing loop + session.readHead = oldReadHead; - // Backup the read head, if we didn't find a command and need to continue in the more expensive parsing loop - readHead = oldReadHead; + return RespCommand.NONE; + } } } else From c17af39af7b3e173e67964aa3be8cbfc75a054f8 Mon Sep 17 00:00:00 2001 From: aromaa Date: Thu, 4 Apr 2024 02:05:04 +0300 Subject: [PATCH 2/2] PR feedback --- libs/server/Resp/RespCommand.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libs/server/Resp/RespCommand.cs b/libs/server/Resp/RespCommand.cs index 3a34ba86f0..9d1180fad3 100644 --- a/libs/server/Resp/RespCommand.cs +++ b/libs/server/Resp/RespCommand.cs @@ -239,7 +239,7 @@ private RespCommand FastParseCommand(out int count) 8 when lastWord == MemoryMarshal.Read("ADONLY\r\n"u8) && *(ushort*)(ptr + 8) == MemoryMarshal.Read("RE"u8) => RespCommand.READONLY, 9 when lastWord == MemoryMarshal.Read("DWRITE\r\n"u8) && *(uint*)(ptr + 8) == MemoryMarshal.Read("READ"u8) => RespCommand.READWRITE, - // Commands with fixed amount of arguments + // Commands with fixed number of arguments (1 << 4) | 3 when lastWord == MemoryMarshal.Read("3\r\nGET\r\n"u8) => RespCommand.GET, (1 << 4) | 3 when lastWord == MemoryMarshal.Read("3\r\nDEL\r\n"u8) => RespCommand.DEL, (1 << 4) | 3 when lastWord == MemoryMarshal.Read("3\r\nTTL\r\n"u8) => RespCommand.TTL, @@ -267,7 +267,7 @@ private RespCommand FastParseCommand(out int count) _ => ((length << 4) | count) switch { - // Commands with dynamic amount of arguments + // Commands with dynamic number of arguments >= ((3 << 4) | 3) and <= ((3 << 4) | 6) when lastWord == MemoryMarshal.Read("3\r\nSET\r\n"u8) => RespCommand.SETEXNX, >= ((6 << 4) | 0) and <= ((6 << 4) | 9) when lastWord == MemoryMarshal.Read("RUNTXP\r\n"u8) => RespCommand.RUNTXP, >= ((6 << 4) | 2) and <= ((6 << 4) | 3) when lastWord == MemoryMarshal.Read("EXPIRE\r\n"u8) => RespCommand.EXPIRE,