HN user

charleslmunger

1,305 karma
Posts2
Comments443
View on HN

I mean this is sort of true in that windows is constantly introducing trash, but I've run into all kinds of nonsense:

1. Amd HDMI 2.1 fiasco and adapter workarounds -> debugged the adapter compared to the displayport spec pdf, emailed the company, got a firmware update, patched my kernel. Fixed! This one is going away for good with FRL support upstream soon.

2. Game stops responding to controller input after playing for a bit. Debugged, turns out the service for doing something fancy with shaders has shared fate with the steam input process. It launches a zillion threads and OOMs from virtual memory exhaustion which takes out steam input; fixed by adding a wrapper script for steam that reduces thread stack sizes to the windows default size.

3. Xbox elite series 2 controller back buttons not supported in 2.4ghz wireless mode; reverse engineered the USB packets, contributed support to out of tree xone driver.

4. Flydigi controller software not supported on Linux; find random GitHub project that reversed their hidraw protocol. It's got bugs, so fix them and use it.

5. Terrible banding in Silksong. Set up gamescope to apply dithering; this breaks steam input, figure out all sorts of incantations with LD_PRELOAD

But all of these are very much off the beaten path problems, lots of people have fun with normal controllers, no VRR, etc. My steam deck has been just perfect with zero effort, and I assume that's because I'm not treating the system configuration like a puzzle game.

I got a measurable improvement from eliminating a null pointer check within the last week. Billions of devices have arm little cores, and the extra branch predictor pressure and frontend bandwidth from those instructions can be significant.

A standard way to eliminate those is to invoke undefined behavior if some condition is not met;

    if (a == NULL) {
      __builtin_unreachable();
    }
Which then allows elimination of the null check in later code, possibly after inlining some function.

Wouldn't that make a compiler that emitted bounds checks violate the standard, since it would not be emitting the actual memory operations if you deref out of bounds?

Is that really a meaningful distinction?

Once you are addressing arbitrary values you are firmly in the realm of "anything happening" in practice, but you've now given up optimization opportunities. As has been repeatedly demonstrated over the years, once memory safety breaks it is practically impossible to make any guarantees about program behavior.

The Synaptics VMM7100-based adapters only support VRR on older firmware versions with bugs.

The Chrontel CH7218 is the most reliable but still also suffers blackouts during VRR.

ParadeTech PS196 adapters advertise VRR support but their DPCD does not correctly communicate that it is supported. So even if you add the chip to the VRR PCON list in the amdgpu driver, it still won't see VRR as supported.

And while some of these advertise themselves as displayport 2.0, all of them only support bandwidth of 25.96gbps on the displayport side, requiring DSC for 4k 120hz 10bit color, even though they support 48gbps on the HDMI output.

Yeah arm little cores are a very different story - they aren't superscalar out of order architectures, they can dispatch up to two operations per cycle.

Big cores are more like that dispatching 8 or more operations per cycle, but they're also more expensive, larger, etc.

The first implementation I encountered was a linear search, starting at the last-found field. Empirically it performed better to do a binary search with early exit and branchless bounds selection, I think due to branch predictor pressure. The data representation could be changed but it's tricky, as there are other traversals that want to go in sorted order, and there are lots of places that pass just one pointer for fields. But I agree any further improvement will probably have to come from that.

SIMD is tricky even with SoA because there is significant latency going between the general registers and the vector units, plus arm little cores can be configured to share a vector unit with another core.

Yeah namespaces and public/private would be quite nice, but C doesn't have them, so they get hacked on via macros and prefixing. The syntax was not the hard part of working or analyzing this code, though.

I've spent some brainpower on binary search and have not been able to beat this:

https://github.com/protocolbuffers/protobuf/blob/44025909eb7...

1. Check for dense list O(1) 2. Check upper bound 3. Constant trip count binary search

The constant trip count is great for the branch predictor, and the core loop is pretty tightly optimized for the target hardware, avoiding multiplies. Every attempt to get more clever made the loop worse and did not pay for itself. It's hard because it's an array-of-structs format with a size of 12, and mostly pretty small N.

I had fun exploiting this to detect the falling convention used by some code at runtime - there were two different options depending on OS version; one passed a jnienv* as the first param, the other did not. So if I called it with 0, I could tell which was being used based on whether the first argument was NULL or not. Only used for specific architectures with a defined ABI that behaved this way, of course.

