Related, and linked in the readme: <a href="https://github.com/xoreaxeaxeax/smiiiiiiiiiiiiiiii" rel="nofollow">https://github.com/xoreaxeaxeax/smiiiiiiiiiiiiiiii</a> (using the slow instructions to break SMI)
Bus cycles can be arbitrarily long on any processor that has memory cycles with a hand shake requiring an ack, with no timeout.<p>E.g. we can build a board around a MC68000 where we make it lock up forever in a bus cycle, waiting for a DTACK that doesn't arrive.<p>Some early microprocessors had clocked bus cycles without handshaking. They would put out an address on some address lines and signal some line together with a read/write indication, and then expect the transfer to be completed within some clock cycles. If nothing is attached to the address, they would read whatever values are on the bus, like maybe all 1's if it is an open drain system that requires the transmitting device to pull to ground to indicate zero.<p>I'd say that kind of thing belongs to a hall of shame; it requires software hacks to interface with anything that can't keep up with the prescribed bus cycle.
Bus cycles can also be arbitrarily long on those microprocessors if they don't use dynamic logic - you can stop the clock.
In general, more complex processors have latency issues, and in some ways modern chips have actually become worse with each design iteration.<p><a href="https://en.wikipedia.org/wiki/Metastability_(electronics)" rel="nofollow">https://en.wikipedia.org/wiki/Metastability_(electronics)</a><p>Ultimately... failure modes must arise because naive gate design simulation models can't determine issues in a computationally feasible time frame.<p>The consequences of "fixing" CDC prone design flaws makes a processor many times slower (8 to 16 times slower on my dumb attempt), and develops weird alien design features very different from Von Neumann architectures.<p>This is why we can't have nice things. =3
It says in the rules<p>> Trapped/emulated/virtualized instructions may only time the trap, not the handler.<p>But I feel like that 12ms write to an ACPI IO port at current leaderboard position 8 is probably trapping to SMM and being handled there.
This author also has other things like: A compiler that emits only `mov` instructions and another compiler that deliberately messes with the control flow so that, if disassembled, common debuggers will draw symbols like skulls or threats. <a href="https://github.com/xoreaxeaxeax/repsych" rel="nofollow">https://github.com/xoreaxeaxeax/repsych</a>
Depending on his interpretation of the rules about trapped instructions, one could just build a loop in the x86 page tables. Those are usually a tree linked by pointers, and any page table lookup can create another page fault that creates another lookup that...<p>Leads to x86 page table MMU magic being turing complete: <a href="https://github.com/jbangert/trapcc" rel="nofollow">https://github.com/jbangert/trapcc</a><p>And the simplest thing you can do on such a system is just to loop indefinitely, thus creating a simple instruction with a memory access (mov or anything, doesn't really matter, even the instruction fetch for a nop would work) to take infinite time.
<i>Nop</i> should be #1, because it is infinitely slow for what it does. ;)
It increments rip by one.
I thought I remembered reading somewhere re: the 8086 microcode disassembly that NOP, which is encoded as XCHG AX,AX actually does run the XCHG microcode and uses an internal scratchpad register to do the exchange.
No, that's done by the decoder. It actually does nothing.
It's still part of the instruction to increment it by one, as opposed to write a value or offset to it, as jump instructions do.
The decoder is an implementation detail that is a subcomponent of NOP; GP was right, and your correction isn't.
As specified by the spec, it arguably increments RIP by one.<p>The actual typical hardware implementation just fetches the next 16-32 bytes from icache, shifts it to the correct alignment, and slams it into a bunch of parallel decoders which each attempts to decode one x86 instruction per byte.<p>The next cycle, the first 1-6 non-overlapping valid instructions are accepted into a queue for further decoding. The NOP almost certainly takes up space in this queue.<p>At no point does RIP get incremented by one. There isn't even a single physical RIP register to increment, the CPU is "executing" dozens or even hundreds of RIPs in parallel.
It's not an implementation detail, because the decoder runs before the execution of <i>every</i> instruction. If we're going to say that NOP increments IP by one, then we should also say that ADD "stores in dst the addition of src and dst, as well as incrementing IP by the length of the instruction", and JMP imm "increments JMP by imm + the length of the instruction".
I mean....there are several architectures out there which has a nop that is a jump forward. Kind of a tree forest issue imho
Strategy: nop does nothing. It opens the leaderboard accordingly.<p>Score: 1 cycles
Time: 0 nanoseconds
It's a little faster than yep.
Does that mean Chris Domas is ready for his next adventure?
what im seeing from this chart is that we should be using the nop instruction for everything
I wonder if you can do some damage with scatter/gather ops within a VM, such that each fetch is a TLB miss inside the VM, and every table walk fetch is a TLB miss outside of the VM (which gets you up to 24 "fetches per fetch").
A great resource for any performance deoptimization.
62s for a single instruction! Wonder if an compilers cost tables knows that. But it's data dependent, and cost functions probably don't do that.
Related, somehow: Core War [0].<p>[0]: <a href="https://en.wikipedia.org/wiki/Core_War" rel="nofollow">https://en.wikipedia.org/wiki/Core_War</a>
This matches everything I've seen
Very cool! Also, huh interesting. I’ve used rdtsc to measure cycle diffs but had no idea its execution takes that long. Is that common across architectures?
The cycle count for RDTSC is ~25 cycles on Skylake-era microarchitectures. The 49 number shown in the OP seems off.
It's actually benchmarking 1000 repetitions of the RDTSC instruction running in parallel.<p>My guess... On Skylake, multiple in-flight RDTSC instructions slow each other down for some reason?<p>Possibly because it's attempting to provide a strict monotonic guarantee, that no two RSTSC instructions will return the same timestamp. Intel's manual only claims monotonic, which theoretically allows for two RSTSC instructions to return the same timestamp.
AFAIK it acts as some kind of execution barrier, to give meaningful timing.
PCIe is more like a packet-switched network than a bus, which is incidentally why things like Thunderbolt (effectively external PCIe) and sillier demonstrations like <a href="https://www.youtube.com/watch?v=q5xvwPa3r7M" rel="nofollow">https://www.youtube.com/watch?v=q5xvwPa3r7M</a> work.<p>...and with things like <a href="https://en.wikipedia.org/wiki/ExpEther" rel="nofollow">https://en.wikipedia.org/wiki/ExpEther</a> , you can get even higher latencies.
Pretty dumb right? When latency gets this high, you need a more asynchronous design to get any reasonable performance. PCIe is clearly designed with the assumption of latencies a few hundred cycles at most (or usually) - this MMIO register is an extreme outlier. It might be unmapped, and timing out on the hardware side, or it might be converted to an access on some really slow configuration bus.
It'd be really interesting to see whether the winning (losing?) instructions/strategies would be different on other architectures. At least right now the top spot (`fxrstor64` on MMIO, starve PCIe) seems relatively architecture-independent, but maybe something about MMIO ordering rules on e.g. POWER would be different enough to change that -- or perhaps open up new avenues?<p>I wonder what the actual limit on this `fxrstor64` is right now. If you can stall the PCIe bus for that long, then why not indefinitely? Certainly there's no forward progress guarantee here.
Oh wow, glad to see Chris Domas active online again!
just curious, how much do these actually discover useful practices or pitfalls, on top of just being for fun?
Domas manages to abuse x86 is ways that make me unsure whether or not I should be impressed or disgusted. I guess impressed, then disgusted over Intel (and AMD?).
Using MMIO is cheating and makes the results very boring.<p>It would be much more interesting to know the results if you're only allowed to use main memory.
There’s definitely strategies here; A lot of the floating point operations use subnormals, and a lot of the worst instructions are slowed down by really, really fucking with MMIO.
This is neat!
[dead]
[dead]
Sahil
It’s crazy how computers still seem to get perceivably slow every few years, given how many instructions can be executed in 1ms. Shameful, even..<p>What’s that law called about programmers wasting all the compute on abstraction?
There are two aspects to compute performance: latency and throughput.<p>There has been a ton of work to improve throughput, but that's often come at the cost of latency, because a great technique to improve throughput is batching, to avoid the per-task overhead cost.<p>We've also added many layers of abstraction.<p>A seminal example is Dan Luu's "computer latency" table (2017) <a href="https://danluu.com/input-lag/" rel="nofollow">https://danluu.com/input-lag/</a>, which measures the time between a keypress and visible changes on the screen, and wherein an Apple IIe has latency 5x lower than a Lenovo X1 Carbon on Windows.<p>From some perspective, you could say that the Lenovo on Windows example is an impressive feat of engineering, considering all the subsystems involved (USB, interrupt dispatcher, input layer, windowing system, double-buffering...)<p>This throughput-over-latency tradeoff can be seen across the entire computer landscape design.
Idk man, my computers don't seem to get any slower over time -- no upgrades to any components, either.
Wirth's Law I believe.
The new windows notepad is a disgrace
The new mspaint fucked, then unfucked, then <i>refucked</i> my decades-old muscle memory of Win+R mspaint Enter Ctrl+E 1 Tab 1 Enter Ctrl+V to open Paint, resize canvas to minimum, then paste from clipboard. When you press Ctrl+E now, the Units control is selected by default, for some completely asinine reason!!!
That's pretty cool. I've never needed to do that because Paint remembers the last canvas size set manually.
You aren't alone in being frustrated by decades of muscle memory being disrespected by MSFT.
i’m on LTSC for this (and many other) reasons lol! don’t touch my mspaint and notepad
Andy and Bill's Law
The OS should do less not more
Huh? A millisecond is an eternity!
Seems like spam from this creator since there are two things on the front page?
submitted by two different people, both with year+ old accounts and decent karma. i dont think either is the author. the other submitter probably read this one, looked at the github, saw something else cool and posted it. (i almost did the same, but bookmarked it instead)