2009-12-04 UPDATE: For profiling results on a number of the suggestions posted here, see below!
Consider the following very
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.
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?.
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;
}
}
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
}
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];
}
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
}