Hacker Newsnew | past | comments | ask | show | jobs | submit | sam_bishop's commentslogin

I could read it once I forced my browser into light mode.


I've avoided #pragma once because of reports that it slows down gcc/g++: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=58770

But given that I haven't seen any mention of that issue in other comments, I wonder if it really is an issue.


I'm not a compiler expert either, but I think LLVM IR is the way it is for the benefit of the optimizer. It wasn't intended to be written by humans, typically.

It's more "portable" than assembly so that the same optimizer passes can work on multiple architectures. The static-single-assignment restriction makes it easier to write compiler passes by removing the complexity that comes with multiple assignments.


One of my beefs with Clean Code is its name.

There is no objective measure of code cleanliness. So if "clean code" is your goal, then you have no meaningful criteria to evaluate alternatives. (Including those pitched by Bob Martin.)

It gets worse, though. There's a subconscious element that causes even more trouble. It's obviously a good thing to write "clean code", right? (Who's going to argue otherwise?) And to do otherwise would be a moral failing.

The foundation on which "Uncle Bob" tries to build is rotten from the get-go. But it's a perfect recipe for dogmatism.


Honestly that kind of makes the word "clean" seem like a good fit to me. I can't say that measuring the cleanliness of my house is objective.


I'm an American who lived in the UK for a couple of years. They have many roundabouts there, and I love them. But now I live in an American town with a few, and I've realized that there's a reason that they are less safe in the US than in the UK--besides the fact that American drivers are less familiar with them. It's a solvable problem, but not one I've heard people talk about. (Though to be clear, I'm not a traffic engineer.)

