3 ms·
Why? Channels are already iterable using the range keyword. ch := make(chan int) go func() { for i := 0; i < 100; i += 1 { ch <- i
by rcme 3y ago
Why? Channels are already iterable using the range keyword.
ch := make(chan int)
go func() {
for i := 0; i < 100; i += 1 {
ch <- i
}
close(ch)
}()
for i := range ch {
fmt.Println(i)
}
That is very simple.
- freecodyx 3y agoRace conditions. With coroutines you’re not supposed to deal with race. If i am understanding the motives
- rcme 3y agoYou can write to a channel concurrently.
- ackfoobar 3y agoAnd to make the concurrency safe you have to pay the price of synchronization. https://go.dev/tour/concurrency/8 https://go.dev/tour/concurrency/8 In A Tour of Go, concurrency section, "Equivalent Binary Trees" is an example of paying the price when you don't need it.
- ikiris 3y agoits not that you cant, it's that its very expensive.
- jerf 3y ago"Enormous amount of overhead" is the operative phrase. In general, you want your concurrency operations to be significantly smaller than the payload of the operation. In the case of using channels as iterators with goroutines behind them, it works fine for something like a web page scraper, where the act of fetching a web page is enormously larger than a goroutine switch, but as a generalized iteration mechanism it's unusably expensive because a lot of iteration payloads are very small compared to a channel send. I've encountered a lot of people who read that on /r/golang and then ask "well why are send operations so expensive", and it's not that. It's that a lot of iteration operations are on the order of single-digit cycles and often very easy to pipeline or predict. No concurrency primitive can keep up with that. A given send operation is generally fairly cheap but there are enough other things that are still an order or two of magnitude cheaper than even the cheapest send operation that if you block all those super cheap operations on a send you're looking at multiple factor of magnitude slowdowns. Such as you would experience with your code.
- rcme 3y agoBut if your iteration is so fast then you don’t need a channel at all. Just use a plain for loop.
- zbentley 3y agoYes, that's exactly what the article is about: how we can model "plain for-loop" levels of performance in the presence of complex (intricate state at multiple levels and high potential for nesting iterators) code that is supplying the loop(s)
- wbl 3y agoHow do you solve the tree fringe problem with a for loop?
- Jtsummers 3y agoSince all recursive programs can be converted into iterative programs then you can "simply" (not always simple) convert recursive solutions like McCarthy's Lisp solution to a loop: https://dl.acm.org/action/showFmPdf?doi=10.1145%2F1045283 https://dl.acm.org/action/showFmPdf?doi=10.1145%2F1045283 (page 5) (DE SAMEFRINGE (X Y) (OR (EQ X Y) (AND (NOT (ATOM X)) (NOT (ATOM Y)) (SAME (GOPHER X) (GOPHER Y))))) (DE SAME (X Y) (AND (EQ (CAR X) (CAR Y)) (SAMEFRINGE (CDR X) (CDR Y)))) (DE GOPHER (U) (COND ((ATOM (CAR U)) U) (T (GOPHER (CONS (CAAR U) (CONS (CDAR U) (CDR U))))))) Coroutines are not necessary for solving that problem (though they do offer a neat solution to it).
- wbl 3y agoThe results of that conversion are not fun
- jerf 3y agoThen you lose the separation of iteration from the looping construct. You can use a function or a method, but you lose the nice continuation/generator ability to write a function that can do complicated yields without having to write the state machine yourself, plus you run a risk that the call won't inline in which case you're incurring non-trivial function call overhead. The problem with iteration in Go isn't that you can't solve any given individual problem, the problem is that you can't solve all of them simultaneously the way you can in Rust or Python. (Though one of these days I want to get around to benchmarking Python's iteration versus Go channel-based iteration, I'm not actually sure which would win. What Go considers dangerously slow can still be baseline performance for other languages.) So you can get a defeat-in-detail sort of thing where a person cites a problem, and someone posts as solution to that, and then they cite another problem, and there's a solution for that, and then there's another problem, and a solution is posted for that, and all the solutions do indeed more-or-less solve the given problem... but you can't combine them into one.
- t8sr 3y agoAside from the massive performance penalty, cache thrashing and context switching, this code will also leak a goroutine (and so, memory) if you don't finish receiving from `ch`. It's more brittle, longer to write, less local and in every other way worse than a for loop. Why would you ever do it?