Posted by signa11 19 hours ago
https://www.gnu.org/software/emacs/manual/html_node/emacs/Ba...
You can just record and play back your exact keystrokes like a _Super Smash Melee_ game plays back your inputs to replay matches. If you can figure out how to do any mildly complex action once you can literally replay it a thousand times. Just like AutoHotKey! To my that's the essence of citizen programming!
Here's one that I think more people should know: avoid branches. If I can do the same thing without an if statement and even a logical expression, the code typically both becomes easier to understand for people and easier to run for the CPU.
I have always felt like my bar for publishing something (even just to internal wikis/channels) is too high due to being overly self-conscious. I think we should try not to validate that feeling by implying that there is a non-negligible number of readers who will think you have a personality flaw because you wrote down your personal collection of tips in a public place, or that those people deserve consideration in the first place.
There is no such thing as "the things everybody knows". There are just too many things. Even a list of basic tips is probably going to contain one thing I didn't know or perhaps forgot. Write-ups like this are where most of my practical knowledge comes from, not RTFM (which I do).
It's not applicable to every situation, but one way to do this is some very basic fuzzy logic. You do a little math and then either choose a single branch at the end, or sometimes avoid a branch altogether. https://www.geeksforgeeks.org/artificial-intelligence/fuzzy-...
Another way to avoid some branches is to have specialized routines, maybe with multiple dispatch, rather than more general methods with a bunch of checks within them for slightly different situations.
A classic performance hack for critical sections is loop unrolling.
=====
Should you go branchless?
Most of the time, no. Branchless code is harder to read and easier to get wrong. Besides, compilers know a lot of tricks and already do a lot of this work for us.
Only when a profiler points at a hot loop, and the loop contains a branch on unpredictable data this technique can pay off big.
if (x == 0) {
return y;
}
y += 25*x;
return y;
and skipping the if just makes the function shorter and simpler, while also not involving the CPU branch prediction. Another one that doesn't necessarily skip all branching but at least drops one - and more importantly makes the code simpler and easy to verify, is removing the if statement in code like if (count == 0) {
return;
}
for (int i = 0; i != count; i++) {
puts("hello");
}In the first case, the compiler removes the first if/return
In the second case, if you don't have the first if/return the compiler will add it. That's because it will actually convert your loop into a do/while, with the test in the end, because it is more efficient. But it has to handle the count == 0 special case first, so it will do that early return even if it is not explicitly there.
That's the kind of optimization modern compilers are good at.
if (thingThatIsTrue):
// a bunch of logic here...
else: // different logic here...
they mean:if (thingThatIsTrue):
return doThisWhenTrue()
return dothisWhenFalse()Just a simple example. I'm not sure if this is what you consider "obfuscating" the branches. Logically the same, but a bit more linear to understand?
Edit: I am bad at formatting comments here.
Example:
No space before start of line.
One space before start of line.
Two spaces before start of line.
Thus, you can put multiple lines of code with indentation as well as long as you put two spaces at the start of the line:int main() { return 0; }
int main() {
return 0;
}
See https://news.ycombinator.com/formatdocLike imagine you have a few different classes of things A,B,C so instead of checking if the thing you're handling is an A,B,C you have like a shared interface across all and can call Thing.do_it or whatever.
Still branching conditionally but it's passing it off to language features instead of code you have to write.
if Val === "A" then Do funcA() else if Val === "B" then
and so forth for lots of values, or using a switch statement or similar branching instead of
Object functions = { "A": funcA() {does what funcA does}, "B": funcB() {does what funcB does} etc. etc.
}
runnableFunction = functions[val]; runnableFunction();
Actually writing it I remember now someone who did this, a junior who had to update a validation function for XML invoices based on their root namespaces, which there could be a large number of these, and so she wrote out
switch namespace == "somenamespace" { validatingscheme = "someschema"; doPreliminaryFunctionToDetermineifshouldvalidate(); }
I can't remember all the details as this was almost 20 years ago, however while it was true that one branched on the schema, it made much more sense to look up what one was supposed to do based on the rule for branching and then just execute that one action rather than writing a bunch of branching logic.
So to make it more concrete: Once branching rules becomes sufficiently complex prefer query for what you should do rather than branching
on edit: note again, not real code, but should be understandable and translatable into real code to understand what is being said easily enough.
on 2nd edit: this is also just basically one of the things I prefer instead of getting a lot of branching logic. I have never seen any stats on any benefit to this model than just having a bunch of branching statements, but I feel that the benefit is there nonetheless.
A common pattern in an old C++ job I had: People writing for loops, coupled with if conditionals, for things that could just be done by chaining functions in the algorithm library.
Don't do a for loop, check for a condition, and break. Use find_if.
A trivial example is actually written with a branch in C/C++, but relies on compiler optimizations to kick in. If you compile a ternary operator in C/C++ (and probably rust, C# and other languages) such as in:
int min_branchless(int a, int b) {
return a < b ? a : b; // Often emits cmov with -O2
}
With gcc/clang a -O2, one would expect the compiler to emit the following assembly: cmp edi, esi
cmovle eax, edi ; select a if a <= b
ret
There's numerical tricks for other operations/comparisons, and compilers know a lot of them. But, I just suggest compiling your code and configuring your compiler to emit the generated assembly with references to the code it was generated from (you should be able to get it to emit source line references in the assembly). You'll likely be surprised at the optimizations applied at -02, and utterly confused by what you find at -03.edit: Also, it doesn't mean to never branch, but to minimize branching, especially in tight loops. Branch outside loops, not inside, for instance.
e.g. don't do:
for (...) {
if (condition independent of loop variable) {
...
} else {
...
}
}
do: if (condition independent of loop variable) {
for (...) {
...
}
} else {
for (...) {
...
}
} vector conditionmask = <some computation...>; // E.g., 11111111 00000000 00000000 11111111
vector truebranch = <some computation...>;
vector falsebranch = <some computation...>;
vector result = (truebranch & conditionmask) | (falsebranch & ~conditionmask);
where each lane of the conditionmask has either all bits set or all bits clear, depending on the outcome of the conditional test for that lane.The processor obviously does execute both branches here, so there's going to be wasted work. But since it's just a linear sequence of operations it can often schedule them independently and run them out-of-order and in parallel. And of course, if there's any shared computation between the two branches, the compiler can do common subexpression elimination.
That said, that sort of approach where you go ahead and do both and then blend them was definitely the kind of optimization where you'd want to profile rather than doing it blindly. But it was a pretty common thing to do when hand-vectorizing code. (Thankfully, auto-vectorizers are pretty good at doing this sort of optimization for you these days. It's been a very long time now since I've had to hand-write vector intrinsics.)
If you want to tell the Rust compiler that you're certain a branch predictor can't help here [be very sure, most often humans are wrong which is why historically these "I know better than the branch predictor" features get ignored by optimisers] you can core::hint::select_unpredictable(condition, a, b) rather than using a dedicated operator.
† That's not its actual name, some languages have an operator with three operands which does something else, such as fused multiply-add so in a multi-lingual context better to say explicitly you mean the ternary conditional operator.
total = calculateOrderTotal(user.order);
if (user.isPremiumMember) {
total = total * 0.9; // 10% discount
versus total = calculateOrderTotal(user.order);
discount = calculateDiscount(user); // Returns 0.9 or 1.0
total = total * discount; total = calculateOrderTotal(user.order);
total = total * user.discount; total = calculateOrderTotal(user.order);
total *= user.discount;
or: return
calculateOrderTotal(user.order)
* user.discount; return 1 - user.isPremiumMember * 0.1;
would also cut it. v = setup()
if v == 1:
side_effect_1()
elif v > 1:
side_effect_1()
side_effect_2(v)
else:
raise Exception()
then we can "refactor" v = setup()
if v < 1:
raise Exception()
side_effect_1()
if v > 1:
side_effect_2(v)
i know that this might seem "dumb" that the code was ever setup the first way but code can grow into that shape pretty easily. this refactor "removes" the v==1 branch. this new code also follows the "early return" pattern, which improves readability.Or stupid, like all those vloggers posting "ZOMG! Go all in with these secret hidden weird trick iPhone life hacks to level up!" that are just regurgitating what's in the manual.
As we used to say, RTFM: https://support.apple.com/en-us/docs/iphone
My "find" is almost always
tree -if | grep "<whatever-pattern>$" | head -1
head, of course, being optionalThe tradeoff is needing to learn regex, which is not any easier to learn than find arguments.
similar to ripgrep, though, fd is a modern replacement that’s a bit friendlier to use.
And I don't think that's a good thing. Especially because AI coding agents use it a lot. They only need to hallucinate a little to destroy your filesystem.
It's essentially like the SQL SELECT expression.
find -name '*.md' -and -not -type d
Of course, then it becomes more obvious why there are parens, why those parens must be escaped for the shell, etc. E.g., find '(' -name '*.md' -and -not -type d ')' -or '(' [...] ')'
I think once someone groks the nature of the expression args, then find becomes easier to start working with.The most messed up part in my mind though is that while most things in find are clearly helping the expression towards its goal of "true" or "false" on whether to include the file or not in the results, some, like -prune or -exec do so but with side-effects. And since they're usually invoked primarily for their side-effect, it isn't immediately obvious that they even return a value, or are participating in the expression itself. (-prune is true, and -exec depends.) And this is where it becomes important that `find`'s -and & -or are short-circuiting, too. (In a purely logical expression, it wouldn't really matter except as an optimization.)
People also sometimes omit -and, which I'm not a huge fan of the legibility of. (But using -and makes it not POSIX; you can do -a but ew. which brings me to the last bit…)
macOS: the find there requires the starting-point arg; so my examples above, on macOS, would all need to be,
find . [expression args...]
… which is lame. Brew install GNU find and be done with that.While I agree with globbing (or fd) for interactive use, I think for scripting I'd still say "you might want find"; there are limits to arg list lengths (see xargs) that (esp. recursive) globs can exceed; unless you know you'll never glob the world, find might be appropriate. Also:
echo *nope*
Globs will betray you on the zero-match case. (`man bash` & cf. `nullglob`.)bash: Ctrl-L to clear the screen
VSCodium: Shift+Alt+[arrows|mouse click] to select a rectangular block
scp for moving files (instead of ftp)
Ctrl-l to clear the screen, Ctrl-p to select previous command, Ctrl-j to enter it again. I don't even use Enter key when in terminal anymore.
When you want to pause entering a command for a moment do Ctrl-a to go to the start of the line, and type # to comment out. Then Ctrl-j. You can return to that later with Ctrl-p, then Ctrl-a again and Ctrl-d to remove that comment.
To edit complex commands you might want to use Alt-e to enter $EDITOR.
Both rsync -avz /path/to/file user@server:/another/path/to/ or rsync -avz /path/to/file/ user@server:/another/path/to/file
Will create directory named /another/path/to/file which will contain all contents of the original directory (/path/to/file/*).
Basically, if you include the trailing slash in the source path, only contents of the directory will be copied (useful when you need to rename it).
rsync -avz path/to/old_dir/ user@server:/path/to/new_dir
If you omit the trailing slash in the source path, rsync will create the target directory for you. See man rsync for more info.Record a debugging terminal session including output to a file. Its pretty great.
You can accomplish the same things in vi editing mode (with ? and !!) but they’re not as interactive.
Is it true that you can pass a nodejs agent to fetch ? I don't think so