📜 âŹ†ïž âŹ‡ïž

Ultimate performance: C #

performance I will share 30 practices for maximum performance of applications that require it. Then, I will tell you how I applied them to a commercial product and achieved unprecedented results!
The application was written in C # for the Windows platform, working with Microsoft SQL Server. No profilers - the content is based on an understanding of the work of various technologies, so many topics will be useful for other platforms and programming languages.

Foreword


It all started back in 2008 - then I started working on the tasks of comparing and replicating relational databases. These types of applications primarily require high quality performance, because data loss or corruption can be disastrous. Secondly, database sizes can reach hundreds and thousands of gigabytes, so application performance is required.
The main focus was on SQL Server, and since Microsoft had long abandoned the normal support for the ODBC driver and provided full functionality only in the ADO .NET provider, the application needed to be written in one of the languages ​​for the .NET Framework. Of course the combination of High Performance Computing and the .NET platform at least brings a smile, but do not rush to conclusions.
When the product was ready, the results exceeded all expectations, and the performance was several times higher than the best solutions on the market. In this article I want to share with you the basic principles that should be followed when writing high-performance applications.

Principles of optimization


All principles are grouped into categories, and their description may contain statements without explanation, but they are all based on facts and research, which can be easily found on the Internet.
Each of the items contains brief information that displays the main essence. Some details may not be covered due to the saturation of the material, and for each of the items you can write a separate article. Therefore, I strongly recommend that you familiarize yourself with the details of each item from any available sources, before putting them into practice. Incorrect understanding of the material presented and gaps in knowledge can lead at least to a deterioration in the performance of your application.
All code examples are trivial, and demonstrate only the basic essence, and do not solve a specific problem.

Start with ...
')
1. Good algorithm.
First of all, for the problem it is necessary to choose the best algorithm for its solution. Only after that you can engage in optimization. If the algorithm is chosen incorrectly, no optimization will help.
In-depth knowledge of the work of various technologies is the basis of all optimization techniques. And this knowledge helps in choosing an algorithm from several possible.

2. Action Plan.
If you clearly formulate all the tasks that the algorithm performs, firstly, it will be easier for you to understand and implement the algorithm, and secondly, you will be able to construct a graph of actions - this way you will know the exact order of their execution and what actions you can perform in parallel.

3. Micro-delays.
There is a certain operation, the execution time of which takes F seconds. If you reduce this time by just 1 microsecond, then over 100 million iterations you will get a gain of 100 seconds! Such frequent operations are the first candidate for optimization.

Code branching

4. Do not use “if” when everything is known in advance.
Let's start with a simple example of a class in which there is a public method, and its internal implementation:
code example
class Example
{
    public void Print(String msg)
    {
        if (msg == null)
            throw new ArgumentNullException();
        PrintInternal(msg);
    }

    private void PrintInternal(String msg)
    {
        if (msg == null)
            throw new ArgumentNullException();
        ...
    }
}


. « », «if» – , . «PrintInternal» «Print», . , , . - YAGNI – , ?
:
void PrintValues(IDataReader dataReader)
{
    while (dataReader.Read())
    {
        for (int i = 0; i < dataReader.FieldCount; i++)
        {
            object value = dataReader.GetValue(i);
            PrintValue(value);
        }
    }
}

void PrintValue(object value)
{
    if (value is int)
        ...
    else if (value is long)
        ...
    else if (value is String)
        ...
    else if ...
}


IDataReader . , «Print», ? , GetSchemaTable. :
delegate void PrintMethod(object value);

void PrintValues(IDataReader dataReader)
{
    PrintMethod[] printers = new PrintMethod[dataReader.FieldCount];
    for (int i = 0; i < printers.Length; i++)
    {
        Type fieldType = dataReader.GetFieldType(i);
        if (fieldType == typeof(int))
            printers[i] = PrintInt;
        else if (fieldType == typeof(long))
            printers[i] = PrintLong;
        else ...
    }

    while (dataReader.Read())
    {
        for (int i = 0; i < printers.Length; i++)
        {
            object value = dataReader.GetValue(i);
            printers[i](value);
        }
    }
}


, , , . , - , , . , Boxing / Unboxing , , .

5. «if».
– « », . – , .
Instruction Scheduling – , . :
int x = a * b;
int y = c ^ d;
int z = x + y;
int w = z - y;

, , . , , . , .
, «if». , , , , , Branch prediction.
: «if»? -1, 0, 1, : int CompareBytes(byte a, byte b)
int CompareBytes(byte a, byte b)
{
    unchecked
    {
        int ab = (int)a - (int)b;
        int ba = (int)b - (int)a;
        return (ab >> 31) | (int)((uint)ba >> 31);
    }
}


, «if». , «if» ( ). , .



6. .


7. .
– «if». , , , - (, Copy N Paste).
– 4-
int power4(int v)
{
    int r = 1;
    r *= v;
    r *= v;
    r *= v;
    r *= v;
    return r;
}


, - N K , N K. .. N/K , K . , , - K .
int power(int v, int N)
{
    int r = 1;
    // Reduce the number of iterations by 4 times.
    for (int loops = N >> 2; loops > 0; loops--)
    {
        r *= v;
        r *= v;
        r *= v;
        r *= v;
    }
    // Process the tail.
    for (int loops = N & 3; loops > 0; loops--)
    {
        r *= v;
    }
    return r;
} 


, . «Loop unwinding» Wikipedia.



8. .
, , – . , ( Monitor lock), (, ManualResetEvent), Interlocked, volatile. , . .
«volatile», .. . , , -, , . – . , stack’ – .. . , . , . , – , , , , . .. .
«volatile» – , , , – . . , «» , . , Interlocked, .
«Volatile» , , , . .
class AsyncWorker
{
    volatile int progress;

    public int Progress
    {
        get
        {
            return progress;
        }
    }

    void DoWork()
    {
        for (this.progress = 0; this.progress < Count; this.progress++)
        {
            ...
        }
    }
}


, UI. «progress» «volatile», , 0. , , .

9. .
ThreadPool, , , . , , – ! , .
2 , 2. 3 :

, , – , . . , . , , , . , , «» , .
, , . , - , . , .

10. .
N , , – . , . F , S . , F S, S F. , , , ( ) , (N x F <= N x S). , , .
K «», , , , K . 1000 , 1000 . , ? , 16 , N x F / 16. F S, N x F / 16 > N x S / 1000. .. , 16 . :
void DoWork(ItemProvider provider)
{
    Batch batch = NextFreeBatch();

    while (true)
    {
        Item item = provider.GetNextItem();
        batch.AddItem(item);

        if (batch.IsFull)
        {
            EnqueueBatchForAsyncProcessing(batch);
            batch = NextFreeBatch();
        }
    }
}

