HNHacker News
TopNewBestAskShowJobs

GrumpySloth

4,901 karma · joined September 13, 2020

submissionscomments
GrumpySloth··on Energy Efficiency across Programming Languages (2017) [pdf]
Except SIMD code while faster may sometimes be less energy-efficient than scalar code. Different SIMD extensions have different energy efficiency characteristics. Also, multithreaded code may finish a task sooner, but spend more energy budget due to costs of synchronisation, spawning and joining threads, etc.
GrumpySloth··on Energy Efficiency across Programming Languages (2017) [pdf]
That may be the case for numerical code. However, a big niche for Python (if not the biggest one) is web services. How common is calling C++/Fortran code in a Django/Flask project? Those projects seem to have layers upon layers of pure Python code, with metaprogramming and whatnot.

Additionally, going through the FFI from C++ to Python and back to C++ has a cost. Eliminating this overhead was one of the motivations behind Julia.

GrumpySloth··on OOP “is really just a common sense extension of structured programming”
Something being common sense is not a good argument for it making sense. The most obvious demonstration of that is physics. Our common sense is hopeless at modelling the world around us accurately. Maths is better at that. In programming, similarly, algebraic properties and representation in memory should be preferred over a reasoning that something is just “common sense”.
GrumpySloth··on A 14kb page can load much faster than a 15kb page
That's only doable with a text-centric website. I'm currently finishing a photography section for my personal website, and the gallery pages are several hundred kBs in size, while single photo page is almost 1MB in size (provided you load it on a 28" screen; browser will load smaller variants on smaller screens). Most of that weight is the thumbnails (and almost-full-size photo in the latter case). The only JS I have is 6 lines (before minifying) on single photo pages which allow you to go to next or previous photo with keyboard arrows. I don't use any CSS frameworks, just a single hand-written CSS file. I don't have any tracking, analytics, social media features or anything of this sort.

So if even a personal site being done with no deadlines, no pressure from management to include analytics etc. can't do it, because it wants to display a bunch of photos, then I don't think we can expect "web-scale" websites to achieve it.

GrumpySloth··on Email doesn't suck – it's email clients that need improving
Forsaking HTML doesn't imply embracing command line.

In my experience, most mails don't actually use any styling beyond the automatic blockquote for the endless quote chains that mail clients automatically add. So people are wasting storage and transfer for something they don't really use.

When I see a mail which could actually use some styling, it's usually something involving maths and HTML is not suitable for that case. People usually send LaTeX snippets in plain text in this case.

And while I'm at it, the endless quote chains are another thing which has no reason for existing. Each mail in a thread steadily grows in size, because mail clients "conveniently" always quote everything and put the text cursor above it so that the user doesn't stop for a moment to look at the size of what they're sending and consider the absurdity of the situation, when their client is perfectly capable of reconstructing the whole thread without copying all mails in each message. Quoting should be opt-in on a case-by-case basis. As they function in the wild today, the quotes are just useless garbage attached to everything.

GrumpySloth··on Reference count, don't garbage collect
No-pause GC is possible, when we're talking about concurrent GCs, but it also shouldn't be mistaken for a "no-overhead GC". And reference-counting, as long as the actual reference counting isn't done in a separate thread as well, instead of blocking in a destructor, which triggers other destructors, is not a no-pause GC. It's true that application developer can avoid pauses even then, but that's a different claim.

From what I've been told, C++ games do another thing and just use arena allocators, which make allocations cheap (just an integer addition, provided you know a reasonable upper bound on the size of an arena, and if you don't, you may reserve a large piece of virtual memory, and commit it later in reasonably small, but also not too large chunks) and free-s even cheaper (either reset the integer, so that the arena can be reused, or a single syscall to unmap the whole arena in one go). That's a different strategy than making a lot of little allocs and frees, and then trying to minimize them by reusing objects, which is also quite hairy.