Most American drivers are familiar with slip lanes, which allow drivers to make a right-hand turns without necessarily coming to a stop. (Wikipedia article: https://en.wikipedia.org/wiki/Slip_lane. Note that the diagram shows an example from a country where they drive on the left side of the road.) They're convenient for drivers, but dangerous for anyone on foot or a bike. This is because a driver in a slip lane is looking for an opening in traffic in the direction opposite of the direction they're traveling.

A roundabout is basically an intersection made up of nothing but slip lanes. So they're fundamentally dangerous to pedestrians in the same way that a slip lane is, but the fact that vehicles are moving slower means that they're still safer than typical American intersections.

However, if I remember correctly (it's been over 20 years ago), in the UK they don't mix roundabouts and crosswalks. They'd put crosswalks (called "Zebra crossings": https://en.wikipedia.org/wiki/Zebra_crossing) between intersections, where drivers aren't distracted by other things. I think we need to do that in the US as well if we're going to adopt roundabouts.


This kind of roundabout is awful for pedestrians and drivers: https://www.google.co.uk/maps/@54.5737159,-5.9326045,127m/da...

Surprise! I'm going to walk in front of you.


It's funny how the Run Coaching is placed in the middle of the roundabout!


In the Netherlands it is very common to have both crosswalks and bike priority lanes on the roundabout, especially in busy city environments. This actually improves safety, as the 'vulnerable' road users cross when drivers are driving slow and alert. Also, the access to the roundabout is slowed down by the crosswalks and bike lane, which improves safety for the drivers as well.


Note that it does require the design of the roundabout to be made with this in mind. Leaving any dutch roundabout involves a fairly tight 90 degree curve, forcing cars to slow down before encountering cyclists or pedestrians.


Roundabouts are also common in Australia, and crosswalks are placed some distance from them.

However, in my experience, pedestrians still cross at them all the time because an intersection (whether it's a roundabout, red lights, etc.) is always going to be the most convenient place to cross if you are trying to walk down a road for several blocks.

I have been living in a rural area of Australia now for over 10 years, and while I do think roundabouts are great, they should not be used where there are a lot of pedestrians. They get WAY overused in small rural towns here, leading to a high rate of rural pedestrian deaths.


> If you’re storing doubly linked lists in a DB you’re doing it wrong.

Assuming that the database uses B+ trees (like most do), then the database records themselves are very likely to be in a doubly linked list.

Not every doubly linked list is the kind you see in an introductory data structures class.


Yes, this. Because of this, I saw substantial data corruption that had built up over years in live medical databases: The on-disk B+trees had corrupted internal links due to incorrect handling of concurrent updates and rollbacks. This made some queries return incorrect results.

The developers had been writing messy heuristic workarounds for database low-level corruption in application code for over a decade, instead of figuring out the cause and fixing that.

For example, application code had special routines to attempt to detect and ignore duplicate records in some queries (but not others), in response to customer bug reports, but of course that didn't fix missing records or make the data correct. It just patched over an observed symptom. It also didn't fix all the places it could happen, so much of the application still had occasional flaky behaviour, but not consistently enough to be reported in detail and get the (bad) workarounds to hide it.

Correctly implemented transactions are very helpful for database internals and on-disk structures too, not just for the API level presented to applications.


I don't use the proscribed TDD process very often, but I use the TDD mindset all the time to great effect.

Listing out the tests you're going to write before you write the code (even mentally) can be considered a continuation of the requirements-gathering process. And thinking about how you're going to test your code before you write it will, in my experience, improve the design. (Particularly, it seems to encourage the single-responsibility principle, as code that's doing too much or combining layers of abstraction is really hard to test.)


>Listing out the tests you're going to write before you write the code (even mentally) can be considered a continuation of the requirements-gathering process.

But this is not TDD.

I've worked in HW industry where cost of bugs is high and we analyzed specs during brainstorming session as 3-5 ppl and brainstormed test cases that we want to test.

It worked well because this way we we're finding way more things to test than when doing it alone.

But still, this is not TDD. We weren't doing TDD.

>(Particularly, it seems to encourage the single-responsibility principle, as code that's doing too much or combining layers of abstraction is really hard to test.)

Whether your code is easily testable will be challenged by writing tests regardless of the moment of writing test - before or after writing impl.

So no benefit from TDD over non-TDD approach.


> But this is not TDD.

That's true. Did you miss the part where I mentioned the "TDD process" versus the "TDD mindset"?

> Whether your code is easily testable will be challenged by writing tests regardless of the moment of writing test - before or after writing impl.

I don't want to derail the conversation, but when I'm working on projects alone then my test coverage is 100%. (Line and branch coverage.) But surely that slows me down, right? And I must have a million test cases? No, quite the opposite actually. Writing code that way is just a skill though, the same as juggling, that becomes easy with enough practice. (Along with the right practices and tools.) However, I don't aim for 100% (or even a particularly high percentage, to be honest) when I'm working on projects with other people, because the only way it's possible is if the code was designed for it from the get-go.

I encourage junior developers to learn and try to use TDD for a while because it can be useful sometimes and improve the way they write code. But I would never require them to do it.


I've heard that the Hotspot JVM uses a bump allocator but don't know the details. I'm sure it's heavily optimized though, so I'm curious about how this compares.


For most specific purposes, a specialized bump allocator can be significantly more optimized.

Hard-coding the alignment simplifies many questions (especially if you can guarantee the allocation size being a multiple of the alignment, at which point it becomes a complete non-issue).

And if you have an upper bound on the requested allocation size, the integer overflow checks can be dropped too (e.g. in a Java "new int[n]", "n" is an int, which has a max value of 2^31-1, which can safely be added to a bump pointer, provided it's further than that away from the address space end (which it's gonna be due to the kernel reserving the upper half of the address space)).

And for constant-size objects, your entire bump allocator can become just

    if (ptr > precomputedEndMinusObjectSize) fallback();
    result = ptr;
    ptr += size;


I believe HotSpot can move allocations out of the arena when garbage collection happens, which means fragmentation is less of an issue.


Here's an article[0] with some information on Hotspot's bump allocators.

[0] https://shipilev.net/jvm/anatomy-quarks/4-tlab-allocation/


Pooling objects (for the purposes of minimizing GC) is consider a bad practice in modern Java. The article suggests that compacting, generational collectors are a bad thing, but they can dramatically speed up the amount of time it takes to deallocate memory if most of your objects in a given region of memory are now dead. All you have to do is remove objects that are still alive, and you're done: that region is now available for use again. The result is that long-lived objects have a greater overhead.


Does object pooling still make sense for direct ByteBuffers nowadays?


Yes. Those aren't GC controlled so any arguments about GC is irrelevant with direct byte buffers.

Also, object pooling isn't really a GC related hack, it's more useful as a cache booster. Programmers like immutability and garbage collection but your CPU doesn't like these things at all. If you're constantly allocating new objects it doesn't matter if your GC kicks ass, because those objects will always be in parts of memory that are cold in the cache. If you allocate some up front and mutate them, they're more likely to be warm.

Obviously this isn't a language or even VM thing. It's a "mutable variables are good for performance" thing.


I agree. The author seems to know quite a bit about Go and GCs, but doesn't seem to have much experience with Java. As a Java performance engineer, it sounds like he is comparing Go to how he thinks Java works based on what he's read about it.


Additionally he doesn't seem to know that much about C#, which also has advanced GC, while allowing for C++ like memory management, if needed.


It's odd how most people that haven't used a VM with GC are amazed by Go (no VM) and WASM (no GC) but still fail to understand that with a GC _AND_ VM you can code something that doesn't crash even if you make a big mistake!

And they haven't even bothered to try it out!

To use anything other than JavaSE/C# on the server you really need very good arguments!


Or being amazed by Go's compile speed, when Turbo Pascal and Object Pascal compilers were already doing that in the 1980's, or finding WASM innovative when polyglot bytecodes with no GC also go back to the early 1980's, like Amsterdam Compiler Kit EM as one example among many.


The innovation in WASM is more about getting all the major players in the browser space to agree and support it as a first-class citizen in the web stack. That polyglot bytecodes existed in the early 1980's does nothing for the web.


What a perfect example, have you even tried Java?

I'm guessing you are coding for the client?

C++ arrogance is the problem here.

About the pipe dream of WASM there are 3 problems:

1) Compile times (both WASM and the browser)

2) If you thought Applets where insecure (btw they wheren't) wait until the .js (also a VM with GC...) bindings that you are forced to go through to reach anything from WASM securely gets attention!

3) If you build for the browser you have Intel, Nvidia, Microsoft and Google (do you work there, might explain things) to deal with on Windows. You DON'T want that... use C to build for linux on ARM/RISC-V has to be one of your options and then all that work you spent on getting WASM to work is wasted. (because you won't have the performace/electricity/money for the cycles you need in the long term)

Edit: Please don't replace Python (that you should probably never have touched) with Go...


1) No one was realistically compiling C++ or Python to Java though. WASM is not new tech as the other poster said — it’s people coming together to support one compile target that also works on the web, which itself is the crowning achievement.

2) Building a secure VM is easy. You only need to give it access to things it should have access to. If a VM has only math instructions, it’s not going to access the file system. My computer can’t poke my nose because there is literally no machine instruction for it.

Java did not build its VM that way. Instead, the JVM had full access to everything and individual functions were blacklisted within Java applications and this was enforced by Java code running in the same machine as the attacker’s code. Naturally every blacklist works like a sieve.


When I think about it, the memory security is probably the weakest point of WASM and probably also the reason nothing of value has come out of that initiative yet.

How does WASM protect memory?

Or maybe there is something valuable made in WASM and I don't know about it, Figma is I think, but I'm not in the target audience.


Not to mention Turbo Pascal was compiling faster than Go, on a single-core, in-order 20Mhz PC...


With more advanced language features too.


Yep, Turbo Pascal 7.0 vs Go 1.0.


I just wrote some Go last weekend and the compile time was very slow. It reminded me of Scala. Any way I switched to Ruby and didn't have to deal with it any more. Turbo Pascal really was fast, but I don't see that in Go.


Scala and SBT are some of my biggest productivity killers. They murder my machine and take years to do compile.


> To use anything other than JavaSE/C# on the server you really need very good arguments!

Haha! Quoting this so you can't delete it.


Most programmers use PHP, Python, Ruby, Node.js which are all GC bytecode VMs.

What language are you imagining as an argument here?


C# makes it possible to do C (not C++) like memory management but it does not make it easy. Unsafe C# code is really, really, really unsafe, much more unsafe than equivalent C code, and does not compose well. It's improving, with Span/Memory, etc, but it remains an absolute last resort.


C and C++ memory managements are alike, C# doesn't need reference counted library types.

If you mean RAII, they can be easily done with IDisposable, using and Rosylin analysers that throw errors when usings are forgotten.

And yes, manually memory management should be last resource, proven by profiler data.


I do mean RAII, which is fundamental to C++. Memory management without it is not "C++ like".

IDisposable is not a substitute for RAII, and C# has nothing that can manage ownership in equivalent ways as it lacks deterministic destruction. Implementing, for example, a data structure based on unmanaged memory in C# in such a way that it can be safely used by code outside the library without undermining the safety of the language (i.e. without introducing the possibility of programming errors outside the data structure implementation causing leaks or memory corruption) is an exercise in discipline and requires a thorough understanding of the runtime - eg. knowing that an object can be garbage collected while a method on it is executing. I know this because I've done it (as a last resort after extensive profiling and production use of various optimised, managed versions of the library).


Maybe you should explore better Marshall Interop services and the span related improvements.

IDisposable is definitely an alternative when coupled with Roslyn analysis or SonarQube.

As if doing it in C++ doesn't require the same knowledge level, worse actually, as it also requires ISO C++ mastery and a good knowledge of how each compiler being used handle IB and UB.

Also unless static analysis is getting used, RAII can also get thr ownership wrong, specially with use after move errors, or smart pointers incorrectly given as arguments.


I used Spans extensively. They help (mostly by providing efficient bounds checking), but not much. The main problem, as I mentioned initially, is the composability of this sort of code.

I wasn't trying to give some kind of defence of C++ here, just pointing out the dissimilarities to C#. This particular piece of code would have been far easier in C++ and RAII would have been a huge help, but the system as a whole would have been an utter nightmare in C++ (which I know because I've worked on multiple similar systems in C++).


Sure, but I am the opinion it does compose, if one embraces it isn't the same as writing C++ in C#, not trying to downplay your experience on the matter.

For example, here are the Roslyn analysers for a more RAII like experience with IDisposable,

https://github.com/DotNetAnalyzers/IDisposableAnalyzers

Turn "DISP004-Don't ignore created IDisposable." into a compiler error and you have your region allocated RAII like experience.

And moving a bit the goal posts, for those scenarios where C# fails completly to provide a usefull solution, we can rewrite just that module into C++ (preferably C++/CLI when only Windows matters), and have the best of both languages.


There's also ref which is almost like pointers and safe. Span/Memory do not remain an absolute last resort, they are becoming the standard way for string formatting/parsing/(de)serialization and IO.


I didn't say Span/Memory are a last resort, I said C style memory management (AllocHGlobal/Free), despite being somewhat improved by Span/Memory, remains an absolute last resort. Span/Memory aren't primarily aimed at handling unmanaged memory, though they're useful for it.


Of course C style memory management is a last resort, 99% of devs don't need it, and the 1% that actually need it, better learn to use V-Tune.


why is it more unsafe than equivalent C code? I always thought it's simply a way to enable C-like real pointers/native memory ...


Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: