HN user

broken_broken_

472 karma
Posts28
Comments38
View on HN
gaultier.github.io 21d ago

Optimization tales with CockroachDB: the slow logout

broken_broken_
1pts0
gaultier.github.io 22d ago

Optimization tales with CockroachDB: the slow password reset flow

broken_broken_
2pts0
gaultier.github.io 1mo ago

I sped up the test suite by 2x with one simple change

broken_broken_
2pts0
gaultier.github.io 6mo ago

The production bug that made me care about undefined behavior

broken_broken_
1pts0
gaultier.github.io 7mo ago

Detecting Goroutine Leaks with DTrace

broken_broken_
2pts0
gaultier.github.io 8mo ago

A million ways to die from a data race in Go

broken_broken_
4pts0
gaultier.github.io 9mo ago

How to reproduce and fix an I/O data race with Go and DTrace

broken_broken_
1pts0
gaultier.github.io 10mo ago

Observe live SQL queries in Go with DTrace

broken_broken_
3pts0
gaultier.github.io 11mo ago

A subtle bug with Go's errgroup

broken_broken_
27pts19
gaultier.github.io 1y ago

An optimization and debugging story with Go and DTrace

broken_broken_
2pts0
gaultier.github.io 1y ago

A subtle data race in Go

broken_broken_
1pts0
gaultier.github.io 1y ago

What should your mutexes be named?

broken_broken_
6pts0
gaultier.github.io 1y ago

Build PIE executables in Go: I got nerd-sniped

broken_broken_
2pts0
gaultier.github.io 1y ago

Making my static blog generator 11 times faster

broken_broken_
3pts1
gaultier.github.io 1y ago

Making my debug build run 100x faster so that it is finally usable

broken_broken_
87pts20
gaultier.github.io 1y ago

Addressing CGO pains, one at a time

broken_broken_
2pts0
gaultier.github.io 1y ago

Tip of the day #4: Type annotations on Rust match patterns

broken_broken_
1pts0
gaultier.github.io 1y ago

The missing cross-platform OS API for timers

broken_broken_
49pts33
gaultier.github.io 1y ago

The missing cross-platform OS API for timers

broken_broken_
2pts0
gaultier.github.io 1y ago

Way too many ways to wait on a child process with a timeout

broken_broken_
129pts32
gaultier.github.io 1y ago

Perhaps Rust Needs "Defer"

broken_broken_
74pts67
gaultier.github.io 1y ago

Lessons learned from a successful Rust rewrite

broken_broken_
146pts42
gaultier.github.io 1y ago

Tip of the day #2: A safer arena allocator

broken_broken_
23pts2
gaultier.github.io 1y ago

A small trick for simple Rust/C++ interop

broken_broken_
2pts0
gaultier.github.io 1y ago

Odin and Musl: Cross-Compiling Odin Programs for the Raspberry Pi Zero

broken_broken_
4pts0
gaultier.github.io 2y ago

How to rewrite a C++ codebase successfully

broken_broken_
6pts0
gaultier.github.io 2y ago

You've just inherited a legacy C++ codebase, now what?

broken_broken_
380pts347
gaultier.github.io 2y ago

Roll your own memory profiling: it's not hard

broken_broken_
2pts0

It depends how much the code uses manually spawned goroutines, and how complex the lifecycle of these goroutines is… in big codebases such as kubernetes, docker, etc, it has been a problem. There have been research papers and blogs about this, but most Go developers are not aware of this issue it seems.

I am not an expert in incident reaction, but I thought the safe way was to image the affected machine, turn it off, take a clean machine, boot a clean OS image with the affected image mounted read only in a VM, and do the investigation like that ?

Assume that the malware has replaced system commands, possibly used a kernel vulnerability to lie to you to hide its presence, so do not do anything in the infected system directly ?

Nice article, thank you. Did you also consider using bpftrace while debugging?

I do not have much experience with it, but I think you can see the kernel call stack with it and I know you can also see the return value (in eax). That would be less effort than qemu + gdb + disabling kernel aslr, etc.

Author here, thanks for the feedback on legibility, I have now just learned about the CSS `tab-size` property to control how much space tabs get rendered with. I have reduced it, should be better now.

I love a good SIMD article!

Just one point about testable assembly: I like the Golang guidelines which require to write a Go version before implementing the same in assembly. That way there is a baseline to compare with in terms of correctness and performance. Also the Go project has recently introduced mutation testing and test coverage on assembly code which is very interesting.

Finally about detecting at runtime the presence of simd: I am confused why a third-party dependency or even the std is needed. At least on x64, it’s just one instruction (“cpuid”) and then checking if a bit is set, right?

Your are right about shadowing: I just checked and the go specification calls it “redeclaration” and it works as you described:

Redeclaration does not introduce a new variable; it just assigns a new value to the original

I added a mention about that in the article. Thank you.

I agree with the premise, just use a specific version of your dependencies, that’s generally fine.

However: You absolutely do need a lock file to store a cryptographic hash of each dependency to ensure that what is fetched has not been tampered with. And users are definitely not typing a hash when adding a new dependency to package.json or Cargo.toml.