void EnqueueBatchForAsyncProcessing(Batch batch)
{
    lock (this.batchQueue)
        this.batchQueue.Enqueue(batch);
}

void ThreadRoutine()
{
    while (true)
    {
        Batch batch = DequeueBatchForAsyncProcessing();

        foreach (Item item in batch)
            ProcessItem(item);

        RecycleBatch(batch);
    }
}

Batch DequeueBatchForAsyncProcessing()
{
    lock (this.batchQueue)
        return this.batchQueue.Dequeue();
}


, – . , .


11. .
, «» , ? ( ), . «», – , . .. , , , , , . , , « ».
– , - . , , .
, /. Process.ProcessorAffinity ( ), ProcessThread.ProcessorAffinity.
– Thread.Priority. , . – Process.PriorityClass.



12. .
, , , . , / «», 512 . «», . Windows, – 4 KiB. , 1 , . / .
, 2 KiB , 1 KiB, 4 KiB , 4 KiB . , . , 2 KiB 3 KiB, KiB , KiB .
. - , , – . , , RAID , .
/, , . FileStream, - 4 KiB. , FileStream bufferSize.
[DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true, EntryPoint = "GetDiskFreeSpaceW")]
static extern bool GetDiskFreeSpace(string lpRootPathName, out int lpSectorsPerCluster, out int lpBytesPerSector, out int lpNumberOfFreeClusters, out int lpTotalNumberOfClusters);

// Each partition has its own cluster size.
public static int GetClusterSize(string path) {

	int sectorsPerCluster;
	int bytesPerSector;
	int freeClusters;
	int totalClusters;
	int clusterSize = 0;
	if (GetDiskFreeSpace(Path.GetPathRoot(path), out sectorsPerCluster, out bytesPerSector, out freeClusters, out totalClusters))
		clusterSize = bytesPerSector * sectorsPerCluster;
	return clusterSize;
}


MTU (Maximum Transmission Unit) . .

13. .
, Stream 
 ? , . , FileStream , , . , . , ? ( HDD -.) , , . , .
Stream, , . , (. №9). , . Stream – . Stream. -, ! , . , .
, (, NetworkStream).

14. .
( №13), . , . , . Stream.
.

15. .
, , – . , . , IOPS (Input-Output Operations per Second) – , . , :

, ( ) . , .
, , - LZ (Lempel-Ziv), ( ).



16. , .
, :

, reference types , «ref».

17. .
.NET Framework, , . , – . ( ), , ( main). , , , , .
, «null». , , .
– , .

18. .
– , . , , ( byte[], int[], .). , – . , new , .
stack. stack’ (. CreateThread), , , , .
, , , , Array.Resize. , . , (. 26).
, «byte» «int» :
internal static class ByteArrayReinterpreter
{
    private static IntPtr intArrayTypePtr;

    unsafe static ByteArrayReinterpreter()
    {
        int[] intArray = new int[1];
        fixed (int* ptr = intArray)
            intArrayTypePtr = *(IntPtr*)(((byte*)ptr) - (IntPtr.Size * 2));
        intArray = null;
    }

    public static unsafe int[] AsIntArray(this byte[] array)
    {
        if ((array.Length & 3) != 0)
            throw new ArgumentException("The array length must be multiple of 4.");

        object arrayObj = array;
        int newLength = array.Length >> 2;

        fixed (byte* ptr = array)
        {
            *(IntPtr*)(((byte*)ptr) - (IntPtr.Size * 2)) = intArrayTypePtr;
            *(int*)(((byte*)ptr) - IntPtr.Size) = newLength;
        }

        return (int[])arrayObj;
    }
}

– , , .




19. «unsafe» .
«unsafe» C# , . – int Guid, , , .. unsafe , .

20. .
, . «bool»? 4 32- 8 – 64-. «bool» – . , «byte», «ushort», «uint», «ulong», (. «enum»).
, . «int», 0 10? «byte» .
, , 20 ( 0 1048575) «int» (32 ), 12 , . , . , , ( , ).
int field;
// Decouple numeric and flags
const int NumericMask = ((1 << 20) - 1);
int numeric = field & NumericMask;
MyFlags flags = (MyFlags)(field >> 20);
// Unite numeric and flags back
field = ((int)flags << 20) | numeric;


, . , « » .
, , , , .

21. .
:
struct S
{
    byte a;
    short b;
    short c;
}

5 . , S[], - «b». , «new» , 0x100000 – , , , , 4 8. «b» 0x100001, – 0x100006, – 0x10000B, .. , – « ». x86 x86-64 ( 4 8 ) , .
padding – , . 4 , :
struct S
{
    // offset: 0, size: 4
    byte a;
    byte __padding_byte_1;
    byte __padding_byte_2;
    byte __padding_byte_3;
    // offset: 4, size: 4
    short b;
    byte __padding_byte_4;
    byte __padding_byte_5;
    // offset: 8, size: 4
    short c;
    byte __padding_byte_6;
    byte __padding_byte_7;
    // total size: 12
}


, .NET Framework StructLayoutAttribute. , – , , .
. , , . , . , cache lines – (, 32 ), ( ). «S» 5 , , , 6 ( - 32 ; , , - ). -, – . -, , . , , . «PInvoke for GetLogicalProcessorInformation» StackOverflow .
, , . , . , 4 KiB, 8 KiB . « »? , Windows ? .

22. x86-64.
64- , 64- RAX, RSP, .. , , «long» «int». – ( ), . – «int» 8- , .
Guid. 11 (int, 2 short, 8 byte), 16 . , Equals :
Equals
bool Equals(Guid g)
{
    return g._a == this._a && g._b == this._b && g._c == this._c && g._d == this._d
         && g._e == this._e && g._f == this._f && g._g == this._g && g._h == this._h
         && g._i == this._i && g._j == this._j && g._k == this._k;
}


11 , 4-, «unsafe»:
unsafe bool Equals(Guid g)
{
    fixed (Guid* pg = &this)
    {
        int* p1 = (int*)pg;
        int* p2 = (int*)&g;
        return p1[0] == p2[0] && p1[1] == p2[1] && p1[2] == p2[2] && p1[3] == p2[3];
    }
}


x86-64, :
x64
unsafe bool Equals(Guid g)
{
    fixed (Guid* pg = &this)
    {
        long* p1 = (long*)pg;
        long* p2 = (long*)&g;
        return p1[0] == p2[0] && p1[1] == p2[1];
    }
}


