[HN Gopher] I 10x'd a TI-84 emulator's speed by replacing a swit...
       ___________________________________________________________________
        
       I 10x'd a TI-84 emulator's speed by replacing a switch-case
        
       Author : xena
       Score  : 323 points
       Date   : 2022-08-08 01:20 UTC (21 hours ago)
        
 (HTM) web link (artemis.sh)
 (TXT) w3m dump (artemis.sh)
        
       | teddyh wrote:
       | > _I had to unminify it, but I was used to dealing with
       | obfuscated code from Minecraft._
       | 
       | The ancient ritual persists.
        
       | marethyu wrote:
       | Any good resources for building my own TI-84 emulator?
        
         | MaxLeiter wrote:
         | https://wikiti.brandonw.net/index.php?title=WikiTI_Home
         | 
         | Cemetech is the largest (and now maybe the only) calculator
         | forum around. There's also a few IRC channels on EFNet
        
         | AreYouSirius wrote:
        
         | Dwedit wrote:
         | Look up the Z80, the ROM, and the IO ports.
        
       | nolist_policy wrote:
       | See also the Nostradamus Distributor
       | https://news.ycombinator.com/item?id=2593095
        
       | djmips wrote:
       | FYI - This article's from 2013 or the work was done in 2013.
        
       | marwatk wrote:
       | For anyone looking for an open source web-based TI emulator I
       | just ported wxWabbitEmu (which I believe supports the TI-84) [1].
       | I only skinned the TI-85 since it's the calc I use, but the skins
       | are just CSS.
       | 
       | I'm curious what the performance is like. I run it on my phone as
       | a basic calculator, haven't messed with running binaries at all.
       | 
       | [1] https://github.com/marwatk/wasmwabbitemu
        
         | thrtythreeforty wrote:
         | This is really cool! It's a shame that jsTIfied isn't open
         | source; I would have liked to use it for live demos in my
         | roundup [1] of cool TI software projects. Your project with a
         | skin would be perfect.
         | 
         | [1]: https://www.thirtythreeforty.net/posts/2021/10/ti-
         | calculator...
        
       | summerlight wrote:
       | http://lua-users.org/lists/lua-l/2011-02/msg00742.html
       | 
       | Somewhat tangential, but one of the most informative thread on
       | interpreter performance optimization.
        
         | james-redwood wrote:
         | I have this bookmarked. It's a great thread.
        
       | mysterydip wrote:
       | From the title, I expected to find one of the frequently called
       | case statements was unoptimized and they fixed it. Instead, the
       | fix was rewriting all case statements in a way the javascript
       | engine would optimize. Very frustrating if you're unaware of the
       | obscure rules around it.
        
         | pjmlp wrote:
         | It is no different from compiled languages, that is one of the
         | reasons why most compilers regardless of the language, allow to
         | look into the generated Assembly.
        
           | IshKebab wrote:
           | It's not fundamentally different but it is generally a _lot_
           | easier to predict what will be fast with e.g. Clang than V8.
        
             | anonymoushn wrote:
             | For inner loops of interpreters in low-level languages,
             | unless you are using some labels-are-values extension, it's
             | often tough to convince the thing to generate the right
             | code.
        
             | pjmlp wrote:
             | Only if one has a PhD in ISO C semantics and compiler
             | optimization handling of UB.
             | 
             | What is fast in clang might be totally crap on xlC, or even
             | plain clang vNext, for example.
        
           | mysterydip wrote:
           | The difference is control. You can choose which compiler to
           | use for your project, even an outdated version if it works
           | for your use case. But you have no control over what JIT
           | engine visits your site.
        
             | pjmlp wrote:
             | Partially true, most of the time many dev teams don't
             | control what is on the CI/CD servers, or what compilers are
             | being used when shipping libraries.
        
         | benreesman wrote:
         | This kind of stuff is usually at least somewhat frustrating in
         | the moment, but it's just the nature of things: we're always
         | mapping some task onto some machine and it wouldn't be such a
         | great story if it wasn't such an elegant hack. This one is
         | particularly interesting because it's one VM mapping onto a
         | second VM and the impedance mismatch is very meta.
         | 
         | My only nitpick with a great read is that I want to know a
         | little more gory details of how the automation was done, it
         | could easily have involved a third or even fourth VM!
        
         | [deleted]
        
         | yarg wrote:
         | Not quite - the javascript engine failed to optimise reasonable
         | situations, so the dude translated to a code structure (arrays
         | of functions) that didn't require optimisation.
         | 
         | Essentially he was forced to manually code what should've been
         | done under the hood anyway.
        
           | shadowofneptune wrote:
           | I'm unsure. They're different enough in structure that it
           | feels to me that it should be a conscious decision. In a
           | switch statememt, there's usually one large scope, while in
           | the function table there's a lot of little scopes. The
           | pathway through the code during execution is different ad
           | well. Massive switch statements are also not reasonable
           | situations from the perspective of a compiler, as they see
           | little use outside of VMs.
        
             | yarg wrote:
             | > They're different enough in structure...
             | 
             | Sure, I mean more in terms of producing a compiled form
             | with O(1) rather than O(n) performance.
             | 
             | > Massive switch statements are also not reasonable
             | situations from the perspective of a compiler, as they see
             | little use outside of VMs.
             | 
             | I'm not really following this logic, massive switch
             | statements are those that can benefit most significantly
             | from the generation of jump tables.
             | 
             | And what needs to be done to generate them doesn't differ
             | significantly based upon the branch count (when you hit
             | case 0x100 you'll need to switch to 16-bit, so binary size
             | will be impacted).
             | 
             | The fact that it's primary use case is reasonably niche is
             | fairly irrelevant given there's bugger all compiler
             | overhead required for support.
             | 
             | Now from a code perspective, I would say that a statement
             | that large probably isn't ideal (depending on the size of
             | your case blocks).
        
               | shadowofneptune wrote:
               | > Now from a code perspective, I would say that a
               | statement that large probably isn't ideal (depending on
               | the size of your case blocks).
               | 
               | This is what I had meant, yes. Optimizing such large
               | blocks is quite difficult. A case label in C and C-like
               | languages does not even need a block, making it harder.
               | 
               | > The fact that it's primary use case is reasonably niche
               | is fairly irrelevant given there's bugger all compiler
               | overhead required for support.
               | 
               | Even static C compilers still struggle with optimizing
               | the switch-in-for-loop pattern, let alone a JIT compiler.
               | Clang does the best job nowadays, but it still lags
               | behind other methods like computed goto or continuation-
               | passing. The Python VM will switch to computed goto if
               | it's available on the C compiler.
               | 
               | Best primer on the subject I have found, even if old:
               | https://www.complang.tuwien.ac.at/forth/threaded-
               | code.html
               | 
               | My own benchmarks from last year (note that these toy VMs
               | may not be applicable to larger ones):
               | https://github.com/shadowofneptune/threaded-code-
               | benchmark
               | 
               | It does take work to make a faster VM, it feels
               | unreasonable to expect a compiler to do a good job at
               | optimizing it without effort on the writer's side.
        
             | andreareina wrote:
             | VMs aren't exactly rare though, and are a case (heh) of
             | when you really want all the optimization the compiler can
             | give.
        
               | naniwaduni wrote:
               | In the grand scheme of things, VMs and interpreters are a
               | class of program that tend toward perhaps surprisingly
               | pathological behaviour, among programs people are likely
               | to write.
        
           | xena wrote:
           | she* the author is female.
        
         | eyelidlessness wrote:
         | But knowing how the JIT deoptimizes is a good thing for anyone
         | programming perf-sensitive code for that JIT. Even developing a
         | sense of where branchy logic might deopt is a good thing, _even
         | if_ your code might not be perf-sensitive but could be called
         | in a perf-sensitive call stack, because it'll give you a good
         | heuristic for when optimization is premature. Optimizing for
         | the JIT isn't and shouldn't be a guiding principle for every
         | dev, but it's harmless or good for us to be aware.
        
           | saagarjha wrote:
           | > Optimizing for the JIT isn't and shouldn't be a guiding
           | principle for every dev, but it's harmless or good for us to
           | be aware.
           | 
           | No, it can be quite harmful: you could end up overoptimizing
           | for one engine and ruin performance on another engine, or
           | even a different version of the same engine. When making non-
           | trivial optimizations that are designed around the quirks of
           | the optimizing compiler you should be very careful about the
           | tradeoffs and also what the penalty will be if the
           | optimization breaks in the future.
        
             | pjmlp wrote:
             | Just like it happens in C when driven by assembly output on
             | a specific compiler version.
        
               | saagarjha wrote:
               | Yep, exactly. There are very few codebases that can
               | actually write code directly to the optimizer and they
               | are usually developed very closely with a specific
               | compiler. For other cases usually you can trust that a
               | number of basic optimizations can be performed and then
               | be cautious about what the various compilers can do. As
               | you gain more experience you can slowly expand the set of
               | things you would expect to be "easy" for the compiler-
               | when those passes don't run, they're generally considered
               | to be bugs.
        
       | stn_za wrote:
        
       | Tehdasi wrote:
       | Tangentially related, but I remember reading years ago a piece
       | from a graphics driver developer (nvidia maybe?) that one of the
       | reasons why the opengl driver was slower than their directx
       | driver (I believe) was that the structure of OpenGL was such that
       | you had to put a massive switch in the code which really hurt
       | performance.
        
         | flohofwoe wrote:
         | Very unlikely that an actual switch statement was the problem
         | (if that actually exists, and wasn't just 'figure of speech').
         | OpenGL state is a huge collection of tiny knobs and toggles
         | ('switches' if you like) and each state change may in turn
         | influence other state, while D3D (since D3D10 at least) groups
         | the same state into a small number of immutable state objects.
        
       | Eeems wrote:
       | I had pointed out this optimization years ago, but I never took
       | the time to actually get it merged into the emulator.
        
         | lern_too_spel wrote:
         | This article is about a change merged 9 years ago.
        
           | Eeems wrote:
           | Jeez, has it really been that long?
        
             | AlotOfReading wrote:
             | I get the same sensation too. It's kind of funny how these
             | little reminders of people and interactions from cemetech
             | and Omni pop up elsewhere in the tech community. I'm still
             | proud of some of the stuff I wrote back in those days.
             | 
             | - Qwerty
        
       | AreYouSirius wrote:
        
       | fabioborellini wrote:
       | So the writer is a 10x developer.
        
         | amelius wrote:
         | No, the compiler writer was a 0.1x developer.
        
           | saagarjha wrote:
           | The compiler writer is probably a fine developer. You just
           | can't 10x everything.
        
           | motoboi wrote:
           | The compiler writer used everything known to mankind about
           | how to optimize code you haven't yet seen.
           | 
           | Human beings still suck at code.
        
       | justinlloyd wrote:
       | Interestingly I wrote a 6502 (actually Atari 6507) emulator and
       | Z80 emulator for the MIPS R3000 in the original SONY PSX, and the
       | only way to get the speed I needed was to use calculated jump
       | tables rather than relying on a switch case. I know that C
       | provides that calculated jump, but even that wasn't enough.
       | Eventually I rewrote the entire emulator in R3000. And then I
       | eventually switched to using CPU retargeting/recompilation from
       | 6502 to R3000, and then used the same recompilation trick to get
       | 6502 emulation running at a decent speed on the ARM CPU in the
       | Nintendo Gameboy Advance.
        
         | klelatti wrote:
         | > CPU retargeting/recompilation from 6502 to R3000
         | 
         | Is this converting 6502 assembly to R3000 - not recompiling the
         | source?
        
           | justinlloyd wrote:
           | Ja. Take 2600 ROM dump and convert to R3000.
        
         | flohofwoe wrote:
         | That depends a lot on the compiler, I guess in the 90's
         | compilers didn't optimize switch-case that well yet.
         | 
         | In modern compilers a switch-case may end up faster than a
         | function-pointer jump table, because even though the compiler
         | will turn the switch-case into a jump table as well, the code
         | that's called through the jump table doesn't have the
         | prologue/epilogue overhead of regular function call. In my
         | emulators I also haven't seen a difference between a regular
         | switch case, and 'computed goto' (but depending on how the
         | decoder loop looks like, computed goto may still provide have
         | an advantage, just not in my CPU emulators).
        
           | justinlloyd wrote:
           | The old compilers optimized the switch/case, just not as well
           | as something from 2022, and there were lots of edge cases
           | too. And limited registers, and small caches and so forth.
           | Plus, these were CPUs that were at the boundary of what could
           | be done in terms of emulation of another CPU and everything
           | else about the device being emulated too. Today we have the
           | advantage of far more advanced compiler technology and far
           | more advanced CPU technology that runs crazy fast, and a
           | greater understanding of how to emulate the machines and best
           | practices for creating those emulators. 25 years ago, not so
           | much. The PS2 and the XBOX were the first consoles where you
           | could just take MAME or the 2600 VCS emulator I wrote and
           | compile it to the target console and "it just worked." I
           | ported MAME to the PS2 and ran six different games on a
           | tumbling cube, and then MAME to the XBOX shortly afterwards,
           | and Dolphin emulator on XBOX and PS2 as well - though again,
           | smacking up against what that generation of consoles could
           | realistically emulate.
        
       | klelatti wrote:
       | Shameless plug for my 6502 JS emulator / assembler in an
       | Observable notebook, which takes the opcode, converts into pseudo
       | assembly and then uses a switch statement on the mnemonic. I
       | expected it would be horribly slow but maybe this switch on the
       | (50 something instructions) is OK?
       | 
       | https://observablehq.com/@tech30k/6502
       | 
       | Written as a convenient way to try to explore the ISA and hoping
       | to try the same approach with other early ISAs soon (6800 in
       | progress).
       | 
       | (Please be kind with the code. I'm not a JS programmer - its my
       | first JS beyond a handful of lines - and there are lots of
       | issues!)
        
       | siddontang wrote:
       | Using map or array instead of switch-case is a common
       | optimization way, but please do a benchmark if you have no
       | confidence whether you need do or not.
       | 
       | E.g, it doesn't already work in Go,
       | https://stackoverflow.com/questions/46789259/map-vs-switch-p...
       | 
       | But if there are too many switch-cases, the codes may be ugly to
       | read, so maybe using a map or array is a better choice here.
        
         | firloop wrote:
         | Wonder if that benchmark still holds true for Golang. Go 1.19
         | (released last week) includes jump table optimizations for
         | switch statements.
         | 
         | https://go.dev/doc/go1.19#compiler
        
           | jheuel wrote:
           | I just tested it:
           | 
           | 1.18:                   BenchmarkMap-10         13388006
           | 77.38 ns/op         BenchmarkSwitch-10      50482033
           | 23.00 ns/op         BenchmarkSlice-10       100000000
           | 10.90 ns/op
           | 
           | 1.19:                   BenchmarkMap-10         14947908
           | 76.78 ns/op         BenchmarkSwitch-10      49444435
           | 23.01 ns/op         BenchmarkSlice-10       100000000
           | 10.74 ns/op
           | 
           | edit: If I understand the release notes correctly the switch
           | optimisations are only new for large integers and strings.
        
       | edf13 wrote:
       | Has this been fixed in V8 now?
        
       | KMnO4 wrote:
       | This is one of those gotcha that trap senior devs way more than
       | junior devs. Growing up with C being my first language, I
       | intuitively associate switch with a jmp table (ie extremely
       | fast). Even though I knew it's unlikely to be as performant in
       | JS, it never occurred to me that it would actually be slow, until
       | I did a deep dive into low level JS optimization.
       | 
       | Related: Matt Godbolt's talk on emulating 6502 in JS. He also
       | mentions the poor performance of switch for opcodes:
       | https://youtu.be/7WuRq-Wmw5o
        
         | fenomas wrote:
         | > the poor performance of switch for opcodes
         | 
         | I think you've slightly misunderstood TFA. The issue there
         | wasn't that the switch statement was slow, per se - it was that
         | V8's optimizing compiler didn't yet support such statements, so
         | V8 was bailing out of its JIT step and executing the function
         | via a (dog-slow) interpreter. In other words the function would
         | have run painfully slow even if it returned before reaching the
         | switch statement.
         | 
         | It's also maybe worth adding that gotchas like this were
         | _super_ common way back in the early days TFA describes, but
         | AFAIK they are no longer a concern now. Back when V8 's
         | optimizer was primitive it used to bail out for all kinds of
         | reasons - because it saw a "try()" statement, because a
         | function was too long, or even just because it got confused.
         | Nowadays it's a different world - I do a lot of JS perf work,
         | but it's probably been two years since I've seen V8 deopt
         | anything for any reason.
        
           | ncmncm wrote:
           | I think you _also_ misread the article. It clearly said that
           | an optimized switch would still run as a series of  "if"
           | tests, and that when he got the compiler to generate that
           | code, it wasn't faster.
        
             | fenomas wrote:
             | TFA doesn't cover any cases where switch statements got
             | optimized. It starts with the relevant code getting
             | deopted, then the author tries a fix but the code is still
             | deopted, then he removes the switch statements.
             | 
             | (Mind you I'm not saying switch statements are fast - they
             | may well be slower than the alternatives even after being
             | optimized. I'm just pointing out that TFA isn't about the
             | performance of switch statements, it's about avoiding
             | deopts.)
        
               | fps_doug wrote:
               | TFA references a post that states that optimized switch
               | statements in V8 are still just if-elseif-else and not
               | jump tables, at least back when it was written. They
               | might be faster than unoptimized JS, but certainly not as
               | fast as jump tables. The author didn't even bother to
               | split up the functions/switch blocks further when they
               | learned about the requirements to get V8 to actually
               | optimize a switch, as it seemed not worth it.
        
               | fenomas wrote:
               | Sure, none of that is in doubt. But in my experience the
               | performance cost of hot code getting deopted is so
               | massive as to overwhelm virtually any other factor, so
               | realistically, fixing the deopt is what solved TFA's
               | problem. Of course getting rid of the switch statements
               | was presumably an additional speedup, but since TFA
               | doesn't compare those cases I guess that's neither here
               | nor there.
        
               | whizzter wrote:
               | The if-else behaviour comes from the EcmaScript spec,
               | check the sibling comment.
        
               | whizzter wrote:
               | The EcmaScript switch spec basically says that each case
               | statement in turn is evaluated until one becomes equal or
               | default is encountered, so to follow the spec the basic
               | case is equal to a bunch of if-else's.
               | 
               | To optimize the runtime first has to figure out that all
               | case values are constants (and insert guards to de-opt if
               | it doesn't hold anymore), so in essence the machinery for
               | running this code optimized has to be very conservative
               | (and probably why they placed arbitrary limits that
               | really aren't suited for emulators).
               | 
               | https://tc39.es/ecma262/multipage/ecmascript-language-
               | statem...
        
           | ruuda wrote:
           | V8 did not have an interpreter in 2013.
        
             | nickelpro wrote:
             | Of course it did, how do you think a JS engine runs code
             | that it doesn't have any type information about without an
             | interpreter?
        
               | Dylan16807 wrote:
               | With a bunch of dynamic dispatch.
               | 
               | Interpreters and basic compilers are remarkably similar,
               | and it's easy to transform between them if you do some
               | extra busy work. Once you have your final AST, instead of
               | outputting bytecode instructions, output the
               | _implementation_ of each instruction. Or vice versa.
               | 
               | You have code that handles different types in the
               | interpreter? Great, bake it into the compiled code.
               | 
               | The transform from bytecode interpreter to compiler is
               | always pretty simple, but the other way around gets more
               | difficult the smarter the compiler is. So yes it is very
               | possible to have a baseline javascript compiler and no
               | interpreter.
        
               | nickelpro wrote:
               | I guess I'm differing on this point. By "baking in" the
               | dynamic dispatch the final compiled code is, in my mind,
               | an interpreter. Which is how I always thought of the
               | "Full Codegen" step of the old v8 architecture.
        
               | Dylan16807 wrote:
               | Well let's say you wrote C++ where most of your data is
               | in a super union of float, string,*, bool, int, map*,
               | etc, with gobs of repetitive dynamic dispatch whenever
               | you touch one of those unions. That wouldn't mean your
               | code is being interpreted, would it?
        
               | ReadTheLicense wrote:
               | Could you please explain what do you mean by type
               | information? I thought you never have type information in
               | JavaScript... - except the base types (bool, number,
               | string, object, array, function), but you always have
               | this information...
        
               | fenomas wrote:
               | Basically, modern JS engines are fast because they do a
               | lot of inferring about types. They do this both
               | statically (by inspecting the code at compile time) and
               | dynamically (by watching the code as it executes), and
               | they do it for objects as well as literals.
               | 
               | For a hand-waving theoretical example, if you have code
               | like:                   var a = { foo:1 }         var b =
               | { foo:2 }         var c = { foo:3 }
               | doSomething(a)         doSomething(b)
               | doSomething(c)
               | 
               | then modern JS engines can see that the "doSomething"
               | function always gets called with arguments of the same
               | type signature, and if it's a hot function they might
               | optimize the function around that information.
               | 
               | If you want to know more, searching on "V8 hidden
               | classes" should turn up relevant articles.
        
               | dchest wrote:
               | The bytecode interpreter appeared in 2016
               | https://v8.dev/blog/ignition-interpreter Before that, it
               | compiled everything into machine code.
        
           | madacol wrote:
           | What is TFA?
        
             | cleerline wrote:
             | The Freaking Article
        
               | dotancohen wrote:
               | The _Fine_ Article.
        
           | hinkley wrote:
           | > because a function was too long,
           | 
           | Famously, function inlining decisions were based on the size
           | of the function, not the size of the AST of the function,
           | which lead to situations where removing inline comments
           | allowed a function to be faster.
        
         | ChrisRR wrote:
         | I'm the same. I've always worked in C and would've naturally
         | assumed it would've executed as a jump table.
        
         | phkahler wrote:
         | >> I intuitively associate switch with a jmp table
         | 
         | Me too, but I try to keep the set of values dense and use a
         | mask (op & 0xff) to help the compiler know it doesn't need to
         | do bounds checking. Verify these changes help of course.
        
         | teruakohatu wrote:
         | > would actually be slow, until I did a deep dive into low
         | level JS optimization
         | 
         | Can you explain why this is?
         | 
         | Intuitively it would seem like optimizing a switch statement
         | would be really low-hanging fruit. I would have thought it
         | would be optimized into something almost exactly like what the
         | author hand rolled.
        
           | TAForObvReasons wrote:
           | You can switch on arbitrary types in JS and you can add
           | expressions:                   switch(3) {            case
           | true: break;           case "ab" + "c": break;           case
           | Math.random() >= 0.5 ? "heads" : new Error("tails") : break;
           | }
           | 
           | The optimizer will have to be persuaded that the switch and
           | each case is integral before optimizing with a jump table. On
           | the other hand, Arrays typically have a fast path for integer
           | indices.
        
             | teruakohatu wrote:
             | That explains it, but shouldn't a switch with string or
             | numerical constants (or an expression made up of constants)
             | just be optimized away anyway?
        
               | jcfields wrote:
               | The article quotes a Stack Overflow post with the
               | conditions under which V8 will optimize the switch
               | statement:
               | 
               | > @LGB actually in V8 (JS engine used by google chrome)
               | you need to jump through a lot of hoops to get switch
               | case optimized: All the cases must be of same type. All
               | the cases must either be string literals or 31-bit signed
               | integer literals. And there must be less than 128 cases.
               | And even after all those hoops, all you get is what you
               | would have gotten with if-elses anyway (I.E. no jump
               | tables or sth like that). True story.
               | 
               | In the case of this emulator, it wasn't being optimized
               | because it had more than 128 cases.
        
               | teruakohatu wrote:
               | > In the case of this emulator, it wasn't being optimized
               | because it had more than 128 cases.
               | 
               | Now that makes sense. Thank you.
        
             | shultays wrote:
             | But in js you can have maps of arbitrary types as keys
             | right? So jump table could just use that
        
               | mattashii wrote:
               | that's not quite true, because arbitrary expressions can
               | change meaning due to e.g. overloading of functions.
               | 
               | E.g. Random.random() will have to be called each time
               | that case branch is evaluated, because that function
               | could be overloaded or change its returned value.
        
         | syntheweave wrote:
         | I don't know. Maybe it's because I've spent too long thinking
         | about such things, but interpreter dispatch routines are one of
         | those areas of code where micro-optimizations are very
         | pernicious and one should expect to review the landscape each
         | time to rediscover best practice. See all the ways of compiling
         | and executing Forth words for examples.
        
         | dehrmann wrote:
         | In my first job out of school, I was writing driver code in C.
         | In one of my projects, I had a code block that wasn't working,
         | but all it did was a switch on ints. On a hunch, I tried ifs,
         | and it worked. Turns out I found a bug in the in-house GCC
         | fork. I'm sure my manager thought I was an idiot--I was a new
         | grad saying my project isn't working, and I think it's a
         | compiler bug.
        
           | throwawaysleep wrote:
           | Did you stick to it? I admittedly would have conceded I was
           | wrong in that situation.
        
             | dehrmann wrote:
             | I did! The compiler team agreed that it was broken and gave
             | me a flag to disable jump tables. Surprisingly, that didn't
             | cause any noticeable performance regressions.
        
           | carlmr wrote:
           | We always assume compilers to be infallible, and for the most
           | part this is right. But special compilers for special targets
           | or certified compilers for special uses, I've found to often
           | have compiler issues. Why did you have a GCC-fork?
        
             | dehrmann wrote:
             | I wish I could remember. It wasn't a secret sauce thing, so
             | all I can think of is you get improved support and faster
             | bugfixes.
        
           | georgecmu wrote:
           | What processor was that for? I remember (1999-2000) we found
           | out the hard way that GCC for Hitachi H8s had that exact bug.
        
             | dehrmann wrote:
             | I think it was a generic mips CPU.
        
       | rkagerer wrote:
       | _2013 was a different time, and JS engines have come a long way
       | since then. I really hope they've made this better than it was._
       | 
       | Have they? Do the 128 case limit and signed integer literals
       | constraints still exist?
        
         | rkagerer wrote:
         | Here's a similar case from 2020:
         | https://stackoverflow.com/a/59570825
         | 
         | With a link to the V8 line of code defining the 128 limit:
         | https://github.com/v8/v8/blob/596d0ce7b7a3900c529025aaa77e5f...
        
           | RichieAHB wrote:
           | Worth noting that SO comment calls out that the linked line
           | points to the code as it was in 2013 and that there have been
           | changes since then:
           | 
           | > The link into the V8 source code above is pretty old
           | (2013), so I tried to find the modern equivalent. I didn't
           | find a hard limit, but found several heuristics that decide
           | between table lookups and tree (binary search) lookups (ia32,
           | x86). When I plug in my numbers I don't quite get a
           | borderline case where I found it, so I'm not sure this is the
           | actual cause or whether there's another optimization not
           | being triggered elsewhere.
        
           | klelatti wrote:
           | Interesting thanks. Is there a good reason for a relatively
           | low fixed limit like this?
        
       | greggman3 wrote:
       | I like function tables. I think they are cleaner than switch
       | statements
       | 
       | But, (maybe this is an invalid test), checking today my results
       | suggest a switch is faster than a function table in 2022? At
       | least at a base level. Maybe the more complicated the code for
       | each case gets the less likely it is to get optimized.... which
       | is true in general? large code blocks are less likely to get
       | optimized and a switch is considered one large code block.
       | 
       | https://jsbenchit.org/?src=5c1e5bcf62448b6d1750b12893b0f0a1
        
         | whent wrote:
         | Are function tables really all that cleaner? Honest question.
         | If I can offer a counterpoint a function table requires us to
         | code some function dispatch routine that looks at the intention
         | (keywords in function table), then looks up the function, then
         | finds a way to bind the arguments to the function (language
         | specific) and finally call the function? Isn't this more
         | complexity in code?
         | 
         | Isn't switch-case simple because it is available as a construct
         | in the language?
        
         | no_time wrote:
         | Staggering difference between Spidermonkey and JSCore. I hope
         | the situation isn't as dire on linux :/
        
         | masklinn wrote:
         | The article is a decade old.
         | 
         | In the meantime the entire v8 execution pipeline has been
         | rewritten: a new optimising compiler was swapped in, a baseline
         | interpreter was added, and the baseline compiler was removed
         | (in more or less that order iirc).
         | 
         | Wouldn't at all be surprising if that lack of optimisation had
         | been long fixed.
        
           | vanderZwan wrote:
           | Nitpicking on an otherwise insightful comment: the article is
           | recent, but the period in which the optimization was made was
           | decade ago.
        
       | game-of-throws wrote:
       | I wonder why the deopt cutoff is 128 and not a nice round number
       | like 256. It seems like switching on the value of a byte would
       | not be that uncommon.
        
         | baobob wrote:
         | Don't know the guts of the optimization, but 128 64bit pointers
         | is 1024, which is a round number. Could be to avoid memory or
         | cache blowup, or maybe they're using that 8th bit for something
         | internally
        
       | dado3212 wrote:
       | > I was used to dealing with obfuscated code from Minecraft
       | 
       | I wonder how common this is for engineers. I've been in industry
       | for the last 4-5 years or so, and it was how I got a pretty big
       | jump into programming. Talked to a few similarly aged engineers
       | at FAANG and a couple others got into programming through
       | Minecraft as well.
        
         | Jaygles wrote:
         | My first useful code I wrote was writing scripts to beat
         | Neopets games
        
         | wiseowise wrote:
         | Isn't Minecraft Java?
        
           | a_t48 wrote:
           | Yup
        
         | difu_disciple wrote:
         | Bypassing cheat protections for Maplestory
        
         | saagarjha wrote:
         | I know several people who are experts at reverse engineering,
         | obfuscation, and runtime patching solely because they did
         | Minecraft mods.
        
         | bobsmooth wrote:
         | I imagine Roblox will have been a similar inspiration for
         | developers in a few years.
        
         | machiaweliczny wrote:
         | For me it was bots for RuneScape
        
           | girvo wrote:
           | Same, and writing bespoke keyloggers for RuneScape GP
        
         | sdflhasjd wrote:
         | Minecraft is too new for me, but I did get most of my pre-
         | professional experience from modding in video games, other
         | people I've worked with can say the same.
        
       | jraph wrote:
       | I understand that switch-cases may not be optimized well in JS,
       | but what surprises me is that all the instructions in the cases
       | are now grouped into functions and _this_ is faster than regular
       | switch case.
       | 
       | When I write Javascript, I expect function calls to be slow. So I
       | see three possibilities:
       | 
       | - (unoptimized) switch statement is _very_ slow in JS, so slow
       | that function calls will be faster
       | 
       | - function calls are not that slow in the end.
       | 
       | - both
       | 
       | It's a shame that the switch statement cannot relied on to be
       | fast though. They are very readable, and indeed, feel like code
       | that can be highly optimized. I guess that would risk making the
       | JIT compilation too slow, or that not enough code found on the
       | web rely on switch statements for this to matter too much.
       | 
       | Is this still true today? (I've seen a comment that says the 128
       | limit is still in V8's code)
       | 
       | How is this handled in SpiderMonkey, JavascriptCore or other JS
       | engines?
        
         | fenomas wrote:
         | TFA is basically about a limitation in V8's compiler, circa
         | 2013. Based on the bug report it looks like that limitation was
         | gone a few years later, and either way V8 switched to a whole
         | different compiler back in 2017 or so.
        
       ___________________________________________________________________
       (page generated 2022-08-08 23:02 UTC)