I have different strings that will return different integers, though some strings have the same value (usually 4 per code).
I used a Dictionary
first, and still have them, but it's kinda messy. I also tried using switch
, which is basically the same to me in this case.
Here are two examples:
public static int PPCFloatCalc4RegistersSwitch(string code)
{
switch (code)
{
case "fsel": return 23;
case "fsel.": return 23;
case "fmul": return 25;
case "fmul.": return 25;
case "fm": return 25;
case "fm.": return 25;
case "fmuls": return 25;
case "fmuls.": return 25;
case "fms": return 28;
case "fms.": return 28;
case "fmsub": return 28;
case "fmsub.": return 28;
case "fmsubs": return 28;
case "fmsubs.": return 28;
case "fmadd": return 29;
case "fmadd.": return 29;
case "fma": return 29;
case "fma.": return 29;
case "fmadds": return 29;
case "fmadds.": return 29;
case "fnmsub": return 30;
case "fnmsub.": return 30;
case "fnmsubs": return 30;
case "fnmsubs.": return 30;
case "fnmadd": return 31;
case "fnmadd.": return 31;
case "fnmadds": return 31;
case "fnmadds.": return 31;
}
return -1;
}
public static readonly Dictionary<string, int> PPCFloatCalc4RegistersDictionary = new Dictionary<string, int>
{
["fsel"] = 23,
["fsel."] = 23,
["fmul"] = 25,
["fmul."] = 25,
["fm"] = 25,
["fm."] = 25,
["fmuls"] = 25,
["fmuls."] = 25,
["fms"] = 28,
["fms."] = 28,
["fmsub"] = 28,
["fmsub."] = 28,
["fmsubs"] = 28,
["fmsubs."] = 28,
["fmadd"] = 29,
["fmadd."] = 29,
["fma"] = 29,
["fma."] = 29,
["fmadds"] = 29,
["fmadds."] = 29,
["fnmsub"] = 30,
["fnmsub."] = 30,
["fnmsubs"] = 30,
["fnmsubs."] = 30,
["fnmadd"] = 31,
["fnmadd."] = 31,
["fnmadds"] = 31,
["fnmadds."] = 31,
};
As you can see, it's repetitive, which is kinda bad for readability. It's worth noting that performance is a bit needed here, so switch
/Dictionary
speed would be great.
I don't think I need to add any more examples be cause the functions pretty much show what it does quite clearly.
4 Answers 4
Dictionary and switch are far from the same, even in your case.
As a general guideline I use a dictionary as a lookup mechanism and a switch as flow control.
As a lookup mechanism a switch is less efficient than a dictionary.
Maintenance wise, adding new values to a dictionary is not changing code per se. Modifying the dictionary will not cause a change to the code calling it. And a dictionary has a built in error check - you can't have the same key twice with different values.
However the switch is very much involved in program flow control. Therefore changing the switch code is higher risk than changing dictionary content.
Finally, using a dictionary decouples your data from the code that uses it. It makes the data re-usable. And you don't have duplicate switch code everywhere the data is used.
-
\$\begingroup\$ Important note is that my "Dictionary" would be constant, static. There will never bee any mutation during runtime. \$\endgroup\$Zerowalker– Zerowalker2016年11月25日 04:54:01 +00:00Commented Nov 25, 2016 at 4:54
-
\$\begingroup\$ Never say never. Just ask Sean Connery. Even so that does not change the answer. As for both approaches looking equally "messy", I'm not clear on what the goal is. But from coding principles and code maintenance perspective, the switch is hands-down messier. \$\endgroup\$radarbob– radarbob2016年11月25日 05:04:51 +00:00Commented Nov 25, 2016 at 5:04
-
\$\begingroup\$ Well it's a dictionary to a compiler i make. So the instructions (strings) are "written in stone" so to speak;P Of course i would love to do it in a more clear way, if there wasn't any real performance hit. But if there is i can probably live with it:o \$\endgroup\$Zerowalker– Zerowalker2016年11月25日 05:14:15 +00:00Commented Nov 25, 2016 at 5:14
-
\$\begingroup\$ "... in a more clear way". At the risk of turning this into a chat; I don't understand what you are getting at. How do you wish it could be done? \$\endgroup\$radarbob– radarbob2016年11月25日 05:28:41 +00:00Commented Nov 25, 2016 at 5:28
-
\$\begingroup\$ well tbh i don't know, hence why i am asking. I kinda suck at structuring and stuff, and i only stuff it together and hope for the best. So sadly i can't give any direction to what i actually Want:( \$\endgroup\$Zerowalker– Zerowalker2016年11月25日 05:49:00 +00:00Commented Nov 25, 2016 at 5:49
I suggest creating an enum:
enum PpcRegister
{
fsel = 23,
fmul = 25,
fnmsub = 30,
fnmsubs = 30,
// ...
}
generate the dictionary once with a little bit of reflection
var pccRegisters =
Enum.GetValues(typeof(PpcRegister))
.Cast<PpcRegister>()
.ToDictionary(x => x.ToString(), x => (int)x, StringComparer.OrdinalIgnoreCase);
and use it to get the value where you trim the .
dot at the end
var value = 0;
var pccRegisterName = "fmul.";
var result =
pccRegisters.TryGetValue(pccRegisterName.TrimEnd('.'), out value)
? value
: -1;
-
\$\begingroup\$ What do you mean trim the dot at the end, the dot sets a condition, so fmul. is different from fmul \$\endgroup\$Zerowalker– Zerowalker2016年11月25日 09:30:04 +00:00Commented Nov 25, 2016 at 9:30
-
\$\begingroup\$ @Zerowalker yes, but somewhere else in you application. For the lookup you don't need it because according to your switch
"fsel" == "fsel." == 23
. \$\endgroup\$t3chb0t– t3chb0t2016年11月25日 09:36:11 +00:00Commented Nov 25, 2016 at 9:36 -
\$\begingroup\$ Ah i see. hm whould it possible to dectect "o" as well, that one is at the end as well, Except if it's a dot, then it's at the "second last character". Already have a detect for it though which looks at them ;p \$\endgroup\$Zerowalker– Zerowalker2016年11月26日 00:39:58 +00:00Commented Nov 26, 2016 at 0:39
Ron Beyer's suggestion:
The original method in the switch
uses a return
on each case, even if it's identical. This method simply lets each identical case fall through to the last.
It can be either placed in rows or in a line as shown.
public static int PPCFloatCalcSwitch(string code)
{
switch (code)
{
case "fdiv": case "fdiv.": case "fd": case "fd.": case "fdivs": case "fdivs.": return 18;
case "fsub": case "fsub.": case "fs": case "fs.": case "fsubs": case "fsubs.": return 20;
case "fadd": case "fadd.": case "fa": case "fa.": case "fadds": case "fadds.": return 21;
case "fabs": case "fabs.": return 264;
case "fctiw": case "fctiw.": return 14;
case "fctiwz": case "fctiwz.": return 15;
case "fmr": case "fmr.": return 72;
case "fnabs": case "fnabs.": return 136;
case "fneg": case "fneg.": return 40;
case "fres": case "fres.": return 24;
case "frsp": case "frsp.": return 12;
case "frsqrte": case "frsqrte.": return 26;
default: return -1;
}
}
-
1\$\begingroup\$ Yes, this is probably the most performant of your two, although personally I'd have each
case
on its own line. \$\endgroup\$Ron Beyer– Ron Beyer2016年11月25日 04:44:22 +00:00Commented Nov 25, 2016 at 4:44 -
\$\begingroup\$ It's faster, really? Won't it do some extra nop calls or something before returning? And yeah i just think it looks a bit more readable for me. \$\endgroup\$Zerowalker– Zerowalker2016年11月25日 04:46:27 +00:00Commented Nov 25, 2016 at 4:46
-
\$\begingroup\$ @Jamal , added some explanation, not really sure how to show the difference as it's basically the same, except some removed lines:( \$\endgroup\$Zerowalker– Zerowalker2016年11月25日 04:50:47 +00:00Commented Nov 25, 2016 at 4:50
-
\$\begingroup\$ @Zerowalker I didn't say it was "faster" than the other one, it is probably the same. If you want to really be sure, compile both of them and take a look at the IL that is emitted (in release). I'm willing to bet its not much different. Why I suggested it is that it is a little more compact (but I would still put each
case
on its own line). \$\endgroup\$Ron Beyer– Ron Beyer2016年11月25日 04:54:11 +00:00Commented Nov 25, 2016 at 4:54 -
\$\begingroup\$ @RonBeyer ah my mistake then. Never really looked at the IL, should probably start doing that, might learn something, thanks. \$\endgroup\$Zerowalker– Zerowalker2016年11月25日 04:55:42 +00:00Commented Nov 25, 2016 at 4:55
If you are looking for speed then newing with each call is not good
The final , is a syntax error
Dictionary lookup es O(1) where switch is O(N) According to Demity switch in O(1)
Still if you are going to use Dictionary then this is a better pattern
On switch you can have a default return
private static readonly Dictionary<string, int> pPCFloatCalc4RegistersDictionary = new Dictionary<string, int>
{
["fsel"] = 23,
["fsel."] = 23,
["fmul"] = 25,
["fmul."] = 25,
["fm"] = 25,
["fm."] = 25,
["fmuls"] = 25,
["fmuls."] = 25,
["fms"] = 28,
["fms."] = 28,
["fmsub"] = 28,
["fmsub."] = 28,
["fmsubs"] = 28,
["fmsubs."] = 28,
["fmadd"] = 29,
["fmadd."] = 29,
["fma"] = 29,
["fma."] = 29,
["fmadds"] = 29,
["fmadds."] = 29,
["fnmsub"] = 30,
["fnmsub."] = 30,
["fnmsubs"] = 30,
["fnmsubs."] = 30,
["fnmadd"] = 31,
["fnmadd."] = 31,
["fnmadds"] = 31,
["fnmadds."] = 31
};
public static int PPCFloatCalc4RegistersSwitch(string code)
{
if(pPCFloatCalc4RegistersDictionary.Contains(code))
return pPCFloatCalc4RegistersDictionary[code];
else
return -1;
}
-
\$\begingroup\$ Hmm.
switch
is O(1) too. \$\endgroup\$Dmitry– Dmitry2016年11月25日 14:30:13 +00:00Commented Nov 25, 2016 at 14:30 -
\$\begingroup\$ @Dmitry I had switch uses else if under the covers. What does it use? \$\endgroup\$paparazzo– paparazzo2016年11月25日 14:41:08 +00:00Commented Nov 25, 2016 at 14:41
-
1
-
\$\begingroup\$ The lookup is in another code, so doesn't my code just make a new dictionary one time as well? I used a "TryGet" function when doing the lookup. \$\endgroup\$Zerowalker– Zerowalker2016年11月26日 00:52:55 +00:00Commented Nov 26, 2016 at 0:52
-
\$\begingroup\$ The final
,
in the dictionary is not a syntax error. Trailing comma's are allowed in collection initializers and enum definitions. \$\endgroup\$Ron Beyer– Ron Beyer2016年11月26日 02:51:51 +00:00Commented Nov 26, 2016 at 2:51
.
from the end of the string (if it exists), but the trim operation is probably more expensive than the extra cases. What do you prefer? Shorter syntax or pure performance? You can also remove 3/4ths of thereturn
statements and let the cases fall through since the return values are the same, will make it neater but not do much performance wise. \$\endgroup\$case
doesn't have any statements it will "fall through" to the next case. You just need the last common return statement, all the ones above it that do not have a return (or any other statements) will fall through to that return. \$\endgroup\$