Comments by "Edward Cullen" (@edwardcullen1739) on "Low Level"
channel.
-
26
-
8
-
5
-
5
-
3
-
2
-
1
-
1
-
1
-
Hi, I will explain in detail (please read) but this is a bad video, which focuses on the wrong things and teaches all the wrong lessons.
To be clear, this isn't your fault, you (and I!) were simply taught badly in the first place.
As others (i.e., the other old gitz 😉) have already pointed out: an optimising compiler will analyse a cascading if-else and treat it like a switch (giving the generally appropriate optimisation) if it can do so. By focusing on what a non-optimising compiler will do, you are engaging in a Premature Optimisation, which we know "is the root of all evil" (C.A.R. Hoare)
Indeed, the truth of this can be found in Python: Guido, being aware of this optimisation, deliberately ommited a switch statement from the language.
So, why switch vs. cascading if-else?
The correct answer has zero to do with optimisation of the output code; it is entirely to do with design intent and maintainability of your code.
Your example of an Enum is the correct starting point and you are correct about the readability/maintainability aspect of enums over magic values (though, it should be OPTION_QUIT, etc. as you're almost certainly going to have to expose it outside the module, but I digress.)
Combining an enum with a switch, you are signalling to the reader/maintainer that you processing a fixed set of values (and what those values are). This signal enables the maintainer to perform a cognitive context switch, to "filter out" a whole bunch of questions and to load-in a focused set of diagnostics:
- are all enum values checked (if not, why not?)
- are there any missing break statements? (And are these intentional?)
- are the cases correctly formed? (No jumps or compound statement gotchas).
- does the default do something sensible (and if it is just a break, is there a comment saying that this is intentional?)
Equally importantly, it enables static analysis tools to ask these same questions and prompt you to fix your errors. (These are errors, unless appropriate AND documented in the code...)
These questions are important for one simple reason: experience shows that variances are what lead to bugs. Code that's "fast", but does the wrong thing is the quintessential Premature Optimisation.
Using a cascading if-else requires a different, more open-ended analysis, which requires more energry and is more time consuming.
Where you have more complex predicates - whether evaluating inequalities, ranges or multiple variables - this is where if-else becomes the correct tool. How you then write your if-else is a separate topic and is really an "it depends" and there is more room for personal preference and tailoring to a specific set of code block (do I bifurcate and nest or do I repeat tests?)
As to the detail of how a compiler "optimises", these are correct, but you have missed THE most important step: you did not profile the code BEFORE determining whether to persue optimisation.
Modern processors (not just CPUs) are too complex to "guess" what the optimal output is. If you're guessing, you're going to be wrong, unless you are lucky. Even the the example you pick, which, in general, will be optimal, is not guaranteed to provide the best performance in all circumstances (never seen it, but that doesn't mean it can't happen; the point is that processors are THAT complex that any assumptions about performance are, by default, wrong; think of it as "Murphy's Law of Optimisation".)
But more importantly, it's extremely unlikely that a switch/if-else block is going to be such a bottleneck that it's worth optimising.
20 years ago, it barely made sense "in general", but now? Now, if you have not profiled your code and from that data determined that you have a major bottleneck, then this is such a low-level optimisation, that this is completely the wrong thing to be worrying about.
This is a topic for people wanting to understanding how optimising compilers work and would serve as a good introduction to that subject, but you should be explicitly presenting in that context, rather than the general context you are using here.
Finally: C does not guarantee zero-initialisation of non-static arrays/variables, so you should always explicitly zero-out an array or set the value (scanf) before you evaluate, i.e. there should be two scanf_s() calls (or use a do-while) - one before the while and one at the end, assuming you have not terminated. (Always use scanf_s over scanf, especially in educational code.) Using scanf_s would have prevented the buffer overrun you created - %s outputs n+1 characters.
Educational code should always check for error in return values (if (scanf_s(...) == EOF) {// Error occurred} in this case).
1
-
1