6 ms·
Well this is how I'd write it in Go: descending_squares := []uint{} for x:=4; 0<=x; x-- { descending_squares = append(descending_squares, uint(x*x))
by jaekwon 12y ago
Well this is how I'd write it in Go:
descending_squares := []uint{}
for x:=4; 0<=x; x-- {
descending_squares = append(descending_squares, uint(x*x))
}
- pcwalton 12y agoUse unsigned integers for your index and values. That's what makes it hard to use a for loop. (Sure, you could cast a signed integer loop index to unsigned inside the loop to avoid the underflow problem in this specific case, but I'd argue that the functional style is so much clearer than code that has to work around unsigned underflow gotchas.)
- jaekwon 12y agoAll I did was take your for-loop code and translated into idiomatic Go code. My point is the for-loop in Go isn't as terrible as the loop example you wrote.
- pcwalton 12y agoYour version infinite loops: http://play.golang.org/p/uglbTETE6d http://play.golang.org/p/uglbTETE6d See why for loops are tricky? :)
- jaekwon 12y agoDoh! I fell straight in :) Overflows in general are tricky. How does Rust deal with it?
- pcwalton 12y agoWe have special checked types you can use if you want checked arithmetic. The default is to not check, because CPUs currently make it expensive to check (although I would love it if that could change--we need hardware support though).
- jaekwon 12y agoNice. :)
- acqq 12y agoBut CPU's have the overflow flag, that is actually set if the operation overflows? You can't write that check explicitly in C, but you can in assembler. Available now, across the different hardware.
- dbaupp 12y agoAs you say, detecting the overflow is easy, but efficiently handling it is not. It adds a branch to every single arithmetic operation, and it makes it much harder for the compiler to optimise things e.g. it is hard to vectorise a loop summing an array, if every + has a conditional branch on the overflow flag. (Also, I believe it introduces a lot of data dependencies, getting in the way of the out-of-order execution of modern CPUs.)
- acqq 12y agoTo handle the overflow on the places where you want to handle them, modern processor doesn't have any problem with an additional jump instruction. Also, modern compilers could optimize the checks away if they aren't used. In effect, implementing overflow checks definitely won't turn your C speed (1s) code in a Python speed (40s) code. I estimate it wouldn't be even two times slower in most of the use cases. It's certainly not the problem of the CPU's. It can be the problem of the certain compilers if they don't have the infrastructure to reason about overflow flags though. But it's not a hardware problem.
- masklinn 12y ago> I estimate it wouldn't be even two times slower in most of the use cases. In his "We Need Hardware Traps for Integer Overflow"[0], Regher quotes 5% to 100% overhead for languages such as JS or Racket, and that a "highly tuned" checker would likely be in the 5% range. Playing with arithmetics-heavy programs and Rust's checked_* (which are backed by LLVM's overflow intrinsics[1]) I got anywhere from 5 to 40% performance loss IIRC. That's not a lot, but at the same time when you're competing with languages specifically not paying those 5%, a 5% hit on all computations is not going to get you much love. Which is why Rust currently lets you do that (via num::Checked* and num::Saturating) but uses overflowing default semantics. [0] http://blog.regehr.org/archives/1154 http://blog.regehr.org/archives/1154 [1] http://llvm.org/docs/LangRef.html#arithmetic-with-overflow-intrinsics http://llvm.org/docs/LangRef.html#arithmetic-with-overflow-i...
- deleted 12y ago[deleted]
- codygman 12y agoI love when arguments are decided by/proven by runnable code examples.
- hit8run 12y agoJust use int and it works fine: http://play.golang.org/p/nZcd3M5aL_ http://play.golang.org/p/nZcd3M5aL_ To me golang is readable while even small rust examples don't feel quite right right.
- masklinn 12y ago> Just use int and it is a completely different piece of code.
- gorhill 12y ago> functional style is so much clearer There is something that makes me uncomfortable in there: > let descending_squares = range(0u, 5u).rev().map(|x| x * x).collect(); The overhead. I wonder about it. I am unable to get a sense of what it is. With a simple for-loop, it's rather easy to see it, but with the version above I have no idea. So "much clearer" is not what I see with the piece of code above. I see what it does, but what is not so clear is what code will be generated, something which matters when trying to write efficient code.
- dbaupp 12y agoThere is not much overhead with Rust iterators at all. They are essentially concrete versions of the deforestation that Haskell can do to avoid constructing intermediate lists, and compile to code that is close to the equivalent C (the functionality is all statically dispatched, so the compiler can inline and optimise the calls). This is a case of having experience with and trusting one's tools. I wrote a blog post a while ago that used iterators heavily http://huonw.github.io/blog/2014/06/comparing-knn-in-rust/ http://huonw.github.io/blog/2014/06/comparing-knn-in-rust/ , as you can see the performance is good.
- gorhill 12y agoI am definitely going to have to look more into Rust.
- kinghajj 12y agoWell then, good news! Such usages of iterators in Rust are almost always result in the same code that would be generated by old-fashioned, hand-written loops. The 'trick' is that the methods (and the closure!) are inlined, and generics are specialized at compile time.
- jksmith 12y agoOr if Go and Rust had a FOR loop guaranteed to stop at 0, as with a Wirthian language, no?