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

>Our neighboring town has a beautiful system in place. It tracks your average speed over +- 100m. If you speeded, you get a fine and even better: the light always turns red and you have to wait for a minute. If you run that red light, you get another (heftier) fine. If you drive normally, the light is always green.

I have never seen a system like that. What town? Or what system?


It's a basic trajectory control system (using ANPR cameras), combined with a red light camera. They read your license plate twice in different locations to determine how long you took to drive a section of road. They're very common here in Belgium, but it being coupled to a red light is not that common. They even have a sign that says "too fast = red light + fine".


The reason for avoiding OS threads is not only the cost of creating them, but also the overhead of scheduling them. The kernel uses preemptive multitasking, so it does work to determine when it should switch control between threads, and which threads should be running, and there is a cost of performing that context switch. The OS also can tell from syscalls when a thread is waiting on I/O.

Java virtual threads are more similar to async/await since they are a form of cooperative multitasking. That is, virtual threads must explicitly tell the runtime when they are ready to yield execution to other tasks. In practice, the programmer does not need to do this manually; the low-level 'blocking' operations in the JDK have been modified to signal this state. This is why you do not need to use an explicit syntax like async/await. Under the bonnet, virtual threads are mounted and unmounted to/from platform threads (JVM wrappers for OS threads) in a pool. All of this involves overhead of copying virtual thread contexts to and from the heap, and of managing the scheduling of virtual threads.

Why don't other languages use this implicit approach? I am sure there a various reasons, but I cannot really speak to them in specifics. I would be interested to know as well. I would guess that the overhead of the implementation is one major reason.


Yeah, it makes a lot of sense in the JVM or Golang runtime where you're already paying for indirection. My first guess for why JS didn't do greenthreading is simplicity of implementation.

I know that threads are expensive to schedule in the OS, but admittedly I don't remember exactly why. There's context-switching and such, but greenthreads also have to do their own analog of that. I know there are answers online of why OS threads can't be cheap like Golang greenthreads, just gotta dig through them.

Also turns out someone asked a similar question on Reddit, but I don't see an answer that nails it: https://www.reddit.com/r/ProgrammingLanguages/comments/prr8j...


According to this 2020 paper, "resuspended" particulate matter may be a major, previously unaccounted-for contributor to pollution from roads.

https://www.mdpi.com/1660-4601/17/8/2851

>Traffic-related resuspended dust is particulate matter, previously deposited on the surface of roadways that becomes resuspended into the air by the movement of traffic.

>Results show that the inclusion of resuspended dust in the emission and dispersion modeling chain increases prediction of near-road PM2.5 concentrations by up to 74%.

The weight, speed, and volume of traffic, are therefore considerable factors even beyond the pollutants the vehicles leave behind.


Lead does not readily degrade, either. It is still in the soil, the dust, our building, our foods, et cetera. People seem to be generally less exposed in each successive generation, however.

https://www.efsa.europa.eu/en/efsajournal/pub/1570


If you are open to using zsh, take a look at https://github.com/larkery/zsh-histdb . It stores your zsh history in a sqlite3 db. The benefit of this is that your history is well-structured and indexed, and that you can use SQL to query it. The working directory, exit status, and more are included, so you can easily implement your own smart history tools.


This is similar to how java.util.concurrent.atomic.LongAdder works:

https://github.com/openjdk/jdk/blob/master/src/java.base/sha...


Huh, that's ... still quite inefficient. Since a multiple variables in a cache line is fine if all will be accessed from the same cpu, you ideally want a separate allocator for that, then you can avoid all the spooky dynamic growth. And most CPUs offer a direct "atomic add" instruction which is much faster than a CAS loop. For pure stat counters you generally want `relaxed` memory ordering on that; for other cases acquire and/or release may be desired (this is tricky to get performance-optimal given that some platforms upgrade to a stronger memory ordering for free so it's best to require it in your algorithm, whereas others are best with an explicit fence in the edge case). I've never found a real-world use for `seq_cst`.

It's unfortunate that per-cpu variable are difficult in userland, but there are at least 2 ways to fully emulate them - rseq and pinning - and you can also just revert to full-blown thread-locals (which have much better tooling support) if you aren't constructing a gratuitously large number of threads, or shared thread-locals if you do have a lot of threads. If you make the wrong choice here, correctness never suffers, only performance.


Uncontended CAS without carried dependendies on the result (almost always the case in this use case) are similar in performace to atomic add on most platforms.

The CAS is the price they pay for contention detection, though it would be interesting to consider solutions which usually use unconditional atomics with only the occasional CAS in order to check contention, or which relied on some other contention detection approach (e.g., doing a second read to detect when the value incremented by more than your own increment).

The solution looks reasonable to me given the constraints.


Part of my point was that "check for contention" is often a silly thing to do in the first place, when you can just avoid it entirely (and with simpler code too).

Admittedly, Java is fundamentally incapable of half of the solutions, but making a simple bump allocator (called once per statistic at startup) over per-thread arrays is still possible.


Yeah exactly, and this is a commonly used trick in concurrent data structures in general. The Java implemenation has the additional twist that they don't use a fixed number of "slots" but rather start at 1 and use CAS failures as a mechanism to detect contention and then grow the number of slots until there are no longer CAS failures.


