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

Related: "Do the Real Thing" by Scott Young (2020): https://www.scotthyoung.com/blog/2020/05/04/do-the-real-thin...


Entirely unrelated to the topic, but my mind was blown when I recently learnt that clamp() can be implemented via .median(val, low, high), and the param order doesn't matter!


Perion | Advertising Technology | https://perion.com/ | Hybrid (Onsite+WFH)

Hiring software engineers across different locations and teams:

-

Tel Aviv:

Back End (Go) developer: High-performance edge ad auction systems

Data Scientist: Multiple ML and AI initiatives (internal systems as well as client-facing)

-

Montreal:

Data Engineer: Advertising (big data, for real!) data processing systems

Back End (Python) developer: Working on a (tastefully-designed) set of python transactional microservices serving the core business web application and APIs

-

If you see yourself as a good fit for the roles in the above locations, please browse the job descriptions on our web site ( https://perion.com/careers/ ) and reach out to me directly - my contact info is in my HN profile bio.


Agreed with you. I've always told people all code "rusts" (not a language reference) - in multiple ways: the original author's mental model, the contributors, institutional knowledge, and the supporting ecosystem and dependencies. All code atrophies towards being legacy and debt. The more the worse. AI Vibe coding simply creates much more of it, much faster.


Looks good

Not a lot of web sites have made my Macbook M3 show signs of stress ;)


haha thanks!


OP: the readme could really benefit from a section describing the underlying methodology, and comparing it to other approaches (Go channels, LMAX, etc...)


It’s a fairly standard broadcaster based on sync.Cond.


Why are channels so much slower? I would expect a channel to operate basically like a ring buffer + a semaphore.


Channels allow for many-to-many communication.

To a first approximation, you can imagine any decently optimized concurrency primitives as being extremely highly optimized, which means on the flip side that no additional capability, like "multi-to-multi thread communication", ever comes for free versus something that doesn't offer that capability. The key to high-performance concurrency is to use as little "concurrency power" as possible.

That's not a Go-specific thing, it's a general rule.

Channels are in some sense more like the way dynamic scripting languages prioritize ease-of-use and flexibility over performance-at-all-costs. They're a very powerful primitive, and convenient in their flexibility, but also a pretty big stick to hit a problem with. Like dynamic scripting languages being suitable for many tasks despite not being the fastest things, in a lot of code they're not the performance problem, but if you are doing a ton of channel operations, and for some reason you can't do the easy thing of just sending more work at a time through them, you may need to figure out how to use simpler pieces to do what you want. A common example is, if you've just got a counter of some kind, don't send a message through a channel to another goroutine to increment it; use the atomic increment operation in the sync/atomic package.

(If you need absolute performance, you probably don't want to use Go. The runtime locks you away from the very lowest level things like memory barriers; it uses them to implement its relatively simple memory model but you can't use them directly yourself. However, it is important to be sure that you do need such things before reaching for them.)


> Channels allow for many-to-many communication.

Multiple writers can send on a channel, but only one reader will receive a given message from the channel. This makes it unsuitable for the broadcast usecase; the phrasing here makes Go channels sound more general purpose than they are in practice.


What's a concrete example of "concurrency power" here?


You can make a list of guarantees that concurrency primitives provide. You have to get down into the details, ranging from "memory barriers that guarantee all previous operations have completed", which is really quite a weak guarantee, up through things like "no more than one thing can have this lock at a time", which is the sort of thing that a moderately experienced person might already have considered to be one of the weakest guarantees but there's quite a few "memory barriers" that are significantly weaker, up through things like the channel's guarantee of "if you pass the send operation you are guaranteed that some other goroutine has already picked this up", and finally, proceeding upwards to something like a distributed lock's "you are guaranteed to be the only thing holding this lock across the entire cluster", which is very expensive. And this is still only a very-coarse-grained summary of the sorts of concurrency primitives there are.

The easiest one to see is the distributed versus non-distributed versions of locks as they are so dramatically different and so obviously different in expense, but the principle extends all the way down to even different sorts of memory barriers with different guarantees having different costs.

When each of these are optimized to within an inch of their life, as they typically are, including all the way down to the hardware level, stepping up to a higher guarantee level is never free.


I never benchmarked this, so just guessing from principles, take this with a grain of salt. Channel isn't a broadcast mechanism (except when you call close on the channel), so a naive channel-based broadcaster implementation like the one you find in bench/main.go here uses one channel for each subscriber; every event has to be sent on every subscriber channel. Condition variable on the other hand is a native broadcast mechanism. I imagine it's possible to leverage channel close as a broadcast mechanism to achieve similar performance.

Edit: https://news.ycombinator.com/item?id=44416345 seems to have done a much more detailed analysis of the code. There's likely more to this.


you can indeed use channels to implement sync.Cond functionnality, I came across this article a while ago : https://blogtitle.github.io/go-advanced-concurrency-patterns... (scroll down to Condition)


The actual code and the actual bench is very short.


No it's intentional. The highlight is what I call a "known prefix"

It's a hint to tell you the word starts with "s", but since you didn't narrow it down to "su..." or "sv..." it's not giving you more hints.

Once you narrow it down further, say, "sub..." and "sun..." it'll highlight the known prefix "su"


There is a bug in the calculation of the known prefix.

Suppose the target word is before tin, but after timorous. Midword only displays "ti" for the known prefix. But it should display "tim" - there is no string that could start with "tin..." but sort before the string "tin".

(Another bug is that if you take a hint that would reveal the entire word, the site doesn't display what the word was.)


I'll look into these. Thank you.


Aah - not sure why it stopped responding, sorry about that.

I'll try to add some local state management so a refresh for a game doesn't lose progress.


Amazing! I've been enjoying the game a lot!


Thanks - cleaned up the list a bit with the help of AI


I suggest having two lists: a small handpicked list of 1500 possible goal words, and a copy of /usr/share/dict/words with common misspellings added as acceptable guesses


Thanks - I've removed the pattern validation for now


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

Search: