How would you make this switch statement as fast as possible?

后端 未结 21 2032
别那么骄傲
别那么骄傲 2021-01-30 04:17

2009-12-04 UPDATE: For profiling results on a number of the suggestions posted here, see below!


The Question

Consider the following very

相关标签:
21条回答
  • 2021-01-30 04:53

    If you know how often the various codes show up, the more common ones should go at the top of the list, so fewer comparisons are done. But let's assume you don't have that.

    Assuming the ActivCode is always valid will of course speed things up. You don't need to test for null or the empty string, and you can leave off one test from the end of the switch. That is, test for everything except Y, and then return DIRECTEDGE if no match is found.

    Instead of switching on the whole string, switch on its first letter. For the codes that have more letters, put a second test inside the switch case. Something like this:

    switch(ActivCode[0])
    {
       //etc.
       case 'B':
          if ( ActivCode.Length == 1 ) return MarketDataExchange.BSE; 
          else return MarketDataExchange.BATS;
          // etc.
    }
    

    It would be better if you could go back and change the codes so they are all single characters, because you would then never need more than one test. Better yet would be using the numerical value of the enum, so you can simply cast instead of having to switch/translate in the first place.

    0 讨论(0)
  • 2021-01-30 04:54

    I would put it in dictionary instead of using a switch statement. That being said, it may not make a difference. Or it might. See C# switch statement limitations - why?.

    0 讨论(0)
  • 2021-01-30 04:54
    1. Avoid all string comparisons.
    2. Avoid looking at more than a single character (ever)
    3. Avoid if-else since I want the compiler to be able optimize the best it can
    4. Try to get the result in a single switch jump

    code:

    public static MarketDataExchange GetMarketDataExchange(string ActivCode) {
        if (ActivCode == null) return MarketDataExchange.NONE;
        int length = ActivCode.Length;
        if (length == 0) return MarketDataExchange.NBBO;
    
        switch (ActivCode[0]) {
            case 'A': return MarketDataExchange.AMEX;
            case 'B': return (length == 2) ? MarketDataExchange.BATS : MarketDataExchange.BSE;
            case 'C': return MarketDataExchange.NSE;
            case 'M': return MarketDataExchange.CHX;
            case 'N': return MarketDataExchange.NYSE;
            case 'P': return MarketDataExchange.ARCA;
            case 'Q': return (length == 2) ? MarketDataExchange.NASDAQ_ADF : MarketDataExchange.NASDAQ;
            case 'W': return MarketDataExchange.CBOE;
            case 'X': return MarketDataExchange.PHLX;
            case 'Y': return MarketDataExchange.DIRECTEDGE;
            default:  return MarketDataExchange.NONE;
        }
    }
    
    0 讨论(0)
  • 2021-01-30 04:55

    Trade memory for speed by pre-populating an index table to leverage simple pointer arithmetic.

    public class Service 
    {
        public static MarketDataExchange GetMarketDataExchange(string ActivCode) {
        {
            int x = 65, y = 65;
            switch(ActivCode.Length)
            {
                case 1:
                    x = ActivCode[0];
                    break;
                case 2:
                    x = ActivCode[0];
                    y = ActivCode[1];
                    break;
            }
            return _table[x, y];
        }
    
        static Service()
        {
            InitTable();
        }
    
        public static MarketDataExchange[,] _table = 
            new MarketDataExchange['Z','Z'];
    
        public static void InitTable()
        {
            for (int x = 0; x < 'Z'; x++)
                for (int y = 0; y < 'Z'; y++)
                    _table[x, y] = MarketDataExchange.NONE;
    
            SetCell("", MarketDataExchange.NBBO);
            SetCell("A", MarketDataExchange.AMEX);
            SetCell("B", MarketDataExchange.BSE);
            SetCell("BT", MarketDataExchange.BATS);
            SetCell("C", MarketDataExchange.NSE);
            SetCell("MW", MarketDataExchange.CHX);
            SetCell("N", MarketDataExchange.NYSE);
            SetCell("PA", MarketDataExchange.ARCA);
            SetCell("Q", MarketDataExchange.NASDAQ);
            SetCell("QD", MarketDataExchange.NASDAQ_ADF);
            SetCell("W", MarketDataExchange.CBOE);
            SetCell("X", MarketDataExchange.PHLX);
            SetCell("Y", MarketDataExchange.DIRECTEDGE);
        }
    
        private static void SetCell(string s, MarketDataExchange exchange)
        {
            char x = 'A', y = 'A';
            switch(s.Length)
            {
                case 1:
                    x = s[0];
                    break;
                case 2:
                    x = s[0];
                    y = s[1];
                    break;
            }
            _table[x, y] = exchange;
        }
    }
    

    Make the enum byte-based to save a little space.

    public enum MarketDataExchange : byte
    {
        NBBO, AMEX, BSE, BATS, NSE, CHX, NYSE, ARCA, 
        NASDAQ, NASDAQ_ADF, CBOE, PHLIX, DIRECTEDGE, NONE
    }
    
    0 讨论(0)
  • 2021-01-30 04:58

    All your strings are at most 2 chars long, and ASCII, so we can use 1 byte per char. Furthermore, more likely than not, they also never can have \0 appear in them (.NET string allows for embedded null characters, but many other things don't). With that assumption, we can null-pad all your strings to be exactly 2 bytes each, or an ushort:

    ""   -> (byte) 0 , (byte) 0   -> (ushort)0x0000
    "A"  -> (byte)'A', (byte) 0   -> (ushort)0x0041
    "B"  -> (byte)'B', (byte) 0   -> (ushort)0x0042
    "BT" -> (byte)'B', (byte)'T'  -> (ushort)0x5442
    

    Now that we have a single integer in a relatively (64K) short range, we can use a lookup table:

    MarketDataExchange[] lookup = {
        MarketDataExchange.NBBO, 
        MarketDataExchange.NONE, 
        MarketDataExchange.NONE, 
        ...
        /* at index 0x041 */
        MarketDataExchange.AMEX,
        MarketDataExchange.BSE,
        MarketDataExchange.NSE,
        ...
    };
    

    Now, obtaining the value given a string is:

    public static unsafe MarketDataExchange GetMarketDataExchange(string s)
    {
       // Assume valid input
       if (s.Length == 0) return MarketDataExchange.NBBO;
    
       // .NET strings always have '\0' after end of data - abuse that
       // to avoid extra checks for 1-char strings. Skip index checks as well.
       ushort hash;
       fixed (char* data = s)
       {
           hash = (ushort)data[0] | ((ushort)data[1] << 8);
       }
    
       return lookup[hash];
    }
    
    0 讨论(0)
  • 2021-01-30 05:00

    If the enumeration values are arbitrary you could do this...

    public static MarketDataExchange GetValue(string input)
    {
        switch (input.Length)
        {
            case 0: return MarketDataExchange.NBBO;
            case 1: return (MarketDataExchange)input[0];
            case 2: return (MarketDataExchange)(input[0] << 8 | input[1]);
            default: return MarketDataExchange.None;
        }
    }
    

    ... if you want to go totally nuts you can also use an unsafe call with pointers as noted by Pavel Minaev ... The pure cast version above is faster than this unsafe version.

    unsafe static MarketDataExchange GetValue(string input)
    {
        if (input.Length == 1)
            return (MarketDataExchange)(input[0]);
        fixed (char* buffer = input)
            return (MarketDataExchange)(buffer[0] << 8 | buffer[1]);
    }
    

    public enum MarketDataExchange
    {
        NBBO = 0x00, //
        AMEX = 0x41, //A
        BSE = 0x42, //B
        BATS = 0x4254, //BT
        NSE = 0x43, //C
        CHX = 0x4D57, //MW
        NYSE = 0x4E, //N
        ARCA = 0x5041, //PA
        NASDAQ = 0x51, //Q
        NASDAQ_ADF = 0x5144, //QD
        CBOE = 0x57, //W
        PHLX = 0x58, //X
        DIRECTEDGE = 0x59, //Y
    
        None = -1
    }
    
    0 讨论(0)
提交回复
热议问题