Live data from Hacker News

Making a Go program faster with a one-character change

hmarr.com

101–110 of 249 posts

Re: Making a Go program faster with a one-character change

#101
post #17

Earlier quoted context omitted.

BTW; I'm using both Go and Rust lately. In Rust you can write a function that returns the pointer of one element of a slice. You can also write a function that returns the pointer to a heap-allocated copy of an element of the slice. The two functions would have different signatures. The compiler would also prevent mutation of the slice as long as there are any references to individual elements of the slice being pass…

>In Rust you can write a function that returns the pointer of one element of a slice. Have fun fighting the borrow checker on that one.

[deleted]

Re: Making a Go program faster with a one-character change

#102

Earlier quoted context omitted.

Historically Java has traded long pause times for fast allocations, although I'm of the impression that it has recently found a way to have its cake and eat it.

Java has been tunable for a long time. Periodically, the recommended tuning changes, or new GC algorithms become available, etc. But it has long been possible to get short pause times with various combinations of choosing the right algorithm and writing your program the right way. I think what really throws people off here is that getting good performance out of a Java application involves some skills which are alien…

Agreed, but usually tuning for short pause times involves trading off throughput or allocation performance. But at the end of the day, if you aren't allocating a bunch of garbage in the first place, then you don't need to be as concerned about the costs of allocating or cleaning up the garbage. I wish Go did more to make allocations explicit so they could be more easily recognized and avoided; I dislike Java's approach of making allocations even more implicit/idiomatic while trying to fix the cost problem in the runtime (although I admire the ambition).

Re: Making a Go program faster with a one-character change

#103
post #64

A while ago at my company we switched from GCC to Clang, and noticed a couple of massive regressions (on the order of 50%?) in performance having to do with floating point. After profiling for a bit, I discovered that suddenly a lot of time was spent in isinf on Clang and no time in GCC… Clang was emitting a function call where GCC wasn’t. I happened to randomly change isinf to std::isinf (it’s a random habit of mine…

C defines isinf as a macro, whereas C++’s std::isinf is a function. Perhaps the discrepancy has to do with differences in how they’re evaluated?

Re: Making a Go program faster with a one-character change

#104

Earlier quoted context omitted.

It's the difference between assigning/passing around "copies of the data" vs. assigning/passing around "the memory address for that data" under the hood. PHP, for example, has explicit references. If you have an `$arr1=array(1,2,3)` and an `$arr2 = $arr1`, that second array is a full copy of the first array, and updating $arr1 does nothing to $arr2. Similarly, `function update_array($arr) { $arr[0] = 'cake'; }` calle…

That's not technically correct with regards to PHP. Your statement that any changes to $arr1 or $arr2 only impact the one in question, however, is accurate. If no changes are made they still refer to the same data in memory. It's copy-on-write semantics. $arr1 = [1,2,3]; // $arr1 is a pointer to a zval array [1,2,3] and refcount:1 $arr2 = $arr1; // $arr1 and $arr2 are pointers to the same zval array but incremented r…

true, good point.

Re: Making a Go program faster with a one-character change

#105
post #53

Earlier quoted context omitted.

Allocating isn't "an expensive copy"; it's not analogous to clone() in Rust. The copy isn't the problem, it's the allocation.

I'd argue quite the reverse. Allocation can be quite efficient if done properly, but copying involves a lot of other work.

I disagree--the bottleneck here is entirely the allocation. The copying is just a memcpy and it's very fast for small structs like this; like I said, it's not the same as a clone() in Rust, which is a deep copy. If you optimized the allocation away entirely (leaving only the copy cost), there wouldn't have been a significant performance problem and this blog would never have been written.

Re: Making a Go program faster with a one-character change

#106
post #60

Earlier quoted context omitted.

> Java programs make "ridiculous amounts of implicit allocations" because allocations are cheap in Java. And they need to be cheap because Java doesn't have value semantics so it leans hard on escape analysis + cheap allocations. Yes, but that's kind of the point, right? Implicit allocation isn't really a problem because a runtime that optimizes the allocations magically for you is a lot easier to build than a runtim…

> Implicit allocation isn't really a problem because a runtime that optimizes the allocations magically for you is a lot easier to build As far as I know, Java's (default) runtime gives cheap allocations at the cost of long GC pause times. > than a runtime that optimizes whether you really need to be copying objects as much as you do It's not "copying", it's "allocating", and avoiding allocations isn't that much work…

> As far as I know, Java's (default) runtime gives cheap allocations at the cost of long GC pause times.

"long GC pause times" is kind of vague, so I guess you could be correct, but in practice there's a LOT of different ways the memory management can be handled, many of which are deemed "pauseless GC" (though the term is somewhat misleading).

My statement was considering that reality though. While not true for some use cases, in the vast majority of cases, the runtime optimizes the allocations more than sufficiently.

> It's not "copying", it's "allocating"

Allocators can do a pretty good job of minimizing the overhead of allocation, to the point the amortized cost isn't much more than a single machine instruction. Allocating gigabytes of memory quickly is possible. Copying the data can be a lot more work, and often objects have copy semantics that add a lot more additional work.

> Anyway, "a runtime that minimizes allocations" is just an escape analyzer and Java has one of these too, and IIRC it's a lot more sophisticated than Go's (but it's also a lot harder to reason about as a consequence).

I think you're implicitly saying "a runtime that minimizes heap allocations" there, in which case I'd agree.

Re: Making a Go program faster with a one-character change

#107
post #96

Earlier quoted context omitted.

> I increasingly see the wisdom of languages with implicit reference semantics. I spend most of my time in a JVM language of one flavor or another, and when I was learning Go, the first thing that stuck out at me was, "why would I ever want the compiler to invisibly copy a data structure for me?" I suppose the primary reason is to prevent the callee from modifying the caller's data out from under them; unless you pas…

> When I write code, it's common to have references to immutable classes thrown around with wild abandon, heedless of ownership, threads, or good taste, because the data just can't change. If there's anything I wish languages with implicit reference semantics would adopt, it's implicit immutability. I wish Java would be so much nicer with keyword that is half way between "final" and "volatile" that means, "yes, you c…

Agreed.

Could you Imagine a Java where you have a `Map` and a `MutableMap` and that's what you put at your API? I'd make it SO much clearer how safe any individual API is to call.

Re: Making a Go program faster with a one-character change

#108
post #25

Somewhat off topic, but I find a different part of this to be quite ugly: if match || err != nil { return rule, err } Translating this code to actual logic takes too much thought and is too fragile. Is that an error path or a success path? It’s both! The logic is “if we found a rule or if there was an error then return a tuple that hopefully indicates the outcome”. If any further code were to be added in this block,…

They should probably replace that with 2 if statements to make the error path and the non-error path obvious:

    if err != nil {
        return nil, err
    }
    if match {
        return rule, nil
    }

Re: Making a Go program faster with a one-character change

#109
post #47
post #26

I was trying to debug and improve the performance of some parallelized C++ code over the weekend for parsing CSV files. What would happen was parsing each file (~24k lines, 8 columns) would take 100ms with one execution context, but when split across many threads, the execution time of each thread would slow down proportionally and the throughput of the whole program would strictly decrease as thread count increased.…

False sharing, maybe?

Not a bad suggestion - thanks for the idea
Post reply on HN