You were certainly bitten by the Java ecosystem, but I would agree that you hold some responsibility. Adopting Spring Boot so casually is really quite questionable. It's essentially opting to build your application within a giant, bonkers codebase. In the golang community there is a held value of using the standard library and tools, which is helped those being very good. I would suggest that Java is a better experience if you follow that same approach. If you want to serve HTTP, use the jdk.httpserver module. If you want to make HTTP requests, use java.net.http. If you want to interact with the filesystem, use java.nio.file. To build, use the included tools in the JDK. When it comes to cases where a third party dependency is warranted, such as use cases for Tika, you will have to deal with some questionable design decisions, but it's much easier when you have just a few libraries that you use by hand, and no insane framework trying to stitch everything together for you. For getting those dependencies, you are going to be acquiring them from public maven repositories, but I would suggest using Coursier or Maven Resolver instead of actually using Maven.


> Adopting Spring Boot so casually is really quite questionable.

The thing is, you don't always get that choice, at least in a way that wouldn't generate much opposition. If Spring Boot is what people use in most of the projects, opting for something else would generate lots of questions about that "inconsistency", especially as a part of the same team/project where services already exist that are written with it.

Same as not opting for ASP.NET with .NET, Laravel with PHP, Django with Python, Ruby on Rails, Express.js with Node.js and so on. Same as for picking .NET in a "Java shop" or Java in a ".NET shop" or even wanting to use Vue when most other projects are made with React etc. I'm not saying that Spring Boot is always the right choice (god no, honestly everything from Dropwizard to the likes of Javalin and Quarkus etc. have their use cases), just that you'd have to be able to make that choice yourself in the first place, which isn't always the case.

Same for Tika and other dependencies, which are often necessary evils: if you need to handle files and get their MIME types, will you just estimate what a file could be by looking at its extension, or do you actually want to look at the contents? And if you look at the contents, will you attempt the lazier approach ("Huh, this is an MS Office container type, no idea what's inside of it") or will you actually look for something more accurate ("Oh hey, this is an MS Office container type, with a Word document inside of it")? Then it becomes, do you want to spend the time reinventing that yourself or just have it done by the end of the day with an external dependency?

Not that Go is immune from that, you'd still probably want to just use https://github.com/gabriel-vasile/mimetype instead of building your own.

Aside from that, it's nice that Go isn't so "enterprise" oriented (yet?) and that the standard library is so strong (whereas Java only got a built in server around JDK 21 IIRC).

Edit: oh, also on my silly little list of jagged edges of Java, there's also the whole JUL, Log4j, Apache Commons Logging, Logback and SLF4J situation, whereas Go just has https://pkg.go.dev/log and it's nice. Not a horrible indictment or anything, I still use Java more or less daily, alongside other languages.


I agree with your sentiments. It's not always possible or worth it to go again the dominant culture of Java and most Java shops is against it. I have lost more of these kinds of battles than I have won.

I see Boot as a major risk for long term maintainability unless you are willing to keep Boot experts on staff. This is relevant: https://news.ycombinator.com/item?id=33185010

I wouldn't say the same for Tika, which I have used many times. It has a relatively small and readable codebase, excluding the parsers it depends on, and does not impose much upon its host code. While complex, it is significantly more feasible to fork or adopt it, if necessary.

To your point, the typical golang library, from what I have seen, is closer to being something manageable.

The Java community is too accepting of hacks (or hack-like things) such as bytecode manipulation, annotation processing, classpath scanning, classloader tricks, reflection, excessive reliance on not-quite-code configuration, etc. There are legitimate use cases for these techniques, but they should be used sparingly, and with great care.

The logging situation is emblematic of Java's tendency to on third-party solutions even for basic, primary concerns. The development and build tooling is another major example. Whereas golang provides tools that are standard, powerful and beloved, the JDK only provides low-level tools that are fairly half-baked and require significant orchestration for typical tasks. Same situation with JUnit instead of having a sensible, built-in approach like go test. The list goes on...


The error handling is explicit only in a limited way. As the author points out, you have to explicitly propagate an error condition, but you don't have to explicitly not-propagate it. Exceptions are reversed in this way: propagation is implicit, and not-propagating is explicit. I would prefer that handling is fully explicit in all cases, and not depend on supplemental linting or sheer discipline. It's a bit incongruous that golang compiler is so fussy about unused imports, but has nothing to say about silently ignored errors.

I also find that stack traces are generally more useful than plain wrapped errors. I want to know the code path in most cases where I am looking at an error.

More generally, it's disappointing that golang has so many idiosyncrasies given its philosophy of straightforwardness. I struggle to think of any useful, general purpose language that has fared better, though.


Gradle and Spring Boot are not a part of the JDK, so it seems unfair to discount Java due to their poor designs.


>Stored Procs make everything about your standard DevOps and SDLC process harder - branching, blue green deployments and rolling back deployments.

There is a naming/namespacing strategy incorporating a immutable version identifier that makes this easier, which I have described here:

https://news.ycombinator.com/item?id=35648974

Note that this requires a strategy for cleaning up old procedures.

It also is possible to individually hash each procedure, which is more sophisticated, and would allow for incremental creation of new procedures.


That’s actually an ingenious solution. I can’t find any flaws in it.


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

Search: