HNHacker News
TopNewBestAskShowJobs

ack_complete

834 karma · joined January 8, 2022

submissionscomments
ack_complete··on Paint.net 5.2 alpha now runs on Linux
Direct2D has some complex algorithms because it was designed for high-quality antialiased rasterization on DX9-class hardware, particularly tesselation, and suitable not only for vector graphics but also text glyphs. This bleeds over into the API, which is quite complex and somewhat annoying at times. It revives the GDI-style interface of needing to create and select a solid brush object instead of just passing a color, and drawing text with colored spans is embarrassingly involved (it involves writing a custom rendering bridge between Direct2D and DirectWrite).

The other problem is that Wine has only implemented a minimal amount of the Direct2D API to get specific programs working that aren't heavy graphics programs. Two major omissions, the last time I looked, were that Wine's implementation does not support antialiasing at all, and its ArcTo() draws a line.

ack_complete··on GUIs should be fully keyboard-driven
The keyboard support in "Modern" Windows apps is so random. In the new Notepad, the dialog that appears asking if you want to create a new files shows Yes and No buttons, but it doesn't accept Y and N keys, only Esc and Enter. However, the dialog that appears when exiting without saving does accept S and N for Save and Don't Save. These inconsistencies are everywhere in UWP.
ack_complete··on SIMD in the 90s: Programming Intel's Pentium MMX
MMX did help significantly with software 3D rendering. I worked on a software rasterizer that benefited significantly from it. But it didn't take long before even a well-optimized software renderer on a high end CPU couldn't keep up with a low end GPU on a low end CPU.
ack_complete··on SIMD in the 90s: Programming Intel's Pentium MMX
MMX in did not become irrelevant with the GeForce 256. Hardware video decoding was only in its infancy at the time and even the highest end GPUs only supported motion compensation acceleration for decoding only at best. Non-display image processing on the GPU was heavily bottlenecked by very slow read-back speeds from the GPU to the CPU across the AGP bus.
ack_complete··on SIMD in the 90s: Programming Intel's Pentium MMX
> I believe that only the first Pentium 3 core, Katmai, did this.

No, all Pentium 3s as well as the Pentium M. Pentium 4 notably didn't suffer from it, but it of course had many, many, MANY other performance issues.

> I have some faint memories of hearing somewhere that Long Mode didn't support x87. I wonder if it is related to this early info you mention and it being Microsoft specific.

It was VM86 mode that Long Mode didn't support, which was one of the rumored reasons for removing 16-bit NTVDM support (among many). x87 and MMX were always supported in long mode and notably some libraries like OpenBLAS still use x87 instructions. Windows does prohibit use of x87/MMX in kernel mode where the need is negligible.

ack_complete··on SIMD in the 90s: Programming Intel's Pentium MMX
MMX had heavy adoption in image and video processing. IDCT, motion prediction/compensation, YUV/RGB conversion, and alpha blending all benefited from it.
ack_complete··on SIMD in the 90s: Programming Intel's Pentium MMX
I did extensive MMX and SSE2 optimization of audio and video codecs in the 2000s. MMX made a large difference, but it was a pain.

MMX optimization practically required assembly language. The Pentium MMX was an in-order dual pipe CPU, and while compilers supported MMX intrinsics, their code generation for it was abysmal. Visual C++ 6, for instance, would emit code that was 2/3rds register-to-register moves, with values being unnecessarily moved between two registers between each ALU op. This was also a problem with SSE/SSE2 intrinsics. The worst case I saw was the _mm_set_epi8() intrinsic, which was used to construct a 128-bit vector from 16 inputs. When used with all constants, it should have generated a single 128-bit constant load; instead, Visual Studio 2008 generated ~80 instructions to compute it from byte loads. Microsoft didn't fix it until VS2010.

The latency of MMX instructions combined with the in-order dual pipe architecture also made asm loops messy. Simple ops were single-cycle, but multiplies had 3 cycle latency, stores required data an additional cycle in advance, and computed load/store addresses were also needed a cycle in advance. Simply running 2-4 iterations in parallel wasn't an option as there were only 8 vector registers and you'd still get bottlenecks on functional units. Getting peak performance thus often required interleaving loop iterations with special entry and exit code around the loop.

The issue with EMMS is understated. When the CPU switched to MMX, it marked the entire x87 stack as full. If you forgot the EMMS instruction, it wasn't just some strange floating-point bugs that would happen -- the next few x87 floating point calculations could just outright produce NaNs due to FP stack overflow. Furthermore, as these NaNs propagated, the CPU required microcode assists to handle them. So, even if the program didn't crash, an entire calculation domain would get poisoned and slow down by ~20x.

Ultimately, I don't think SSE2 was what killed MMX, but rather SSE, and specifically floating point. MMX not only didn't support floating point, but was also highly concentrated on 16-bit signed integers and secondarily 8-bit unsigned integers. Support for 32-bit integers was particularly lacking and pack/unpack conversions were a bottleneck. Trying to do 3D was cramped because doing so required fixed-point and MMX didn't have the same affordances as DSPs or NEON for rounding or implicit narrow/widen in operations, or even swizzles. SSE, on the other hand, was just straight floating point with standard automatic IEEE rounding and also had important added operations like swizzles and insert/extract. Thus, when 3D took off, SSE was far more useful than MMX.