GrumpySloth··on Reference count, don't garbage collect
So, as I understand it, you avoid pauses by avoiding data structures with long chains of pointers. The same will work equally well in a language with a GC. It's also not the case that reference-counting itself doesn't result in pauses itself, but that the user is responsible for using such data structures that they do not result in pauses. Which I think is the only way when you care about performance, no matter whether you use manual memory management, reference counting or a tracing GC, so that's by no means a criticism of you or your language, I think it's very sensible. But I think that describing it as a no-pause memory management mechanism is a mischaracterisation.
GrumpySloth··on Embed is in C23
> you would implement it as a new type of token

That's a good point. I consider myself debunked.

GrumpySloth··on In defence of garlic in a jar
This article is really weird. This person developed insecurity about using jarred garlic, because they saw (or imagined) that someone in college looked at them wrong when they used it. Then they proceeded to look out for comments about jarred garlic on Twitter, because what people say on Twitter is important. And then they complain that the snobs don't think about disabled people when they behave snobby. What a surprise. When I say that running regularly is healthy for you, I also don't think about people who don't have legs, have deformed legs, or have other disabilities preventing them from running. What sort of a complaint is that?

Overall, it gives the impression that this person actively searched for something to feel insecure about and then wrote a looong article about it.

Also, it's weird to label mincing garlic yourself as snobby, when it's the cheap option done by almost everyone. Forget about finding jarred garlic in a pleb fridge. Hipsters with creative eating habits? Much more likely. And for people without applicable disabilities, jarred garlic doesn't save all that much time. Time spent mincing garlic barely registers compared to other activities in the kitchen. And jarred garlic is A LOT more expensive[1]. But if you have disabilities, it may be the rational choice, yup, no argument about that. And even when it's not the rational choice, who cares?

When I cook, I don't check on Twitter if my way of cooking is approved by snobs. Go on detox from Twitter. Your life will get much better than by fighting imagined ableism.

[1]: No surprise here really. Fresh food is almost always several times cheaper than processed food. After all, it's less work to make it.

GrumpySloth··on Mathematics and Magic: The de Bruijn Card Trick (2015)
De Bruijn sequences can also be used to quickly find the index of 1 in a word, when your CPU doesn't provide a dedicated instruction like lzcnt[1] (or when you're in a language competition which doesn't allow writing inline assembly). The paper talks only about 32-bit words, but you may find a 64-bits long de Bruijn sequence (B(2, 6)) on the Chessprogramming Wiki[2], which allows you to trivially generalise the algorithm for 64-bit words.

[1]: <https://www.researchgate.net/publication/2809440_Using_de_Br...>

[2]: <https://www.chessprogramming.org/index.php?title=De_Bruijn_S...>

GrumpySloth··on Ante: A low-level functional language
Explanation based on my familiarity with Koka:

This expression matches on the effect flip() and handles it with the code on the right hand side of -> which becomes the value of the function invoking the effect. In this case the handler runs the continuation for both possible values (resume true and resume false, i.e. the function was called once, but it will return twice, with true and false substituted in the place of flip() in the place which used this effect) and returns their mean as the average.

I.e. each time calculation calls flip(), the effect handler will continue the function for both true and false and then take the mean of those two results. Comments explain that flip() should simulate a coin flip, which generally gives equal probability (1/2) to true and false. Each flip() in calculation was simulated with equal number of true and false values, so it fits the expected distribution. Each time taking the mean of those values is just an expansion of the integral calculating the expected value, as per definition of expected value.

Note that there is some recursion here. While the flip() handler is executing for the first invokation of flip(), another instance of the handler will execute for second and third invokations (inside resume true). I.e. it calculates the expected value by taking the mean of the expected values of each branch created by a flip. When a branch doesn't have any more flips inside, its expected value is taken to be the constant value that this branch returns.

GrumpySloth··on Rust: A Critical Retrospective
To be fair, compilers and linkers can be thought of as pure functions (if we ignore stuff like including timestamps in builds e.g.). They have no special requirements language-wise. You can write them in any general purpose programming language. Even Brainfuck will do (for one-file programs fed through stdin). No argument about the others though.

Although I guess JIT compilers may be classified as systems programming, since you need to do some OS-specific tricks with virtual memory during execution of the compiler.