Maintaining both MV2 & MV3 support isn’t easily sustainable long term when you factor in the need to prioritize other features.

There is no feature Firefox provides that is more differentiating than ublock origin. As long as pages load and security issues are patched it is the reason to choose Firefox as a browser. What would they prioritize over it?

Vite 8.0 Is Out 4 months ago

With Kotlin/Spring Boot, compilation is annoyingly slow. That's what you get with modern languages and rich syntax.

This is because the kotlin compiler is not written in the way people write fast compilers. It has almost no backend to speak of (if you are targeting the jvm), and yet it can be slower at compilation than gcc and clang when optimizing.

Modern fast compilers follow a sort of emerging pattern where AST nodes are identified by integers, and stored in a standard traversal order in a flat array. This makes extremely efficient use of cache when performing repeated operations on the AST. The Carbon, Zig, and Jai compiler frontends are all written this way. The kotlin compiler is written in a more object oriented and functional style that involves a lot more pointer chasing and far less data-dense structures.

Then, if run on a non-graal environment, you also have to pay for the interpreter and JIT warmup, which for short-lived tasks represents nontrivial overhead.

But unlike header inclusion or templates, which are language level features that have major effects on compilation time, I don't think kotlin the language is inherently slow to compile.

The default Gboard keyboard has settings for always showing the number row, or only showing it when entering a password. There is also a setting for the "suggestion strip" under "corrections & suggestions". You can also drag to resize the keyboard itself in the Gboard menu, to scale the height.

Now, whether your users will do that to play your game is a different story, but the options exist.

Not OP, but used Arch for a while in 2011, and at some point doing an update moved /bin to /usr/bin or something like that and gave me an unbootable system. This was massive inconvenience and it took me many hours to un-hose that system, and I switched to Ubuntu. The Ubuntu became terrible with snaps and other user hostile software, so I switched to PopOS, then I got frustrated with out of date software and Cosmic being incomplete, and am now on Arch with KDE.

Back then I used Arch because I thought it would be cool and it's what Linux wizards use. Now Arch has gotten older, I've gotten older, and now I'm using Arch again because I've become (more of a) Linux wizard.

You have a list of IDs, and want to make them compact for storage or transport - fast and simple way is to sort and delta encode.

C99, but with a million macros backporting features from newer language versions and compiler extensions. Lovely features you don't get with ordinary c99:

free_sized

#embed

static_assert

Types for enum

Alignof, alignas, aligned_alloc

_Atomic

It's so weird to see the leading heroin story phrased like a hypothetical, when:

1. Heroin itself was marketed as a "non-addictive morphine substitute", and sold to the public. It didn't become a controlled substance until 1914 (according to Wikipedia) 2. The opioid crisis was basically started and perpetuated by Purdue pharma, again marketing Oxycodone with the label “Delayed absorption as provided by OxyContin tablets, is believed to reduce the abuse liability of a drug.” and other more egregious advertising. 3. Britain went to war with China twice to force the Qing dynasty to allow them to sell opium there. 4. President Teddy Roosevelt's grandfather made a ton of money in the opium trade.

It's supposed to be sort of shocking hypothetical, except actually that's basically the history of the actual drug.

Critical section under 100ns, low contention (2-4 threads): Spinlock. You’ll waste less time spinning than you would on a context switch.

If your sections are that short then you can use a hybrid mutex and never actually park. Unless you're wrong about how long things take, in which case you'll save yourself.

alignas(64) in C++

    std::hardware_destructive_interference_size
Exists so you don't have to guess, although in practice it'll basically always be 64.

The code samples also don't obey the basic best practices for spinlocks for x86_64 or arm64. Spinlocks should perform a relaxed read in the loop, and only attempt a compare and set with acquire order if the first check shows the lock is unowned. This avoids hammering the CPU with cache coherency traffic.

Similarly the x86 PAUSE instruction isn't mentioned, even though it exist specifically to signal spin sections to the CPU.

Spinlocks outside the kernel are a bad idea in almost all cases, except dedicated nonpreemptable cases; use a hybrid mutex. Spinning for consumer threads can be done in specialty exclusive thread per core cases where you want to minimize wakeup costs, but that's not the same as a spinlock which would cause any contending thread to spin.

Unfortunately the standard library mutex is designed in such a way that condition variables can't use requeue, and so require unnecessary wakeups. I believe parking lot doesn't have this problem.