MMX, however, still remained useful for a while for image and signal processing. SSE2 being twice as wide didn't help algorithms that couldn't use the greater width, such as 8x8 block motion prediction. Additionally, some CPUs at the time only had a 64-bit data path and had to split SSE2 ops, but because of their 4-1-1 decode template, could only decode one such instruction per cycle. The result was that code using the MMX registers could still run noticeably faster than with the SSE registers. This caused some confusion with the 64-bit version of Windows since Microsoft tried to say that x87/MMX shouldn't be used in long mode, but after queries from video processing companies had to document that the x87/MMX registers were enabled and context switched for user mode code.

ack_complete··on Why tiny JPEGs look different in Chrome
I wonder if this is a gamma correction related. JPEG encodes directly in a non linear color encoding (full-range YCbCr), so the partial decoding may be effectively scaling without gamma correction.
ack_complete··on How Claude marks AI-generated content
Moreover, what if you quote text that happens to have been generated by Claude, does that bump up the AI-ness score of your source file or document?
ack_complete··on How Claude marks AI-generated content
We already have one, our Claude setup already requires output to be 7-bit ASCII clean and scans it for such.
ack_complete··on Windows 11's built-in Weather app wastes more than 1 GB of RAM
The stacks are allocated gradually, but there's a minimum few committed pages for each thread and there can be a lot of threads. The graphics driver alone will typically spawn a thread per CPU core unless you specifically disable threading optimizations, and that's separate from the thread pool.
ack_complete··on Windows 11's built-in Weather app wastes more than 1 GB of RAM
Hardware texture compression formats aren't really great for UI and result in visible artifacts, even with a good compressor -- mostly flat areas or gradients divided by sharp edges makes the compression loss more visible. BC3/DXT5 in particular can only encode interpolated colors between two endpoints in each 4x4 block, so two colored regions divided by a line in a third color gets smashed.

Alternate encodings like YCoCg tend to do better.

ack_complete··on Windows 11's built-in Weather app wastes more than 1 GB of RAM
No interior pointers, no embedded arrays, and only a subset of Java definitely seems like an odd target. I would have expected at least two different full languages with acceptable porting overhead to be the MVP.
ack_complete··on Windows 11's built-in Weather app wastes more than 1 GB of RAM
> The thing that the task manager doesn't tell you is whether or not these are shared components. It may be that the 662 MB used by the "Renderer" is shared between many Windows components, so killing that Weather app may not reclaim as much space as you may hope, instead, it would require killing every user of the component, some may be core system apps.

It is the other way around, shared memory causes Task Manager to _underestimate_ memory usage. Task Manager's default views report the process private working set, no shared memory included. This means that 662MB is the _minimum_ amount of memory commit that would be released by ending the process.

> the OS can overcommit

Windows does not allow overcommit by default. It may compress or optimize memory allocations to reduce the physical working set, but the kernel will start failing memory allocations once physical + swap is exhausted regardless.

ack_complete··on Pushing the limits of RISC-V emulation
Odd that the Wikipedia article gives 2021 as the first description of this technique when it is far, far older than that. I worked with a software rasterizer JIT that used it in ~2003 and I thought similar techniques were used in the classic MacOS m68k emulator on PowerPC.
ack_complete··on SIMD for Collision
For a while it looked like ARM was trying to call the ARMv8 vector instruction set ASIMD instead of NEON, but they seem to have thrown in the towel as the ARM Intrinsics Guide simply refers to Neon.
ack_complete··on Software rendering in 500 lines of bare C++
My own knowledge of GPU rasterization may be dated, but IIRC GPUs tend to rely on guard band clipping up to a guard band threshold before using geometric clipping. The guard band clipping involves rejecting 2D coarse rasterization blocks that are fully outside of the scissor rect. This is just a quick rectangle check, but the tradeoff is that a larger guard band means more GPU time lost in over-rasterization and potential higher precision requirements for rasterization values (which could be fixed point). Beyond the guard band, the triangles are clipped in floating point against the frustum clip planes.
ack_complete··on Exapunks (2018)
Opus Magnum is one of the most polished Zachtronics games IMO. The presentation is great.

Exapunks can be pretty tricky with the distributed nature, which share some similarities with TIS-100. Like Opus Magnum, though, there are no restrictive code size limits, meaning that some puzzles can be solved with brute force masses of code. It's not as bad as Shenzhen I/O where you have to deal both with a tiny MCU and routing.

ack_complete··on FFmpeg 9.1's new AAC encoder
Most games use the sound support that comes with their game engine or choice of sound system, so I don't think the lack of an STB version is an issue. Performance is more of a problem. Audiokinetic, the makers of the popular Wwise audio system, estimate that Opus takes ~3-5x the CPU of Vorbis:

https://www.audiokinetic.com/en/community/blog/a-guide-for-c...