11 – -, !

23. .
, , , . , , , . .
x86 CALL RET ( «return»). CALL , RET – , CALL. .. JMP ( «jump») – . , , CALL stack ( IP) JMP, RET stack’ JMP .
, - . , stack, . . ( ), stack’, .
, :
:

1. , Guid :
bool CompareGuids(Guid g1, Guid g2)

, Guid 16 , stack 32 , g1 g2. , :
bool CompareGuids(ref Guid g1, ref Guid g2)

, , , 4 32- , 8 64- . , , , stack ( ).
2. , , , :
int Multiply(int a, int b)
{
    return a * b;
}

void Foo()
{
    int a = 4;
    int b = 5;
    int r = Multiply(a, b);
}


Foo Multiply, a b . , , ! ? №3.
, C++ inline, , . , .NET Framework 4.5 - inline ( inline — ). , assembler, . – Copy Paste.
– , .

24. .
, . . :
class X
{
    virtual void A();
    virtual void B();
}

class Y : X
{
    override void B();
}


: X, Y, X. , X : X.A X.B, Y X.A Y.B. ( X Y), .
– , IntPtr[]. , CALL, , – , .
, . – «sealed» . «sealed» , , -. X Y:
sealed class Y : X
{
    override void B();
}

void Slow(X x)
{
    x.B();
}

void Fast(Y y)
{
    y.B();
}


«Y» , , «B» «Y». Slow «X», «B» «Y» , «X» .
, Fast , «Y» , , «B». , , «B» «Y», . , , , «B» . «B», .



25. Reverse engineering.
, . - ...? , SqlDataReader GetValue «xml» String XmlReader. , «xml» . – , StringComparison.Oridnal . , SqlDataReader, XML . , , . , SqlDataReader ( ), «xml» «varbinary». , , . , , XML , , , ( ) XmlReader , XML.

26. .
- 3D , ( ) , , , , . .
3D 3D . , : , 3D . , - .
. - ? , . , - , – . Full HD (1920x1080) 32- – 8 MiB! , . , , ( 100 ). , FPS .
.NET Framework? , , . , . , – new. 0 null, 0. , – , , . , , – . .. .NET , . , ?
GlobalAlloc ( GMEM_ZEROINIT) GC.AddMemoryPressure, , 0 . , , , , .



27. «Format» «ToString».
String.Format, . String.Concat StringBuilder – . , “{0}” .
, ToString. , DML (INSERT/UPDATE/DELETE; ) SQL Server , - String. 3 !

28. .
T-SQL, . identifier , . Hashset. , , :
HashSet<string> keywords;
String tokenText;
bool isKeyword = keywords.Contains(tokenText.ToUpperInvariant());

:

. «keywords» Hashset<int> , -. , T-SQL String.GetHashCode . , 32- . . T-SQL, . , , , , .. ASCII. .NET UCS2/UTF16 , Unicode 127 code points ASCII, ToUpper ASCII, - , «keyword»!
bool IsKeyword(String tokenText)
{
    int hashCode = 0;

    for (int i = 0; i < tokenText.Length; i++)
    {
        int c = (int)tokenText[i];

        // check upper bound
        if (c > 'z')
            return false;

        // to upper case for Latin letters
        if (c >= 'a')
            c ^= 0x20;

        // a keyword must be of Latin letters, numbers, and underscore
        if ( ! ((c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || c == '_'))
            return false;

        // update hash code
        hashCode = hashCode <op> c;
    }

    return keywords.Contain(hashCode);
}


, ToUpper code points, , . , .

29. !
- . – - . – ? , , , , .
: T-SQL UI . : , , . , , 3 : , , . :

, . , : , , .

30. .
, , . – , :
class Multiplier
{
    public int a, b;
    public int Multiply() { return a * b; }
}

-, «Multiply» , . -, . , , , . , , , .
, , «». , , , , - .


, , ( , ) , 5 . , , – . , , .


. , , ( ) . : staging , production; CMS , – , , , ; ; ..
:
  1. , (, , , ..; database schema)
  2. , ( , ; database data)

, : , – . , , .


6 :
  1. . , «» , , .
  2. . , , , .
  3. . , Customers Customers , Products – Products, . .
  4. . , – . 4 :
    ‱ ( )
    ‱
    ‱ ( - )
    ‱ ( )
    ( ), , .
  5. , . , , . ( 10 , 5 , 50 , ..)
  6. . , SQL , . , .



, (, UI back-end’, « »). , .
:

2 3 22 , .
  %
(, )2464
(, )1628

, , - «Adventure Works», 185 MiB , 761 .
   %
– (, )
8 99.9% – , , «» . , 75% SqlDataReader, 25% – .
102.24.5
, – (, ; , MiB)6
125 MiB
1.8
98 MiB
3.3
-
, (, ; , MiB)
, . , .
12
36 MiB
2.2
24 MiB
5.4
-
T-SQL – , (, ; , MiB)
2 98 MiB 187 MiB – SSD
21
245 MiB
2
187 MiB
10.5
-
T-SQL , (, )
T-SQL , SQL Server. , SQL SqlCommand .
2
27
1
26
1.7

, , , . , , ( QueryPerformanceCounter). .
: « ? , ?». , , . QA, , , . .. , .
P.S. , , , .


, . , , .NET.
.NET , C++. , C++ Assembler 1.5 2.5 C#. , unmanaged DLL. , C# .
, . , , - . , .NET, , , . , , .
, , , . , , , , .



, :

:




, 28 ( ), «». IEqualityComparer HashSet . , . : SQL Server . , «select» «SELECT» «Select».
GetHashCode. , .. HashSet , Equals. «select» «SELECT». , String.GetHashCode . — String.ToUpperInvariant . , — StringComparer OrdinalIgnoreCase. GetHashCode , .
Equals. . . IEqualityComparer StringComparer.OrdinalIgnoreCase, «select» «SELECT» . , IEqualityComparer HashSet StringComparer.OrdinalIgnoreCase .
?
1) GetHashCode. — , : () - ToUpperInvariant , () - CompareInfo.GetSortKey, SortKey , (? Unicode Collation Algoritm).
2) Equals. - ToUpperInvariant, ( «OrdinalIgnoreCase», «IgnoreCase» , «Ordinal» — ).
ToUpperInvariant?
‱ ToUpperInvariant , String .
‱ ToUpperInvariant , (2) ( ) (1) ( SortKey). .. 1 3-, ToUpper ( Invariant) ( Case Mappings). , .
‱ ( ).
?
‱ , . .. «1», «», (. 26 — ).
‱ , Case Mappings Unicode.
‱ ToUpperCase — : ToUpper, GetHashCode.
‱ , . Int32 .
‱ .
‱ (. 23 — , 24 — )