GrumpySloth··on The Rust Borrow Checker – A Deep Dive
> Anyway I don't believe you [...] Either you're bullshitting or you have restricted yourself to extremely simple Rust code

How kind.

> what does `for<'a>` and `+ '_` mean?

`for<'a>` allows you to introduce a new lifetime parameter which parametrizes an Fn argument independent of the lifetime parameters of the enclosing function/struct. Sounds complicated when you try to explain it in programming terms, but actually when you think about it in terms of logical quantifiers from maths (for<'a> being the universal quantifier introducing the 'a "variable", "for" -> "for all"), it's pretty simple. The idea is simple, the syntax is noisy. I've already complained about the noise in another comment: <https://news.ycombinator.com/item?id=31433427>

"+" in type constraints is just conjunction.

> C++ definitely gives you a head start when thinking about lifetimes, but there's still a huge difference between thinking about lifetimes, and writing them down to prove to a not-especially-smart borrow checker that they're ok. Obviously the latter is better, because it gives you guarantees. But it's still way way harder.

Getting a program to compile may be harder. But the whole task of writing a program long-term gets easier. Most of the complaints about borrow-checking I see online can be split into two categories:

1. syntax noise --- I agree

2. this program would work, if not for the borrow checker --- in almost all cases here people do not think about API boundaries at all. They look at the body of the functions they wrote and conclude that it should work. But they forget about one important thing: the compiler needs to ensure that changing the body of function f() without changing its signature should not break compilation of function g() which calls f(). That leads to examples where it seems as if something should compile, but doesn't, because of function boundaries which mandate that lifetimes should be correct regardless of function implementations. And there is a good reason for that: if I use a library, this library pushes out an update which doesn't change the types of anything, I don't want to be forced to update my code for it just to compile. So the work that ensures that everything stays composable throughout implementation detail changes is moved upfront.

The alternatives to "fighting the borrow-checker" are GC or difficult to debug (sometimes even to reproduce) bugs. Fixing the compilation is a lot easier for me than tracking down memory-corruption bugs I face in large C++ codebases with hundreds of contributors, so I disagree with the claim that making things work in Rust is harder.

GrumpySloth··on When I tell the Windows Terminal team something is simple, I am "misguided"
This criticism is addressed at code, not people.

> If it was your code they were talking about, you may feel differently.

I'm not the commenter you replied to, but no, I'm not my code, so I'm not going to get offended by someone criticising it. If someone does, it's on them.

There were a number of times in my career when someone called something I wrote overcomplicated, and I've never been even close to feeling any emotion about that fact at all. That remark always prompted me to hear carefully what they have to say about improving it. That's not a brag, just a certain level of emotional maturity.

People shouldn't be held responsible for every emotional reaction people have to their actions/words, because part of the responsibility for that rests on the person experiencing those emotions. In that case I don't think Casey said anything offensive, so if that made someone angry, they have themselves to blame for that.

GrumpySloth··on Ask HN: What methods do you use to focus and stay relaxed?
When I work on a side project, I pull out the Ethernet cable of my computer from the switch. Unfortunately, I can't do that in dayjob thanks to the tyranny of Slack.

When something doesn't involve a computer, I don't have the slightest problem staying focused. So when part of my job requires doing some maths on a piece of paper/window/whatever, when I want to read a book, when I engage in a hobby etc., I don't need to try hard to focus. It's only those bloody computers.

GrumpySloth··on Write plain text files
When I have a chain of commits pending review on Gerrit, and I fixed some flaws in the first commit in chain and need to rebase the dependent commits, merging is not an option. And rightly so: when I look at the master branch, I don't want to see random corrections someone made during review, they're just irrelevant once the reviewed changes are merged.

There are many reasons why linear history is important. Rather than saying that "you shouldn't want to do that", I'd prefer it if the tools people use were fixed to better serve the things people actually want to do.

