you know what makes the news overwhelming to digest? choosing to drink from the mouth of the firehose, and depending on repeating stories to see them, instead of a clean historical record to pour over.
there would be a lot less news if people went back and read what they missed, instead of waiting for it to get brought up again.
Microsoft has some real issues with regards to privacy, security, and performance.
An ETW event on main entry/exit is none of the above.
It's worth yanking from Microsoft's POV because "Telemetry" is a big bad scary word, but from my POV it's a pointless distraction from actual privacy, security, and performance concerns.
It's somewhat analogous to how we went from 16 bit registers to 32 bit registers with the extended opcode variants, so if you understand x86, x86_64 isn't really much of a stretch.
I only have a vague understanding of assembly in general, and x86_64 in general - but I thought that with so many more registers, the recommended style of programming can change quite a bit (hence aggressive use of registers for passing arguments to function calls in the x86_64 C ABIs)?
That's less of an assembly issue and more of a compiler/calling convention issue.
Assembly allows you to pass parameters on the stack or in registers.
Calling conventions simply define a protocol for doing this consistently.
Actually Id'd say that it's mostly the crappy processor design issue: for example, UltraSPARC has 32 physical registers in 32-bit mode, and 256 virtual registers (through register windows, specifically designed for compilers). Even Motorola 68000 with eight general purpose address registers and eight general purpose data registers is far more elegant than a 32-bit four register intel CPU.
intel CPU is just crap from a design standpoint, and since they had to remain backward compatible, it's gotten a lot faster with lots and lots of tricks, but it still sucks in 32-bit mode. No amount of tricks will change that. It has to be run in 64-bit mode to gain a performance boost and simplify the code, whereas processors with fixed 32-bit instruction encoding run faster in 32-bit mode and code simplicity is a constant.
eax is the accumulator register, ebx the base register, ecx the counter register, edx the data register, esi is the source index, edi is the destination index,
ebp is the base pointer, and esp is the stack pointer.
When I originally wrote four general purpose registers, I had eax, ebx, ecx and edx in mind, but after fully listing them above, I revise my earlier statement: the x86 assembler has two general purpose registers. Crappy processor architecture with lots of specialized registers, but too few general purpose ones.
Compared to MOS 6502, Motorola MC680##, or SPARC, only the eax and edx are really general purpose registers -- even mentioning ecx, the counter register, would be iffy.
6502 has only accumulator anyways. X and Y are not general purpose.
68k is pretty nice, d0-d7 registers are indeed interchangeable. Of course a0-a7 are just for addressing, I think a7 was usually stack pointer.
SPARC I've never programmed, so no comments about it.
I've written x86 code in the past (20 years ago) using all 8 registers for general purpose task -- yes, even ESP. It was faster that way to implement a texture mapper. Ugly but fast.
Those 8 x86 registers are mostly general purpose, apart from some exceptions.
Multiplication was the only annoying one, getting result in EAX:EDX.
I always succeeded making x86 do whatever I wanted, despite some limitations with register use.
Indeed. While repurposing ESP might be rightfully considered ugly, repurposing EBP is quite common and EBX, ECX, ESI, EDI pretty much are general purpose because nobody has been using them for their fixed functions for two decades.
I agree that stosb/rep would probably be confined to memory management, and saving/restoring register around such ops isn't the end of the world. Not sure about movsb -- I suppose if you're copying enough data, store/restore is going to be negligible overhead in terms of speed, but if you're actually trying to write clear code, it would certainly be easier to not have to worry about the book keeping?
I don't know what's the current status quo, but most of the time after 80286/80386, "rep stos" and "rep movs" has been significantly slower than just (loading and) storing data in an unrolled loop. This limits usefulness just to very short spans or when code size is most important. But most short spans are also predictable (static), so compilers can often just generate an instruction or two to do so (like xor eax, eax / mov <target>, eax).
Currently the fastest way to memset large chunks of memory is probably to use SSE or AVX. I'd guess this is what gets generated if compiler target arch allows.
With SSE/AVX you also have an option to use non-temporal moves to avoid polluting caches. This might have a negative impact on any memset micro-benchmark [1], but significantly help any concurrently executing memory bound CPU cores.
Properly aligned (cache line 64-byte boundary) you might be able to avoid read-for-ownership as well, further reducing memory bus traffic.
So most use of rep-prefix might be pointless, unless you can accept the performance hit.
[1]: Just like micro-benchmarking any other resource constrained operation. Micro-benchmarks can give you very wrong idea of what is best for the system as a whole.
Those instructions are treated as "legacy PITA" by CPU vendors and, being complex and harder to implement than simpler ones, aren't implemented as efficiently.
The CPUs have lots of duplicated logic to process many instructions in parallel and, on "friendly" code, can sustain average throughput of 2 or more instructions per clock cycle, provided that the instructions are simple enough.
The end result is that a loop made with normal adds, cmps and jnes outperforms those dedicated looping instructions.
They are only used by compilers when optimizing for code size and maybe by people who want concise hand written assembly, though I'm not sure why wouldn't they just use C in such case.
See "Software Optimization Guides" released by AMD/Intel for more info.
I hate to be a naysayer because this is great work and all, but frankly, managing all these platforms is going to be a nightmare. In a traditional engine, the code for each platform is embedded at the lowest level of abstraction. That is the way it works for iOS and android at the moment, but adding new platform support via extensions will make the code harder to manage and reason about. I don't want a separate extension for OSX, Windows, and Linux. I want all the code about, say, filesystem handling to be in a single place (in neatly separated platform specific files). This applies to networking code, rendering, input, etc. I know at the bottom of the article there is hope of integrating it with the original project, but given that we are starting literally on the opposite end of the spectrum, I'm worried we will never reach this hypothetical nirvana. The original react native team needs to prioritize getting other platforms in the original codebase, and I may be mistaken, but I have not yet observed this, and so have some cause to worry.
I think these are salient points. Part of my rationale for writing it the way I did is that howistart is slightly editorial in nature and is a collection of opinionated overviews of a professional workflow by design. In the real world, C++ is never used in vacuum (since its runtime is a very thin layer on top of the operating system). Also, C++ is generally chosen because it compiles on many platforms, and awareness of meta-make systems and how executables are structured and run is important in a professional working environment.
This is why I make it clear at the beginning that the article isn't meant to be an introduction to the language (non-goal!) and also point out references for a beginner to learn from later. I also mention that learning a single IDE first and learning it well is a good idea.
The speed of the GC isn't the issue (although it certainly can be). The non-determinism is. It's hard to control when a GC should happen, and when it happens at a bad time, the ramifications are perceived as an awful user-experience.
At PlexChat[1], we intend on using C++ to write a lot of our infrastructure (where it makes sense, that is). The language has advanced significantly in the last decade and writing idiomatically correct, and safe code, is much easier than it used to be.[2]
Garbage collection is certainly a part of it, but really, it's about programming with a deterministic runtime. Controlling memory budgets and doing things described in this talk is possible in an environment where the programmer has control, but instrumenting a blackbox runtime to identify performance bottlenecks and pain-points can be a huge endeavor. Other languages optimize understandability of the programming semantics (what is this algorithm doing) but do very little to aid in expressing runtime semantics (how will this algorithm execute on the machine).
> Other languages optimize understandability of the programming semantics (what is this algorithm doing) but do very little to aid in expressing runtime semantics (how will this algorithm execute on the machine).
I think this is painting other languages that aren't C++ with too wide a brush. There are many non-C++ languages that make it "easy" to control the low-level generated code (though I question really how easy it is with how incredibly aggressive in optimization modern C++ compilers are).
Other languages, not all other languages ^^. Yes of course there are several languages that map more directly to one's mental model of how code is executed. Rust (which you're working on) is an one such exception. Optimizations are certainly aggressive but the micro-optimizations are usually either well known (like NRVO, loop unrolling, function inlining), or not important relative to more costly operations that are occurring (allocations of heap memory, fetching from the heap, executing a system call, acquiring a mutex, etc).