2,386 karma · joined August 29, 2011
Of course, many GCs have indeed gotten better in their own right, but not using the GC at all remains the most potent optimization.
- Inconsistent use of floating-point precision, which can arise surprisingly easily if e.g. you had optional hardware acceleration support, but didn't truncate the intermediate results after every operation to match the smaller precision of the software version and other platforms.
- Treating floating-point arithmetic as associative, whether at the compiler level, the network level, or even the code level. Common violations here include: changing compiler optimization flags between versions (or similar effects from compiler bugs/changes), not annotating network messages or individual events with strictly consistent sequence numbers, and assuming it's safe to "batch" or "accumulate" deltas before applying them.
- Relying on hardware-accelerated trigonometric etc. functions, which are implemented slightly differently from FPU to FPU, since these, unlike basic arithmetic, are not strictly specified by IEEE. There are many reasons for them to vary, too, like: which argument ranges should be closest to correct, should a lookup table be used to save time or avoided to save space, after how many approximation iterations should the computation stop, etc.
func equiv(x, y float32, ignoreBits int) bool {
mask := uint32(0xFFFFFFFF) << ignoreBits
xi, yi := math.Float32bits(x), math.Float32bits(y)
return xi&mask == yi&mask
}
with the sensitivity controlled by ignoreBits, higher values being less sensitive.Supposing y is 1.0 and x is the predecessor of 1.0, the smallest value of ignoreBits for which equiv would return true is 24.
But a worst case example is found at the very next power of 2, 2.0 (bitwise 0x40000000), whose predecessor is quite different (bitwise 0x3FFFFFFF). In this case, you'd have to set ignoreBits to 31, and thus equivalence here is no better than checking that the two numbers have the same sign.
[1]: https://en.wikipedia.org/wiki/Capability_Hardware_Enhanced_R...
- There is no unsafe block, instead certain operations are "restricted", which currently causes them to emit warnings that can be suppressed on a per-module basis; it seems the warnings will turn into exceptions in the future
- There is no "fixed" statement and frankly nothing like it all, native code is just not allowed to access managed memory period; instead, you set up an arena to be shared between managed and native code
- MemorySegment is kinda like Memory<T>/Span<T> but harder to actually use because Java's type-erased generics are useless here
- Setting up a MemoryLayout to describe a struct is just not as nice as slapping layout attributes on an actual struct
- Working with VarHandle is just way more verbose than working with pointers
The first bullet is possible with the JetBrainsRuntime, a fork of OpenJDK: https://github.com/JetBrains/JetBrainsRuntime
The second bullet is a headline goal of Project Valhalla, however it is unlikely to be delivered in quite the way that a C# (or Go or Rust etc.) developer might expect. The ideal version would allow any object with purely value semantics [1] to be eligible for heap flattening [2] and/or scalarization [3], but in experimental builds that are currently available, the objects must be from a class marked with the "value" qualifier; importantly, this is considered an optimization and not a guarantee. More details: https://openjdk.org/projects/valhalla/value-objects
The third bullet (IIUC) is addressed with the Foreign Function & Memory API, though I'll admit what I've played around with so far is not nearly as slick as P/Invoke. See e.g. https://openjdk.org/jeps/454
[1] value semantics means: the object is never on either side of an == or != comparison; the equals and hashCode methods are never called, or are overridden and their implementation doesn't rely on object identity; no methods are marked synchronized and the object is never the target of a synchronized block; the wait, notify, and notifyAll methods are never called; the finalize method is not overridden and no cleaner is registered for the object; no phantom or weak references are taken of the object; and probably some other things I can't think of
[2] heap flattening means that an object's representation when stored in another object's field or in an array is reduced to just the object's own fields, removing the overhead from storing references to its class and monitor lock
[3] scalarization means that an object's fields would be stored directly on the stack and passed directly through registers
There is nothing in a piece of random software obtained from some random source that authoritatively connects it with a particular domain. Without bringing an App Store or other walled garden into the picture, the operating system must evaluate an executable file according to the contents of the file itself. On cold launch, the information in the certificate can be presented to the user, and the certificate issuer can be checked against the O/S trust store, but nothing equivalent to the HTTPS domain check can be done.
DV certs work for the web because of that intrinsic connection between web site and domain. They fail for arbitrary software because of the lack of such a connection. The trustworthiness of code-signing certs comes from the relatively difficult process necessary to obtain them, and not the name attached to them. The identifiable legal entity to which the certificate was issued is more useful to the O/S vendor, as a harder-to-evade ban target, than it is to the end user.
It's possible that they could start issuing separate certificates upon specific request for code signing purposes, but it's doubtful they would be willing to meet Microsoft's requirements for such certificates, so their code-signing CA would not be added to Windows's trust store, rendering the certificates it issues useless.
The ACME protocol, the key automation technology that makes Let's Encrypt possible, performs domain validation only. It verifies that you, the person (or bot) making the request, are an authorized administrator of the DNS records, or port-80 HTTP server, for that domain. This is directly relevant to, and generally considered sufficient for, HTTPS.
However, domain validation is almost completely irrelevant to, and insufficient for, code signing. Microsoft's rules (and Apple's, incidentally) require establishing the identity of a legal person (individual, or preferably, company). There is no way the ACME protocol can do this, which means that the process is totally out of Let's Encrypt's wheelhouse.
1) Passive mode. What is it and why do I need it? Well, you see, back in the old days, .... It took way too long for this critical "option" to become well supported and used by default.
2) Text mode. No, I don't want you to corrupt some of my files based on half-baked heuristics about what is and isn't a text file, and it doesn't make any sense to rewrite line endings anymore anyway.
3) Transport security. FTPS should have become the standard decades ago, but it still isn't to this day. If you want to actually transfer files using an FTP-like interface today, you use SFTP, which is a totally different protocol built on SSH.
The rationale is basically that newcomers to Python should see the results that they would expect from grade school mathematics, not the results that an experienced programmer would expect from knowing C. While the PEP above doesn't touch on division by zero, it does point toward the objective being a cohesive, layman-friendly numeric system.
It's just a bizarre design choice. I understand wanting to get rid of condition flags, but not replacing them with nothing at all.
EDIT: It seems the same choice was made by MIPS, which is a clear inspiration for RISC-V.
First, the code claims to be returning "unsigned long" from each of these functions, but the value will only ever be 0 or 1 (see [1]). The code is actually throwing away the result and just returning whether overflow occurred. If we take unsigned long *c as another argument to the function, so that we actually keep the result, we end up having to issue an extra instruction for multiplication (see [2]; I'm ignoring the sd instruction since it is simply there to dereference the *c pointer and wouldn't exist if the function got inlined).
Second, this is just unsigned overflow detection. If we do signed overflow detection, now we're up to 5 instructions for add and mul (see [3]). Considering that this is the bigger challenge, it compares quite unfavorably to architectures where this is just 2 instructions: the operation itself and a branch against a condition flag.
[1]: https://gcc.gnu.org/onlinedocs/gcc/Integer-Overflow-Builtins...