:
1) HashSet + ToUpperInvariant
2) HashSet + StringComparer.OrdinalIgnoreCase IEqualityComparer
3) — , .
program code
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Runtime.InteropServices;

namespace ConsoleApplication1
{
    partial class Program
    {
        #region Keywords 1 - ToUpperInvariant

        static HashSet<String> keywords1;

        static void InitKeywords1()
        {
            keywords1 = new HashSet<string>();
            keywords1.Add("ABSOLUTE");
            keywords1.Add("ACTION");
            keywords1.Add("ADA");
            keywords1.Add("ADD");
            keywords1.Add("ADMIN");
            keywords1.Add("AFTER");
            keywords1.Add("AGGREGATE");
            keywords1.Add("ALIAS");
            keywords1.Add("ALL");
            keywords1.Add("ALLOCATE");
            keywords1.Add("ALTER");
            keywords1.Add("AND");
            keywords1.Add("ANY");
            keywords1.Add("ARE");
            keywords1.Add("ARRAY");
            keywords1.Add("AS");
            keywords1.Add("ASC");
            keywords1.Add("ASENSITIVE");
            keywords1.Add("ASSERTION");
            keywords1.Add("ASYMMETRIC");
            keywords1.Add("AT");
            keywords1.Add("ATOMIC");
            keywords1.Add("AUTHORIZATION");
            keywords1.Add("AVG");
            keywords1.Add("BACKUP");
            keywords1.Add("BEFORE");
            keywords1.Add("BEGIN");
            keywords1.Add("BETWEEN");
            keywords1.Add("BIGINT");
            keywords1.Add("BINARY");
            keywords1.Add("BIT");
            keywords1.Add("BIT_LENGTH");
            keywords1.Add("BLOB");
            keywords1.Add("BOOLEAN");
            keywords1.Add("BOTH");
            keywords1.Add("BREADTH");
            keywords1.Add("BREAK");
            keywords1.Add("BROWSE");
            keywords1.Add("BULK");
            keywords1.Add("BY");
            keywords1.Add("CALL");
            keywords1.Add("CALLED");
            keywords1.Add("CARDINALITY");
            keywords1.Add("CASCADE");
            keywords1.Add("CASCADED");
            keywords1.Add("CASE");
            keywords1.Add("CAST");
            keywords1.Add("CATALOG");
            keywords1.Add("CHAR");
            keywords1.Add("CHAR_LENGTH");
            keywords1.Add("CHARACTER");
            keywords1.Add("CHARACTER_LENGTH");
            keywords1.Add("CHECK");
            keywords1.Add("CHECKPOINT");
            keywords1.Add("CLASS");
            keywords1.Add("CLOB");
            keywords1.Add("CLOSE");
            keywords1.Add("CLUSTERED");
            keywords1.Add("COALESCE");
            keywords1.Add("COLLATE");
            keywords1.Add("COLLATION");
            keywords1.Add("COLLECT");
            keywords1.Add("COLUMN");
            keywords1.Add("COMMIT");
            keywords1.Add("COMPLETION");
            keywords1.Add("COMPUTE");
            keywords1.Add("CONDITION");
            keywords1.Add("CONNECT");
            keywords1.Add("CONNECTION");
            keywords1.Add("CONSTRAINT");
            keywords1.Add("CONSTRAINTS");
            keywords1.Add("CONSTRUCTOR");
            keywords1.Add("CONTAINS");
            keywords1.Add("CONTAINSTABLE");
            keywords1.Add("CONTINUE");
            keywords1.Add("CONVERT");
            keywords1.Add("CORR");
            keywords1.Add("CORRESPONDING");
            keywords1.Add("COUNT");
            keywords1.Add("COVAR_POP");
            keywords1.Add("COVAR_SAMP");
            keywords1.Add("CREATE");
            keywords1.Add("CROSS");
            keywords1.Add("CUBE");
            keywords1.Add("CUME_DIST");
            keywords1.Add("CURRENT");
            keywords1.Add("CURRENT_CATALOG");
            keywords1.Add("CURRENT_DATE");
            keywords1.Add("CURRENT_DEFAULT_TRANSFORM_GROUP");
            keywords1.Add("CURRENT_PATH");
            keywords1.Add("CURRENT_ROLE");
            keywords1.Add("CURRENT_SCHEMA");
            keywords1.Add("CURRENT_TIME");
            keywords1.Add("CURRENT_TIMESTAMP");
            keywords1.Add("CURRENT_TRANSFORM_GROUP_FOR_TYPE");
            keywords1.Add("CURRENT_USER");
            keywords1.Add("CURSOR");
            keywords1.Add("CYCLE");
            keywords1.Add("DATA");
            keywords1.Add("DATABASE");
            keywords1.Add("DATE");
            keywords1.Add("DATETIME");
            keywords1.Add("DATETIME2");
            keywords1.Add("DATETIMEOFFSET");
            keywords1.Add("DAY");
            keywords1.Add("DBCC");
            keywords1.Add("DEALLOCATE");
            keywords1.Add("DEC");
            keywords1.Add("DECIMAL");
            keywords1.Add("DECLARE");
            keywords1.Add("DEFAULT");
            keywords1.Add("DEFERRABLE");
            keywords1.Add("DEFERRED");
            keywords1.Add("DELETE");
            keywords1.Add("DENY");
            keywords1.Add("DEPTH");
            keywords1.Add("DEREF");
            keywords1.Add("DESC");
            keywords1.Add("DESCRIBE");
            keywords1.Add("DESCRIPTOR");
            keywords1.Add("DESTROY");
            keywords1.Add("DESTRUCTOR");
            keywords1.Add("DETERMINISTIC");
            keywords1.Add("DIAGNOSTICS");
            keywords1.Add("DICTIONARY");
            keywords1.Add("DISCONNECT");
            keywords1.Add("DISK");
            keywords1.Add("DISTINCT");
            keywords1.Add("DISTRIBUTED");
            keywords1.Add("DOMAIN");
            keywords1.Add("DOUBLE");
            keywords1.Add("DROP");
            keywords1.Add("DUMP");
            keywords1.Add("DYNAMIC");
            keywords1.Add("EACH");
            keywords1.Add("ELEMENT");
            keywords1.Add("ELSE");
            keywords1.Add("END");
            keywords1.Add("END-EXEC");
            keywords1.Add("EQUALS");
            keywords1.Add("ERRLVL");
            keywords1.Add("ESCAPE");
            keywords1.Add("EVERY");
            keywords1.Add("EXCEPT");
            keywords1.Add("EXCEPTION");
            keywords1.Add("EXEC");
            keywords1.Add("EXECUTE");
            keywords1.Add("EXISTS");
            keywords1.Add("EXIT");
            keywords1.Add("EXTERNAL");
            keywords1.Add("EXTRACT");
            keywords1.Add("FALSE");
            keywords1.Add("FETCH");
            keywords1.Add("FILE");
            keywords1.Add("FILLFACTOR");
            keywords1.Add("FILTER");
            keywords1.Add("FIRST");
            keywords1.Add("FLOAT");
            keywords1.Add("FOR");
            keywords1.Add("FOREIGN");
            keywords1.Add("FORTRAN");
            keywords1.Add("FOUND");
            keywords1.Add("FREE");
            keywords1.Add("FREETEXT");
            keywords1.Add("FREETEXTTABLE");
            keywords1.Add("FROM");
            keywords1.Add("FULL");
            keywords1.Add("FULLTEXTTABLE");
            keywords1.Add("FUNCTION");
            keywords1.Add("FUSION");
            keywords1.Add("GENERAL");
            keywords1.Add("GEOGRAPHY");
            keywords1.Add("GEOMETRY");
            keywords1.Add("GET");
            keywords1.Add("GLOBAL");
            keywords1.Add("GO");
            keywords1.Add("GOTO");
            keywords1.Add("GRANT");
            keywords1.Add("GROUP");
            keywords1.Add("GROUPING");
            keywords1.Add("HAVING");
            keywords1.Add("HIERACHYID");
            keywords1.Add("HOLD");
            keywords1.Add("HOLDLOCK");
            keywords1.Add("HOST");
            keywords1.Add("HOUR");
            keywords1.Add("IDENTITY");
            keywords1.Add("IDENTITY_INSERT");
            keywords1.Add("IDENTITYCOL");
            keywords1.Add("IF");
            keywords1.Add("IGNORE");
            keywords1.Add("IMAGE");
            keywords1.Add("IMMEDIATE");
            keywords1.Add("IN");
            keywords1.Add("INCLUDE");
            keywords1.Add("INDEX");
            keywords1.Add("INDICATOR");
            keywords1.Add("INITIALIZE");
            keywords1.Add("INITIALLY");
            keywords1.Add("INNER");
            keywords1.Add("INOUT");
            keywords1.Add("INPUT");
            keywords1.Add("INSENSITIVE");
            keywords1.Add("INSERT");
            keywords1.Add("INT");
            keywords1.Add("INTEGER");
            keywords1.Add("INTERSECT");
            keywords1.Add("INTERSECTION");
            keywords1.Add("INTERVAL");
            keywords1.Add("INTO");
            keywords1.Add("IS");
            keywords1.Add("ISOLATION");
            keywords1.Add("ITERATE");
            keywords1.Add("JOIN");
            keywords1.Add("KEY");
            keywords1.Add("KILL");
            keywords1.Add("LANGUAGE");
            keywords1.Add("LARGE");
            keywords1.Add("LAST");
            keywords1.Add("LATERAL");
            keywords1.Add("LEADING");
            keywords1.Add("LEFT");
            keywords1.Add("LESS");
            keywords1.Add("LEVEL");
            keywords1.Add("LIKE");
            keywords1.Add("LIKE_REGEX");
            keywords1.Add("LIMIT");
            keywords1.Add("LINENO");
            keywords1.Add("LN");
            keywords1.Add("LOAD");
            keywords1.Add("LOCAL");
            keywords1.Add("LOCALTIME");
            keywords1.Add("LOCALTIMESTAMP");
            keywords1.Add("LOCATOR");
            keywords1.Add("LOWER");
            keywords1.Add("MAP");
            keywords1.Add("MATCH");
            keywords1.Add("MAX");
            keywords1.Add("MEMBER");
            keywords1.Add("MERGE");
            keywords1.Add("METHOD");
            keywords1.Add("MIN");
            keywords1.Add("MINUTE");
            keywords1.Add("MOD");
            keywords1.Add("MODIFIES");
            keywords1.Add("MODIFY");
            keywords1.Add("MODULE");
            keywords1.Add("MONEY");
            keywords1.Add("MONTH");
            keywords1.Add("MULTISET");
            keywords1.Add("NAMES");
            keywords1.Add("NATIONAL");
            keywords1.Add("NATURAL");
            keywords1.Add("NCHAR");
            keywords1.Add("NCLOB");
            keywords1.Add("NEW");
            keywords1.Add("NEXT");
            keywords1.Add("NO");
            keywords1.Add("NOCHECK");
            keywords1.Add("NOCOUNT");
            keywords1.Add("NONCLUSTERED");
            keywords1.Add("NONE");
            keywords1.Add("NORMALIZE");
            keywords1.Add("NOT");
            keywords1.Add("NTEXT");
            keywords1.Add("NULL");
            keywords1.Add("NULLIF");
            keywords1.Add("NUMERIC");
            keywords1.Add("NVARCHAR");
            keywords1.Add("OBJECT");
            keywords1.Add("OCCURRENCES_REGEX");
            keywords1.Add("OCTET_LENGTH");
            keywords1.Add("OF");
            keywords1.Add("OFF");
            keywords1.Add("OFFSETS");
            keywords1.Add("OLD");
            keywords1.Add("ON");
            keywords1.Add("ONLY");
            keywords1.Add("OPEN");
            keywords1.Add("OPENDATASOURCE");
            keywords1.Add("OPENQUERY");
            keywords1.Add("OPENROWSET");
            keywords1.Add("OPENXML");
            keywords1.Add("OPERATION");
            keywords1.Add("OPTION");
            keywords1.Add("OR");
            keywords1.Add("ORDER");
            keywords1.Add("ORDINALITY");
            keywords1.Add("OUT");
            keywords1.Add("OUTER");
            keywords1.Add("OUTPUT");
            keywords1.Add("OVER");
            keywords1.Add("OVERLAPS");
            keywords1.Add("OVERLAY");
            keywords1.Add("PAD");
            keywords1.Add("PARAMETER");
            keywords1.Add("PARAMETERS");
            keywords1.Add("PARTIAL");
            keywords1.Add("PARTITION");
            keywords1.Add("PASCAL");
            keywords1.Add("PATH");
            keywords1.Add("PERCENT");
            keywords1.Add("PERCENT_RANK");
            keywords1.Add("PERCENTILE_CONT");
            keywords1.Add("PERCENTILE_DISC");
            keywords1.Add("PIVOT");
            keywords1.Add("PLAN");
            keywords1.Add("POSITION");
            keywords1.Add("POSITION_REGEX");
            keywords1.Add("POSTFIX");
            keywords1.Add("PRECISION");
            keywords1.Add("PREFIX");
            keywords1.Add("PREORDER");
            keywords1.Add("PREPARE");
            keywords1.Add("PRESERVE");
            keywords1.Add("PRIMARY");
            keywords1.Add("PRINT");
            keywords1.Add("PRIOR");
            keywords1.Add("PRIVILEGES");
            keywords1.Add("PROC");
            keywords1.Add("PROCEDURE");
            keywords1.Add("PUBLIC");
            keywords1.Add("RAISERROR");
            keywords1.Add("RANGE");
            keywords1.Add("READ");
            keywords1.Add("READS");
            keywords1.Add("READTEXT");
            keywords1.Add("REAL");
            keywords1.Add("RECONFIGURE");
            keywords1.Add("RECURSIVE");
            keywords1.Add("REF");
            keywords1.Add("REFERENCES");
            keywords1.Add("REFERENCING");
            keywords1.Add("REGR_AVGX");
            keywords1.Add("REGR_AVGY");
            keywords1.Add("REGR_COUNT");
            keywords1.Add("REGR_INTERCEPT");
            keywords1.Add("REGR_R2");
            keywords1.Add("REGR_SLOPE");
            keywords1.Add("REGR_SXX");
            keywords1.Add("REGR_SXY");
            keywords1.Add("REGR_SYY");
            keywords1.Add("RELATIVE");
            keywords1.Add("RELEASE");
            keywords1.Add("REPLICATION");
            keywords1.Add("RESTORE");
            keywords1.Add("RESTRICT");
            keywords1.Add("RESULT");
            keywords1.Add("RETURN");
            keywords1.Add("RETURNS");
            keywords1.Add("REVERT");
            keywords1.Add("REVOKE");
            keywords1.Add("RIGHT");
            keywords1.Add("ROLE");
            keywords1.Add("ROLLBACK");
            keywords1.Add("ROLLUP");
            keywords1.Add("ROUTINE");
            keywords1.Add("ROW");
            keywords1.Add("ROWCOUNT");
            keywords1.Add("ROWGUIDCOL");
            keywords1.Add("ROWS");
            keywords1.Add("ROWVERSION");
            keywords1.Add("RULE");
            keywords1.Add("SAVE");
            keywords1.Add("SAVEPOINT");
            keywords1.Add("SCHEMA");
            keywords1.Add("SCOPE");
            keywords1.Add("SCROLL");
            keywords1.Add("SEARCH");
            keywords1.Add("SECOND");
            keywords1.Add("SECTION");
            keywords1.Add("SECURITYAUDIT");
            keywords1.Add("SELECT");
            keywords1.Add("SENSITIVE");
            keywords1.Add("SEQUENCE");
            keywords1.Add("SESSION");
            keywords1.Add("SESSION_USER");
            keywords1.Add("SET");
            keywords1.Add("SETS");
            keywords1.Add("SETUSER");
            keywords1.Add("SHUTDOWN");
            keywords1.Add("SIMILAR");
            keywords1.Add("SIZE");
            keywords1.Add("SMALLDATETIME");
            keywords1.Add("SMALLINT");
            keywords1.Add("SMALLMONEY");
            keywords1.Add("SOME");
            keywords1.Add("SPACE");
            keywords1.Add("SPECIFIC");
            keywords1.Add("SPECIFICTYPE");
            keywords1.Add("SQL");
            keywords1.Add("SQL_VARIANT");
            keywords1.Add("SQLCA");
            keywords1.Add("SQLCODE");
            keywords1.Add("SQLERROR");
            keywords1.Add("SQLEXCEPTION");
            keywords1.Add("SQLSTATE");
            keywords1.Add("SQLWARNING");
            keywords1.Add("START");
            keywords1.Add("STATE");
            keywords1.Add("STATEMENT");
            keywords1.Add("STATIC");
            keywords1.Add("STATISTICS");
            keywords1.Add("STDDEV_POP");
            keywords1.Add("STDDEV_SAMP");
            keywords1.Add("STRUCTURE");
            keywords1.Add("SUBMULTISET");
            keywords1.Add("SUBSTRING");
            keywords1.Add("SUBSTRING_REGEX");
            keywords1.Add("SUM");
            keywords1.Add("SYMMETRIC");
            keywords1.Add("SYSNAME");
            keywords1.Add("SYSTEM");
            keywords1.Add("SYSTEM_USER");
            keywords1.Add("TABLE");
            keywords1.Add("TABLESAMPLE");
            keywords1.Add("TEMPORARY");
            keywords1.Add("TERMINATE");
            keywords1.Add("TEXT");
            keywords1.Add("TEXTSIZE");
            keywords1.Add("THAN");
            keywords1.Add("THEN");
            keywords1.Add("TIME");
            keywords1.Add("TIMESTAMP");
            keywords1.Add("TIMEZONE_HOUR");
            keywords1.Add("TIMEZONE_MINUTE");
            keywords1.Add("TINYINT");
            keywords1.Add("TO");
            keywords1.Add("TOP");
            keywords1.Add("TRAILING");
            keywords1.Add("TRAN");
            keywords1.Add("TRANSACTION");
            keywords1.Add("TRANSLATE");
            keywords1.Add("TRANSLATE_REGEX");
            keywords1.Add("TRANSLATION");
            keywords1.Add("TREAT");
            keywords1.Add("TRIGGER");
            keywords1.Add("TRIM");
            keywords1.Add("TRUE");
            keywords1.Add("TRUNCATE");
            keywords1.Add("TSEQUAL");
            keywords1.Add("UESCAPE");
            keywords1.Add("UNDER");
            keywords1.Add("UNION");
            keywords1.Add("UNIQUE");
            keywords1.Add("UNIQUEIDENTIFIER");
            keywords1.Add("UNKNOWN");
            keywords1.Add("UNNEST");
            keywords1.Add("UNPIVOT");
            keywords1.Add("UPDATE");
            keywords1.Add("UPDATETEXT");
            keywords1.Add("UPPER");
            keywords1.Add("USAGE");
            keywords1.Add("USE");
            keywords1.Add("USER");
            keywords1.Add("USING");
            keywords1.Add("VALUE");
            keywords1.Add("VALUES");
            keywords1.Add("VAR_POP");
            keywords1.Add("VAR_SAMP");
            keywords1.Add("VARBINARY");
            keywords1.Add("VARCHAR");
            keywords1.Add("VARIABLE");
            keywords1.Add("VARYING");
            keywords1.Add("VIEW");
            keywords1.Add("WAITFOR");
            keywords1.Add("WHEN");
            keywords1.Add("WHENEVER");
            keywords1.Add("WHERE");
            keywords1.Add("WHILE");
            keywords1.Add("WIDTH_BUCKET");
            keywords1.Add("WINDOW");
            keywords1.Add("WITH");
            keywords1.Add("WITHIN");
            keywords1.Add("WITHOUT");
            keywords1.Add("WORK");
            keywords1.Add("WRITE");
            keywords1.Add("WRITETEXT");
            keywords1.Add("XML");
            keywords1.Add("XMLAGG");
            keywords1.Add("XMLATTRIBUTES");
            keywords1.Add("XMLBINARY");
            keywords1.Add("XMLCAST");
            keywords1.Add("XMLCOMMENT");
            keywords1.Add("XMLCONCAT");
            keywords1.Add("XMLDOCUMENT");
            keywords1.Add("XMLELEMENT");
            keywords1.Add("XMLEXISTS");
            keywords1.Add("XMLFOREST");
            keywords1.Add("XMLITERATE");
            keywords1.Add("XMLNAMESPACES");
            keywords1.Add("XMLPARSE");
            keywords1.Add("XMLPI");
            keywords1.Add("XMLQUERY");
            keywords1.Add("XMLSERIALIZE");
            keywords1.Add("XMLTABLE");
            keywords1.Add("XMLTEXT");
            keywords1.Add("XMLVALIDATE");
            keywords1.Add("YEAR");
            keywords1.Add("ZONE");
        }