I wrote a toy Kotlin compiler, for fun. Then one day a Jetbrains employee opens an issue which only says: “Why? Just why?”. Maybe it’s the language barrier… but I did not find that particularly polite.

On the other hand I open sourced my blog and received lots of small contributions to fix typos or such which were nice.

I have implemented polling against a cluster of mixed mariadb/mysql databases which do not offer listen/notify. It was a pain in the neck to get right.

- The batch size needs to be adaptative for performance, latency, and recovering smoothly after downtime.

- The polling timeouts, frequency etc the same.

- You need to avoid hysteresis.

- You want to be super careful about not disturbing the main application by placing heavy load on the database or accidentally locking tables/rows

- You likely want multiple distributed workers in case of a network partition to keep handling events

It’s hard to get right especially when the databases at the time did not support SKIP LOCKED.

In retrospect I wish I had listened to the WAL. Much easier.

The x64 assembly would probably work natively on the Mac, no need for docker, provided the 2 syscall numbers (write and exit) are adjusted. Which llms can likely do.

If it’s an ARM Mac, under Rosetta. Otherwise directly.

About not having perf on macOS: you can get quite far with dtrace for profiling. That’s what the original flame graph script in Perl mentions using and what the flame graph Rust reimplementation also uses. It does not have some metrics like cache misses or micro instructions retired but still it can be very useful.

I have worked for financial services companies that used FreeBSD both in EC2 and on the metal in data centers (self managed). The two features we used all the time were zfs and jails. Each service ran in its own jail for isolation. One (not even beefy) server could run all the services which was insanely cost efficient. A cloud migration was undertaken at some point to have a hybrid setup, using a mix of Linux (k8s) and FreeBSD, and costs skyrocketed. It’s a trade off because in the data center we had to buy and replace our own disks, react to fires taking place, being only in one country etc. AWS gives you multi region, and tons of good stuff, and that has a price.

ZFS was not leveraged that much but it saved our beacon once when a table in the production database was accidentally dropped and we could instantly rollback to the previous zfs snapshot (there was a tiny bit of data loss as a result but this did not matter too much for this application - uptime was more important). ZFS was also used for backups I believe.

A few times I used dtrace in production to troubleshoot.

When we introduced Linux to our fleet of FreeBSD servers, every team picked a different distro organically so it was a bit of a zoo. With FreeBSD on the server you only have the one variant.

I still use and like both, but I must say I really like that FreeBSD is a kernel+OS integrated together.

I can tell you some financial services I have worked for do use FreeBSD on EC2 as well as on the metal in data centers to do millions of transactions a month. I like the OS, thanks for your work.

Good article, I also have a C program (a compiler) that I would like to compile to webassembly to offer a playground web page, so that is good information. Thank you!

About the file system stuff: modern browsers ship with SQLite which is available to JavaScript (is it available to webassembly? No idea) so I would probably use that instead. Ideally you could use the sqlite API directly in C and emscripten would bridge the calls to the browser SQLite db. Something to investigate.

I initially did not see the use case for sending hundreds of MiB to the browser, but near the end, data visualization is mentioned which is fair.

I could also see video games and observability platforms needing to do this, for example Datadog, which must send lots of stack traces with long function names along with numerical data.

I have done something similar for Linux under 2 KiB in assembly some time ago: https://gaultier.github.io/blog/x11_x64.html

As others have said, doing so in pure C and linking dynamically, you can easily remain under 20 KiB, at least on Linux, but Windows should be even simpler since it ships with much more out of the box as part of the OS.

In any event, I salute the effort! You can try the linking options I mentioned at the end of my article, it should help getting the size down.

Fantastic article! But when I read “inspired by the Gameboy form factor”, I thought the cartridge would insert much deeper, like a Gameboy (maybe not all the way so that the picture on the cartridge is still visible) . That way it cannot be accidentally yanked out. It’s probably a dust trap but it’s more robust and compact.

I love reading optimization stories so thank you!

So essentially the code got faster by switching to smarter regexp engine that can transform (hopefully) the regexp into a FSM. Cool, but what about replacing the regexp with straightforward parsing code written manually? That way it is statically known that this function is fast all of the time.

At least that’s what I would do in a natively compiled language with access to simd, not sure if ruby has access to it and can detect the best variant at runtime.

And I’d argue plain code is simpler to understand, debug, troubleshoot than a complex regexp.

The regexp engine part reminds me of writing scalar code, relying on the compiler doing autovectorization to make it fast. And what very often happens is that the new compiler version changed their heuristics and now there is no autovectorization that kicks in and the performance falls off a cliff . It’s a risky bet.

At work the Go backend was also using gob encoding in one case to store some values in the database. After some profiling, I noticed it was the top allocation part of the whole app. It was also showing on the CPU performance profiles. Finally some very light “fuzzing” (just a case were the wrong value was written to the database, and reading it back crashed the app) made me decide to rip it out and use JSON instead. Instantly, this section of the code disappeared from all profiles. I honestly do not understand why this format exists in the Go standard library and I would never use it. There are plenty other formats available…and a decoder should never crash in the face of invalid data!