GrantMoyer
For example, you can donate to Sci-Hub with cryptocurrency. Sci-Hub has an ideological mission which is widely supported, but it runs afoul of copyright law and is illegal in nearly every country and sanctioned in many. You cannot donate to Sci-Hub with fiat currency.
Unfortunately, I agree that in practice >99% of cryptocurrency's use is for scams.
[1]: https://web.archive.org/web/20260312134021/https://developer...
[2]: https://web.archive.org/web/20260328133800/https://developer...
The line doesn't exists. It's most profitable for the company to repeatedly:
1. Have an ad-supported tier and a no-ads tier
2. Wait for most people to move to the no-ads tier
3. Introduce ads into the no-ads tier
4. Wait for people to get sick of ads
5. Introduce a new, higher cost no ads tier
There is no threshold where it's more profitable for the company to never introduce ads at that price point.
Superficially, that benchmark and this one seem to be taking similar measurements. They both measure encoded size versus encoding time versus perceptual quality metrics, although they present the data on plots with different axes. I haven't tried to reproduce the results of either, but I have no reason to doubt either benchmark.
I wonder what accounts for the difference. Maybe the libavif encoder got a lot better in the last 2.5 years. The older benchmark uses libjxl v0.10.0 and libavif v1.0.3, while the newer one uses libjxl v0.12.0 and libavif v1.4.2. Or maybe it's the set of images used. The older benchmarks aggregates results for small test sets of images, while the newer benchmark tests individual images. The newer benchmark does find an image where libjxl wins on file size (the weasel), but it still loses on encoding time, whereas in the older benchmark libjxl almost always won on both. Or maybe it's the hardware (M3 Pro vs. M4 Pro).
[1]: https://cloudinary.com/blog/jpeg-xl-and-the-pareto-front
I've read that contracts fundamentally require a "meeting of the minds", where two parties come to a mutual understanding of an agreement. At the same time, overwhelming people are expected to not read and understand contracts they agree to. I don't understand how these two facts don't combine to mean the vast majority of written contracts are not legally binding, but apparently judges don't share my confusion.
[Are they hot?] -No> <Ignore>
|
Yes
v
[Are they wearing a ring?] -Yes> <Ignore>
|
No
v
…
I don't even know what that last step would be. It's not going to be sex. The woman is at work and the two don't even know each other.Maybe that's not what the author is thinking. I don't know, because I can't understand what the author is thinking. I'm alienated.
I think it's a good thing to keep in mind, that the actual implementation of Rust almost certainly isn't sound. Even an "ideal Rust" still has unsafe, which can affect the soundness of even faraway safe code. But I don't think it's nessesarily wrong to talk about Rust as if it were safe. Much of our communication has implicit assumptions, and the way we talk about Rust is no different. It would be exhausting to qualify every discussion of saftey with "assuming the compiler has no bugs", "assuming all unsafe code follows the (not fully specified!) rules", etc.
Ultimately it's like any other implementation of theory into practice. The theoretical model of Rust lets us reason about and design an implementation to achieve certain practical goals even though the model is a simplification of reality. In Rust's case, practical use has demonstrated it achieves design goals like reducing bugs and reducing development time (compared to peers like C++) to a substantial degree.
> Surely the compiler can just check if each access is valid.
Well, sometimes it can, but sometimes it doesn't know how long the array is. What if the array is passed as a pointer?
> Maybe each array could be annotated with its size at runtime, and accesses could be checked at runtime too.
That works, but it adds runtime cost that may legitimately be too much for some applications, for example, a Gameboy game (set aside that many Gameboy games were written in assembly).
> Fine, so we'll make the programmer promise to ensure array accesses are always valid. Maybe they'll make a mistake sometimes, but what's the worst that could happen? Throwing your hands in the air and saying the compiler is allowed to do anything, that's just stupid.
Well, maybe it's stupid, but this is one thing that could happen if you accidentally write past the end of an array: https://www.youtube.com/watch?v=Vjm8P8utT5g. I'm sure neither the programmers nor compiler writers intended that.
Ultimately, the compiler can't guarantee any behavior if its assumptions are violated. The example may seem contrived, but it demonstrates that, given the right circumstances, the results of the logical contradiction are unbounded. This is a direct consequence of the "Principle of explosion": https://en.wikipedia.org/wiki/Principle_of_explosion. On second thought, maybe the runtime costs of array bounds checking are an acceptable trade-off after all.
There's a caveat where some style guides permit using a comma without a conjunction in specific contexts, but most don't.
Formally, most comma use is an issue of style, not grammar, and style guides differ on when they should and shouldn't be used. The main rule that's actually a matter of grammar is this: commas cannot be used to join two "clauses" (phrases which could be independent sentences) without a conjunction.
The idea, then, is to migrate all git history to a stronger hash before that happens. If we waited until an attack was found, it'd be too late; all git history would be suspect forever into the future (disregarding extensive auditing), even if a stronger hash was used retroactively. Full SHA-256 doesn't have known practical collision attacks, and it doesn't even have known research avenues likely to lead to practical collision attacks, so is considered more future-proof than SHA-1′.
This idea of future-proofing is common in cryptography. For example RSA-1024 keys are now considered practical to factorize (with a supercomputing cluster and a few months), but that's okay, because, foreseeing the possibility, "everyone" switched to RSA-2048 or stronger a decade ago. Now we're adopting new key algorithms to protect against possible quantum computing attacks in the future.
1. Git users all transition to SHA-256 while git hosts stay on SHA-1.
2. Once almost all Git users are on SHA-256, Git hosts flip a switch and convert all hosted repos to SHA-256. Git users on SHA-256 don't notice anything, because the only thing that changes is how the client and server negotiate which objects to send.
3. Git hosts disable SHA-1 support. Git users on ancient clients need to upgrade or switch hosts.
Instead, by the author's account, Git hosts are deciding to convert repos to SHA-256 individually and placing the decision of when to do each conversion upon their users, who may not be informed about the situation. To me, that seems like the wrong decision, but then I don't run a Git hosting service.
No doubt other tools will take some time to catch up too. For example, libgit2 is perhaps the most popular third-party Git client, and it still doesn't support newer Git features like reftables (circa 2020). Though libgit2 does appear to implement SHA-256.
Notably, a few of the featured author's reservations appear to be addressed. According to the Git docs:
- Objects can be referred to by their old, SHA-1 name or their new, SHA-256 name. This means old refs in docs and comments and such remain valid. The mapping between SHA-1 representations and SHA-256 representations appears to be intentionally bijective a.k.a. 1-to-1 (assuming no hash collisions), so that it could be re-computed on demand. The constraint of bijectivity appears to be the source of some limitations, ex. no mixed repos and submodules needing to match hash algroithm, but also bijectivity has strong benefits like the following items.
- A bi-directional dictionary is maitained from SHA-1 to SHA-256 names so translations between the two don't required re-hashing objects. This table could be recomputed on demand due to the bijection between names; it's only a performance optimization.
- A local SHA-256 converted repo (including an SHA-256 converted submodule) can interoperate with an SHA-1 only remote transparently to the remote server by translating names using the lookup table.
- SHA-1 based GPG signatures will be preserved. A commit can be signed based on its SHA-1 representation, its SHA-256 representation, both, or neither. The bijection means the two types of signatures are in a sense interchangeable, or in other words the bijection between object representations implies an equivalence relation on signatures. An SHA-256 converted repo can quickly validate an SHA-1 based gpg signature using the lookup table.
From an infrastructure point of view, providing everyone vegetarian food sources overnight would not be possible, but that's not a realistic scenario. With a gradual transition, the transition would reduce overall resource use on basically all fronts, so there's no reason to expect extraordinary problems.
Of course, this is all theoretical unless people care enough to change their lifestyles.
Instead, the patent would cover running object detection/classification ML models — optionally on remote servers — on screen and audio output and user input of specific applications (particularly videogames), without requiring cooperation from the application, to target ads based on what's happening in the application and to decide when to interrupt the application with an ad.
To hazard a guess: Microsoft or part of Microsoft is planning to include pervasive targeted advertising in the next Xbox console OS, including during gameplay of any game.
So there could be a "instructions completed" counter and a "cycle" counter, and before starting the benchmark, you record the current value of both counters, then after the benchmark, record the final values, and compute the Instructions Per Cycle (IPC) as the difference in instructions completed over the difference in cycle count.
As for optimization, at a high level it's about minimizing the amount of time any part of the CPU is waiting for other parts of the CPU. The specifics require a lot of background knowledge about how modern CPUs work, more than can fit in a post, but if you're interested, topics to read about include:
### CPU cache hierarchy
CPUs store copies of data from RAM in smaller, faster memory physically closer to where the computation happens, so it's available more quickly. The CPU decides what values to store in the cache, and gives only limited control to the program, so an optimal program needs to be careful to not make the CPU make bad caching decisions (including for synchronizing the cache between multiple threads of execution).
### Instruction pipelining and out-of-order execution (a.k.a. "superscalar" execution)
Modern CPUs operate like an assembly line. A new instruction can start executing before the previous instruction(s) finishes. CPUs also have redundant hardware, so multiple instructions can be in progress at the same step of the pipeline. But there are limitations; sometimes the input to one instruction depends on the output of the previous instruction, so the whole pipeline stalls until the result is ready. An optimal program orders its operations to avoid these stalls as much as possible.
### Branch predicition
When the code to execute depends on the result of a computation, like in an `if` statement, we call it a branch. Branches can stall the pipeline, because the CPU doesn't know what instructions to execute next until the current result is ready. However, to mitigate this, modern CPUs predict which code-path will be taken when a branch is reached and begin executing the associated instructions immediately. If the prediction is right, the pipeline stall is avoided, but if it's wrong the pipeline state has to be restored to what it was before the wrong branch started executing, which is even more expensive than a stall. Usually, the CPU predicts branches correctly, so branch prediction is a net gain. Optimal programs need to understand how the CPU makes these predictions and make their branches as predictable as possible.
### Single Instruction, Multiple Data (SIMD)
Some programs do the same operations to each item of a set of data. CPUs have so-called SIMD instructions to accelerate this by, unsurprisingly, performing the same operation to multiple items at once. For example, they could add 4, 8, or even up to 64 pairs of numbers at once, depending on the instruction set and the range of the inputs. Suitable programs are optimized by arranging their inputs and operations so that SIMD instructions can be used — either directly or by being written in a way that a compiler can translate individual operations to SIMD operations.
Perhaps you're thinking of mathematics.
If you have to be able to represent arbitrary abstract logical constructs, I don't think you can formalized the whole language ahead of time. I think the best you can do is allow for ad-hoc formalization of notation while trying to keep any newly introduced notation reasonably consitent with previously introduced notation.
On the contrary, I'm certain such a program would be filled with fascinating techniques, and I have no dread for the idea that humans aren't special.