        static bool IsKeyword1(String tokenText)
        {
            return keywords1.Contains(tokenText.ToUpperInvariant());
        }

        #endregion

        #region Keywords 2 - StringComparer

        static HashSet<String> keywords2;

        static void InitKeywords2()
        {
            keywords2 = new HashSet<string>(keywords1, StringComparer.OrdinalIgnoreCase);
        }

        static bool IsKeyword2(String tokenText)
        {
            return keywords2.Contains(tokenText);
        }

        #endregion

        #region Keywords 3 - Good optimized

        static IntPtr gpCaseMapping;
        static IntPtr gpKeywords3;

        unsafe static void InitKeywords3()
        {
            gpKeywords3 = Marshal.AllocCoTaskMem(1293 * 4 * 4);
            int* pHashset3 = (int*)gpKeywords3;
            for (int i = 0; i < 1293 * 4; i++)
                pHashset3[i] = 0;
            foreach (String keyword in keywords1)
            {
                fixed (char* pKeyword = keyword)
                {
                    int hashCode = keyword.GetHashCode();
                    int index = (int)(((uint)hashCode % 1293) << 2);
                    if (pHashset3[index] != 0)
                        index += 2;
                    if (pHashset3[index] != 0)
                        throw new Exception("Solve the collision.");
                    pHashset3[index] = hashCode;
                    pHashset3[index + 1] = (int)pKeyword[0] | ((int)pKeyword[1] << 8) | ((int)pKeyword[2] << 16) | ((int)pKeyword[3] << 24);
                }
            }

            gpCaseMapping = Marshal.AllocCoTaskMem(65536);
            byte* pCaseMapping = (byte*)gpCaseMapping;
            for (int i = 0; i < 65536; i++)
                pCaseMapping[i] = 0;
            for (int i = 'A'; i <= 'Z'; i++)
                pCaseMapping[i] = (byte)i;
            for (int i = 'a'; i <= 'z'; i++)
                pCaseMapping[i] = (byte)(i ^ 0x20);
            pCaseMapping['2'] = (byte)'2';
            pCaseMapping['-'] = (byte)'-';
            pCaseMapping['_'] = (byte)'_';
        }