A merge would only make sure that I don't need to resolve the same conflict multiple times. It wouldn't result in the conflicts not being generated in the first place, or them being saner. The only difference in conflict resolution between rebasing and merging is that the sides are flipped (what's shown on left in rebase, is on the right in merge, and vice-versa). Which doesn't address the second issue I listed: conflicts are not only repeated, but the conflicts themselves are pretty crazy. In my example, often-times C would never even touch files which were detected as having conflicting changes with A or B. It was pretty absurd that I would have a 3-line change in C, several hundred-line changes in A and B, yet the biggest conflicts would be triggered when rebasing C on top of B, and those would be in files C did not touch. Other times diffs in conflicts would have most of the lines added and removed actually identical, with only a couple in the middle different. Why would git mark them as conflicting, is beyond me. And then there was the issue of git not detecting conflicts, when it should have, instead merging changes in a way that broke source code. None of those issues is better handled by merging than by rebasing. Most of them however have pretty good solutions in a patch-based version control system, as opposed to snapshot-based like git (conflicts in files which weren't changed? not going to happen). An even better solution would be binary formats with domain-specific merge logic, like the one in KeePassXC.

GrumpySloth··on Words known better by males than females, and vice versa
Even if you make the distinction, "female" describes sex, not gender. So the acronym "AFAB" is a bit internally contradictory if you remember that it was created in order to underline the distinction. Then again, these days more often I see people denying the existence of sex, and replacing it with gender altogether, so maybe it's not that surprising after all.
GrumpySloth··on In defense of complicated programming languages
Type classes are a way to do polymorphism, but... dynamic dispatch? I don't think so; which function is called, is determined statically.

As for ML functors: they're neither polymorphism, nor dynamic dispatch. First: functions for different types can't share names (unless we're talking about shadowing of names with lexical scope), the names are either qualified with the name of the struct they come from or they're not qualified at all. In all cases there is exactly one implementation for every name. And again: no dynamic dispatch. MLton even defunctorizes all code at compile-time. The only polymorphism in SML is the one that's used in some built-ins (like +), but it's not available to users of the language. SML has something called "polymorphic functions", but it has nothing to do with writing multiple implementations of one function under one name and letting the compiler select which one is called; which is what is usually meant by "polymorphism".

GrumpySloth··on Never update anything
> "Breaking" is undefinable. I remember reading about some user complaining that a small bugfix broke their work setup because it fixed a bug that used to make the CPU go 100% when the spacebar was held. The user would hold it with a weight and get the CPU to make heat that way. The small fix broke his experience.

You confused XKCD with reality: <https://xkcd.com/1172/>

GrumpySloth··on Chicken Scheme
Does anyone here have any experience using Chicken Scheme in production? How did it go?
GrumpySloth··on A thorough introduction to bpftrace
bpftrace has lower overhead, because, thanks to BPF, it can accumulate all the data needed in kernel and only send a summary to userspace to be displayed. strace on the other hand uses the ptrace(2) syscall to set a breakpoint on each syscall, including the ones that you do not trace (when you filter calls e.g.) and on each syscall data travels between kernel and strace.

It's possible that in the future strace will use BPF as well to lower its overhead.

bpftrace is also more versatile. You could use it to e.g. collect stack traces for all the syscalls a program makes. You can also attach actions to more things than just syscalls. You can e.g. use kprobes to inject code into kernel functions which aren't exported as syscalls.

GrumpySloth··on Zip – How not to design a file format
> How do you read a zip file?

> This is undefined by the spec.

> There are 2 obvious ways.

> 1. Scan from the front, when you see an id for a record do the appropriate thing.

> 2. Scan from the back, find the end-of-central-directory-record and then use it to read through the central directory, only looking at things the central directory references.

I was recently bitten by this at work. I got a zip from someone and couldn't find inside the files that were supposed to be there. I asked a colleague, and they sent me a screenshot showing that the files were there, and that they didn't see the set of files that I saw. I listed the content of the zip using the "unzip -l" command. They used the engrampa GUI. At that point I looked at the hexdump of the file. What caught my eye was that I saw the zip magic number near the end of the zip, which was odd. The magic number was also present at the beginning of the file. At this point I suspected that someone used cat(1) to concatenate two zips together. I checked it with dd(1), extracting the sequence of bytes before the second occurrence of the zip magic number and the remainder into two separate files. And sure enough at that point both "unzip -l" and "engrampa" showed the same set of files, and both could show both zips correctly. Turns out engrampa was reading the file forwards, whereas unzip was reading the file backwards.

GrumpySloth··on “Shared libraries are not a good thing in general”
Even without LTO, linker will discard object files that aren't used (on Linux a static library is just an AR archive of object files). It's just a different level of granularity.
GrumpySloth··on The health benefits of better air
According to my naive knowledge:

1. We have more ozone holes now.

2. We weren't constantly in direct contact with sunlight. If you move past very early stages, we had shelter in forests, caves, tents, igloos.

3. When we were in direct contact with sunlight, we had a darker skin, which is more resilient to that.

Correct me if I'm wrong.

GrumpySloth··on Surprisingly Slow
In my experience, Magit is slow even on Linux. On my small repos at home, subjectively magit-status seems to take around 0.2-0.3 seconds. And that's just status, the most basic information you ask of git. Committing is several times slower. On a large codebase at work, magit-status usually takes around 10 seconds, sometimes longer. Again, I'm usually running it to just check some basic metadata (what branch I'm on, do I have a dirty tree, if yes, then what files are changed), so it's frustrating to wait. Honestly, I'd expect stuff like that to update effortlessly in real time without me issuing any commands. This is what happens in some other editors. However, currently I'm glued to Emacs because of Tramp for working remotely in a nice GUI and org-mode for time-tracking (TaskWarrior/TimeWarrior isn't for me).

I prefer Fork on Windows and Mac (prefer the Windows version for aesthetic reasons). Unfortunately, it's not available for Linux.

GrumpySloth··on Darker Corners of Go
The code, which would break, was already broken. Just in far worse ways than throwing exceptions: silent memory corruption. And there would be no backwards-compatibility breakage, since in cases, where it would throw, currently UB is triggered.
GrumpySloth··on A half-hour to learn Rust
Yup, similar is 3.5->3.7. The community also doesn't seem to value backwards-compatiblity, judging from the libraries.
GrumpySloth··on The Kate text editor is 20 years old
> 1. There are a lot of users that are just not comfortable with working at a shell prompt. Graphical editors mirror the basic Mac or Windows workflow (open folder, double click on file, edit) and key bindings.

Emacs has nothing to do with shell prompts. It's a GUI editor as well. Emacs philosophy goes against Unix. I e.g. never use the terminal mode. Otherwise, your points seem on-point.

GrumpySloth··on The Fatal Flaw of Ownership Semantics
I failed to find an example of a practical problem in the article. What I did find was a load of circling around the subject from a distant philosophical perspective, all boiling down to essentially "you know what, there exist problems that ownership semantics aren't suitable for" (but using lots of wisely-sounding language), forgetting to present the problems.

The article also suffers from the syndrome of taking a feature of a language and pretending that everything has to be seen through the lens of that feature. Well, newsflash: ownership semantics apply to only some things in Rust. They apply to values and bindings. But they don't apply to e.g. indices. I can take as create as many i's, where i=5, all referring to the same Vec, which later are going to be used to mutate the Vec, as I like, and Rust won't stop me. Similarly, there is Rc and Arc.

It also included one of those "wise" thoughts along the lines of "You know what? The true nature of computers/economy/life/love is..." - one of the templates of meaningless sentences. In this case it was "however virtually all computers are fundamentally mutable things". Hmm... Did we suddenly forget about pipelining, instruction ordering, caching, instruction-level parallelism, C/C++'s problems with memory aliasing? C is not a low-level language[1].

[1]: https://queue.acm.org/detail.cfm?id=3212479

If we were to write a statement analogous to the one advocated for in the article, but talking about mathematics instead, it could be "the fatal flaw of mathematics is that a lot of it is about reducing problems to linear algebra". How ridiculous does that sound?

← PreviousPage 7 of 7