ack_complete··on Historical memory prices 1960-2026
Some tasks simply require more RAM. Compiling big software, for instance, wants as many CPU cores as it can get, and each compiler instance needs some amount of RAM to run efficiently. It's not unusual for a 32-core build to need 32-64GB of RAM to run at full speed. Work on a smaller program, though, and 16GB is absolutely fine.
ack_complete··on Computed goto for efficient dispatch tables (2012)
MSVC can do this for x86 if the cases are densely packed and the default is blocked: https://gcc.godbolt.org/z/GPo9fMx8G

However, this optimization is less effective on modern CPUs with more powerful indirect predictors using global history.

ack_complete··on The time the x86 emulator team found code so bad they fixed it during emulation
Doesn't require any special flags, just hitting optimizer limits can do it with MSVC.

https://www.reddit.com/r/cpp/comments/1i36ahd/is_this_an_msv...

ack_complete··on Win16 Memory Management
Some of this was automatically handled by the compiler and wouldn't have been an issue. Current x86-64 ABIs, for instance, require function entry to use specific forms annotated by metadata to support stack walking to support exception handling. Like the far entry here, this is invisible to most programmers -- the compiler does it for you.

Similarly, while locking and unlocking memory blocks is no longer generally a concern, most programs still deal with files, and graphics programs still have to call map/unmap functions to access graphics data. All the same tools apply -- helper functions/libraries, RAII, and leak/sanitizer tools to dynamically detect usage errors.

ack_complete··on Should you normalize RGB values by 255 or 256?
There is an analogous situation in graphics with signed normalized formats. The solution there is that the R16_SNORM format maps -1 to +1 as [-32767, 32767] with -32768 being a special value (not normally emitted, and mostly but not always interpreted as -32767). Some audio storage formats seem to use this mapping too.
ack_complete··on How do I inform Windows that I'm writing a binary file?
It's both. Originally Visual C++ binaries built for DLL-based C runtime relied on MSVCRT.DLL and that was installed by the redist. Starting with Visual Studio .NET 2002, separate CRT DLLs starting with MSVCR70.DLL were used. MSVCRT.DLL is now part of Windows to support parts of the OS itself and for compatibility with programs that still use it. I think some versions of MinGW also use MSVCRT.

Current versions of the OS ship with functions in MSVCRT.DLL that weren't in the last VC6 version, such as the updated C++ exception handler (__CxxFrameHandler4). AFAIK, there is no redistributable version of it, it's unique to the OS.

ack_complete··on Integer Overflow Checking Cost
That's with a simple data operation and using a recent x86 vector ISA (AVX-512) that is only available on some systems, notably excluding any current Intel desktop CPU.

The real killer isn't the data operations, though, it's if the overflow checks interfere with converting the loop logic or data addressing to vectorizable form. Indexing with 32-bit signed int vs. unsigned int on a 64-bit platform in C is a classic case -- with unsigned the compiler cannot assume that addressing offsets don't wrap, which then prevents coalescing data accesses into vector loads and stores.

ack_complete··on You can beat the binary search
Note that CPUs have also gotten dramatically wider in both execution width and vector capability since you were a teenager. The increased throughput shifts the balance more toward being able to burn operations to reduce dependency chains. It's possible for your idea to have been both non-viable on the CPUs at the time and more viable on CPUs now.
ack_complete··on Windows Server 2025 Runs Better on ARM
Two issues.

First, regarding application compatibility: the heap was already changed once prior to the segment heap. The Low Fragmentation Heap (LFH) was added in XP and made default in Vista, with applications no longer having to opt into it:

https://learn.microsoft.com/en-us/windows/win32/memory/low-f...

Second, the segment heap has different tradeoffs that make it not a guaranteed win to swap in, it trades off performance for working set:

https://issues.chromium.org/issues/40138716

ack_complete··on Writing string.h functions using string instructions in asm x86-64 (2025)
The REP MOVS series of instructions have an interesting history due to the advantages and disadvantages of microcode and its shifting performance relative to manual code with each CPU generation. It has long been great for aligned large copies due to the microcode having access to cache-wide copies, but until recently struggled with small copies. Apparently, one of the reasons is a lack of branch prediction in microcode:

https://stackoverflow.com/questions/33902068/what-setup-does...

Non-temporal stores are tricky performance wise. They can be dramatically faster than normal stores (~3x), they may be faster on some generations of CPUs than others, they may be slower if subsequent code needs the destination in the CPU cache, and even for GPUs they may not be ideal if an iGPU is sharing part of the cache hierarchy with the CPU. But the worst issue is that occasionally a specific CPU will have some random pathological behavior with them. IIRC, masked non-temporal stores were horrifically slow on some AMD APUs, on the order of hundreds to thousands of cycles per instruction. I find it hard to recommend them much anymore.

ack_complete··on It's OK to compare floating-points for equality
Yeah, that's effectively quantization, which will not work for general tolerance checks where you'd convert float similarity to int similarity.

There are cases where the quantization method is useful, hashing/binning floats being an example. Standard similarity checks don't work there because of lack of transitivity. But that's fundamentally a different operation than is-similar.

← PreviousPage 2 of 11Next →