        unsafe static bool IsKeyword3(String tokenText)
        {
            unchecked
            {
                fixed (char* pString = tokenText)
                {
                    byte* pCaseMapping = (byte*)gpCaseMapping;

                    int num1 = 5381;
                    int num2 = num1;
                    int c1;
                    int c2;
                    char* ptr = pString;
                    int seq = 0;

                    if (tokenText.Length >= 4)
                    {
                        c1 = (int)(*(ushort*)ptr);
                        c2 = (int)(*(ushort*)(ptr + 1));
                        c1 = pCaseMapping[c1];
                        c2 = pCaseMapping[c2];
                        if (c1 == 0 || c2 == 0)
                            return false;
                        seq = (c2 << 8) | c1;
                        num1 = ((num1 << 5) + num1 ^ c1);
                        num2 = ((num2 << 5) + num2 ^ c2);
                        ptr += 2;

                        c1 = (int)(*(ushort*)ptr);
                        c2 = (int)(*(ushort*)(ptr + 1));
                        c1 = pCaseMapping[c1];
                        c2 = pCaseMapping[c2];
                        if (c1 == 0 || c2 == 0)
                            return false;
                        seq |= (c2 << 24) | (c1 << 16);
                        num1 = ((num1 << 5) + num1 ^ c1);
                        num2 = ((num2 << 5) + num2 ^ c2);
                        ptr += 2;

                        for (int loops = (tokenText.Length >> 1) - 2; loops > 0; loops--, ptr += 2)
                        {
                            c1 = (int)(*(ushort*)ptr);
                            c2 = (int)(*(ushort*)(ptr + 1));
                            c1 = pCaseMapping[c1];
                            c2 = pCaseMapping[c2];
                            if (c1 == 0 || c2 == 0)
                                return false;
                            num1 = ((num1 << 5) + num1 ^ c1);
                            num2 = ((num2 << 5) + num2 ^ c2);
                        }

                        if ((tokenText.Length & 1) != 0)
                        {
                            c1 = (int)(*(ushort*)ptr);
                            c1 = pCaseMapping[c1];
                            if (c1 == 0)
                                return false;
                            num1 = ((num1 << 5) + num1 ^ c1);
                        }
                    }
                    else if (tokenText.Length == 3)
                    {
                        c1 = (int)(*(ushort*)ptr);
                        c2 = (int)(*(ushort*)(ptr + 1));
                        c1 = pCaseMapping[c1];
                        c2 = pCaseMapping[c2];
                        if (c1 == 0 || c2 == 0)
                            return false;
                        seq = (c2 << 8) | c1;
                        num1 = ((num1 << 5) + num1 ^ c1);
                        num2 = ((num2 << 5) + num2 ^ c2);

                        c1 = (int)(*(ushort*)(ptr + 2));
                        c1 = pCaseMapping[c1];
                        if (c1 == 0)
                            return false;
                        seq |= (c1 << 16);
                        num1 = ((num1 << 5) + num1 ^ c1);
                    }
                    else if (tokenText.Length == 2)
                    {
                        c1 = (int)(*(ushort*)ptr);
                        c2 = (int)(*(ushort*)(ptr + 1));
                        c1 = pCaseMapping[c1];
                        c2 = pCaseMapping[c2];
                        if (c1 == 0 || c2 == 0)
                            return false;
                        seq = (c2 << 8) | c1;
                        num1 = ((num1 << 5) + num1 ^ c1);
                        num2 = ((num2 << 5) + num2 ^ c2);
                        ptr += 2;
                    }
                    else
                    {
                        return false;
                    }

                    int hashCode = num1 + num2 * 1566083941;
                    int index = (int)(((uint)hashCode % 1293) << 2);
                    int* pHashset3 = (int*)gpKeywords3;
                    return (pHashset3[index + 0] == hashCode && pHashset3[index + 1] == seq)
                        || (pHashset3[index + 2] == hashCode && pHashset3[index + 3] == seq);
                }
            }
        }

