Earlier quoted context omitted.
With linked lists, memory latency dominates, since you're chasing pointers all over the heap. I guess the null check costs nothing most of the time. On the other hand, removing random objects from lists that you don't know are actually in those lists is a smell.
> I guess > most of the time Two terms that would not fly in kernel level programming; with kernel programming, low-level libraries, you want both optimized speed and predictability.
Linus Torvalds' good taste argument for linked lists, explained
301–310 of 339 posts
Re: Linus Torvalds' good taste argument for linked lists, explained
#302Although I liked the 'elegant' code after reading it, in general I would try to shy away from 'elegant' solutions that are actually harder to understand. Unless you are working with high-performance programs, most of the time you don't care about one or two extra branches. It's usually more beneficial to write code that can be easily understood by a future-you or by another person in your team: less time invested in…
However, given that we're talking about Linus Torvalds' opinions on what makes code 'elegant' or 'good taste', I'd say it's implicit that we're talking within the domain of low level, high performance code, such as kernel code.
Re: Linus Torvalds' good taste argument for linked lists, explained
#303Earlier quoted context omitted.
Yes - and that is the point the OP you responded to is making. This is not a generic library function. It used in a specific setting where preconditions exist and presumably are checked _prior_ to calling this code. Sure you could make the argument that we don't _know_ they are being checked, but it's a pointless discussion. Who cares? _If_ preconditions are met, this code is safe, if they aren't, it's not safe. Sinc…
Imo every piece of code (for reasonable definitions of "piece") is supposed to check its own preconditions and not rely on the caller to check them.
Re: Linus Torvalds' good taste argument for linked lists, explained
#304Earlier quoted context omitted.
Safety is a feature and that null check prevents bugs. I'd rather have slow but safe code rather than fast but buggy code.
Adding checks creates more code branches and tests. Passing null in is the bug.
"Premature optimization.... " you get the drift.
Re: Linus Torvalds' good taste argument for linked lists, explained
#305Earlier quoted context omitted.
I'm arguing that we should use asserts or similar defensive coding mechanisms to ensure that preconditions are met instead of merrily continuing to run our program with cascading error effects.
Asserts have run-time cost. What you need is to ensure ensure those preconditions are met in the first place, statically, by formal methods for example.
Re: Linus Torvalds' good taste argument for linked lists, explained
#306Earlier quoted context omitted.
Um, yes. That jumped out at me. If there's a no-find, the code will de-reference null and crash. That's just not acceptable. Try to write that in Rust, using Some(ref) for the forward link, and the compiler will force you to test for None and detect the end of the list. This is pre-1990s programming style. I've seen such code in assembly programs. Because I was reading crash dumps where it failed.
It's 100% acceptable. You should not remove something from a list unless you know it is there. Since you won't attempt to remove something that doesn't exist, any code to check for that situation is an unjustified performance loss. (separate code exists for searching a list) This... is not Python. C programmers, particularly kernel developers, have that style even in 2020.
This is a classic idea for doubly linked lists. The empty list has the head element linked to itself in both directions. Buffer rings are sometimes organized that way. The cases for doubly linked lists are messier.
Bear in mind that in modern CPUs, branches to nearby code are almost free, but indirection to far memory is expensive.
Re: Linus Torvalds' good taste argument for linked lists, explained
#307Back in the 80's, I learned Pascal, and learned about its dynamically allocated records, then I went on to learn C, and got used to its pointers and arrays. Then I went back to Pascal, and designed a program in my head with some dynamically allocated linked list data structures, and another data structure that had a member that pointed to the head of the linked list. Then I started typing in the Pascal code, and hit…
Nowadays there is the @ operator
Re: Linus Torvalds' good taste argument for linked lists, explained
#308 item** find_parent(item** list, item* item) {
item** potential_parent = list;
while(*potential_parent != item) {
potential_parent = &(*potential_parent)->next;
}
return potential_parent;
}
void remove_item(item** list, item* item) {
item** parent = find_parent(list, item);
*parent = item->next;
}Re: Linus Torvalds' good taste argument for linked lists, explained
#309Earlier quoted context omitted.
And instead of adding a check and crashing visibly here you wait even longer until your program misbehaves due to the error? The longer the program runs after something went wrong, the harder it is to debug, and the more likely it becomes that you get an exploitable bug.
Performance is a feature and that null check has a cost. The code is correct. The null check is unnecessary. The bug is passing null in.
Make it right
Make it fast
This is an ordered list, and the people who forget that make a lot of work for the people who don’t
Re: Linus Torvalds' good taste argument for linked lists, explained
#310Earlier quoted context omitted.
> As a side note: I don't like the "elegant" version's use of double indirection. It's not necessary if you return a pointer instead of void, which is also nice because you can treat calls to your list functions as lists themselves and chain them together. Agreed. > It also allows you to avoid warts like `p = &( p)->next;` Does it? Isn't having `p = &( p)->next;` in the while loop necessary? > at the modest cost of n…
The & isn't needed if you're returning a pointer and no longer need a pointer to pointer to return the list as an argument. Also, if you get rid of the struct containing a single pointer and just use pointers, you can have the following instead… ` p=p->next As for the API, returning the pointer allows a function to be an argument for another function, which I find more flexible. ` p= remove(p,find(p,value))// I also…