        #endregion

        #region Test Data

        static String[] TestTokens = new String[] {
"DECLARE", "@lang", "sysname",
"SELECT", "TOP", "@lang", "Alias", "FROM", "sys", "syslanguages", "WHERE", "lcid",
"IF", "@lang", "IS", "NOT", "NULL", "SET", "LANGUAGE", "@lang",
"SET", "ARITHABORT", "ANSI_PADDING", "ANSI_WARNINGS", "QUOTED_IDENTIFIER",
"NOCOUNT", "CONCAT_NULL_YIELDS_NULL", "ANSI_NULLS", "ON",
"SET", "NUMERIC_ROUNDABORT", "IMPLICIT_TRANSACTIONS", "XACT_ABORT", "OFF",
"SET", "DATEFORMAT", "dmy",
"USE", "AdventureWorks2008R2",
"GO",
"INSERT", "Sales", "SalesOrderHeader", "SalesOrderID", "RevisionNumber", "OrderDate", "DueDate", "ShipDate",
"Status", "OnlineOrderFlag", "PurchaseOrderNumber", "AccountNumber", "CustomerID", "SalesPersonID", "TerritoryID",
"BillToAddressID", "ShipToAddressID", "ShipMethodID", "CreditCardID", "CreditCardApprovalCode", "CurrencyRateID",
"SubTotal", "TaxAmt", "Freight", "Comment", "rowguid", "ModifiedDate", "VALUES",
"ALTER", "TABLE", "dbo", "DatabaseLog", "ADD", "CONSTRAINT", "PK_DatabaseLog_DatabaseLogID", "PRIMARY", "KEY", "NONCLUSTERED",
"DatabaseLogID", "ASC", "WITH", "DATA_COMPRESSION", "NONE",
"EXEC", "sp_addextendedproperty", "dbo", "DatabaseLog", "PK_DatabaseLog_DatabaseLogID",
"GO", "IF", "@@ERROR", "OR", "TRANCOUNT", "BEGIN", "IF", "@@TRANCOUNT", "ROLLBACK", "SET", "NOEXEC", "ON", "END",
"GO", "DBCC", "CHECKIDENT", "RESEED", "GO", "IF", "@@ERROR", "@@TRANCOUNT", "BEGIN", "IF", "@@TRANCOUNT", "ROLLBACK", "SET", "NOEXEC", "ON", "END", "GO"
            };

        #endregion

        #region Timing

        static long _timestamp;

        static void StartTiming()
        {
            _timestamp = Stopwatch.GetTimestamp();
        }

        static double EndTiming()
        {
            return (double)(Stopwatch.GetTimestamp() - _timestamp) / (double)Stopwatch.Frequency;
        }

        #endregion

        unsafe static void DoLexerTests()
        {
            int testCount = 100000;
            int matchCount = 0;
            double time;

            // Test 1
            InitKeywords1();
            StartTiming();
            for (int t = testCount; t > 0; t--)
            {
                matchCount = 0;
                for (int i = 0; i < TestTokens.Length; i++)
                {
                    String identifier = TestTokens[i];
                    if (IsKeyword1(identifier))
                        matchCount++;
                }
            }
            time = EndTiming();
            Console.WriteLine("Test 1 - ToUpper");
            Console.WriteLine("Time:    " + time);
            Console.WriteLine("Matches: " + matchCount);
            Console.WriteLine();

            // Test 2
            InitKeywords2();
            StartTiming();
            for (int t = testCount; t > 0; t--)
            {
                matchCount = 0;
                for (int i = 0; i < TestTokens.Length; i++)
                {
                    String identifier = TestTokens[i];
                    if (IsKeyword2(identifier))
                        matchCount++;
                }
            }
            time = EndTiming();
            Console.WriteLine("Test 2 - StringComparer");
            Console.WriteLine("Time:    " + time);
            Console.WriteLine("Matches: " + matchCount);
            Console.WriteLine();

            // Test 3
            InitKeywords3();
            StartTiming();
            for (int t = testCount; t > 0; t--)
            {
                matchCount = 0;
                for (int i = 0; i < TestTokens.Length; i++)
                {
                    String identifier = TestTokens[i];
                    if (IsKeyword3(identifier))
                        matchCount++;
                }
            }
            time = EndTiming();
            Console.WriteLine("Test 3 - Optimized Code");
            Console.WriteLine("Time:    " + time);
            Console.WriteLine("Matches: " + matchCount);
            Console.WriteLine();
        }
    }
}


, ( ):
ToUpperInvariant2330
IEqualityComparer661
239


C ToUpperInvariant , — , . , .NET Framework ( Windows).
3 , StringComparer, . , StringComparer' C++, C#, . , , , 400 . :
‱ N3 — . , . — 400 , . . — 10 . 10 , .
‱ , , . .
‱ , . , -, . , , . , « ». , , . , IntelliSense. , , 

‱ — ? , SQL Server, .
‱ , :
, ,

, ( « , »). , — «», , .

Source: https://habr.com/ru/post/165729/


All Articles