6 ms·
Go is incapable of ever achieving C like speeds, and that's not because of runtime safety checks like bounds checking. It's because Go still has a garbage colle
by unscaled 10y ago
Go is incapable of ever achieving C like speeds, and that's not because of runtime safety checks like bounds checking. It's because Go still has a garbage collector, and that always comes with a price tag.
Go also often has to copy structs and buffers that would be zero-copy in C, C++ and Rust code. Passing slices to functions which requires arrays or converting between strings and byte slices requires copy for instance.
There's also the matter of poor control of stack vs. heap allocation and poor escape analysis to mitigate that.
All in all, Go provides very impressive performance when compared to other high-level languages, but calling it C-level performance is pure hype.
- kardianos 10y agoThis is true. But much of the GC can be done on parallel threads, reducing the amount of total work that can be done, but keeping the threads that do work nice and speedy.
- _ph_ 10y agoThe garbage collector does not impact the speed of tight code - when it runs it of course takes cpu power, but it runs in parallel to the program code, so this comes pretty cheap. And the GC only consumes any resources, when you allocate heap space. This is generally not necessary, and also comes with a steep price when done in C. Coding efficiently in Go requires some different best practices in C, so this might have gotten you the impression of inefficiencies, but once you have adapted to the good Go practices, there is very little difference.
- bkeroack 10y agoExactly. If you write in a way that allocations are controlled, GC becomes irrelevant to performance. Well-optimized Go without allocations is indistinguishable from C (modulo compiler differences).
- woah 10y agoAre there guides on how to do this? I've always assumed that passing by value helps but I'm wondering if there's more explicit guidance out there?
- _ph_ 10y agoI have no guide at hand, but here a few hints: - in general, understand which types are values, and which have to be allocated. E.g. the slice structure itself is a value type (typically 24 bytes), but it points to an array, which might be heap allocated. Re-slicing creates a new slice structure, but does not touch the heap - extending a slice might. - to analyze your code, use go build -gcflags="-m", this will print out optimization information, especially which allocations "escape to heap", which means, they are heap allocated and not stack allocated. - Interfaces are really great, but copying values of interface{} does involve heap allocation of 16 bytes. Also method invocation via interfaces has some overhead. Method invocation of non-interface types is a fast a plain function call. - profile :)
- lossolo 10y agoThen go for it and make those 7/10 benchmarks in which Go is at least 2x slower than C to be the same speed. http://benchmarksgame.alioth.debian.org/u64q/compare.php?lang=go&lang2=gcc http://benchmarksgame.alioth.debian.org/u64q/compare.php?lan...
- igouy 10y agoPlease show some examples that support what you say.
- rbehrends 10y ago> It's because Go still has a garbage collector, and that always comes with a price tag. This is not really true, at least not as such a general claim. 1. malloc()/free() can be either faster or slower than garbage collection. It depends on implementation details and is situational. Note in particular the issues of memory locality and temporary allocations. 2. This assumes an omniscient programmer who has perfect knowledge of when to allocate or free. In reality, memory management is a traditional cross-cutting concern, and in order to deal with it manually in a modular fashion, you will typically introduce additional overhead. Examples: - in C++, m[a] = b, where m is a map, and a and b are strings requires that both a and b are being copied. This is not necessary in a GCed language. - naive reference counting has significant overhead, significantly more than a modern tracing GC. - even unique_ptr has overhead (though less so than reference counting). Garbage collection allows you to have the necessary global knowledge to (mostly) eliminate memory management as a cross-cutting concern, because the GC is allowed to peek behind the scenes and ignore abstraction boundaries. 3. Things that C is fast at is usually also allocation-free code, i.e. iterating over arrays or matrices. The performance of such code is not going to be any different in a GCed language, assuming an optimizer of comparable caliber.
- swift 10y agoI agree with almost everything in your post, but I'm surprised about your comment that unique_ptr has overhead. Since unique_ptr is a single item struct with no vtable I'd expect no space overhead at all. Similarly, while it does need to run code in its destructor, I'd expect a trivial, non-virtual destructor like that to be reliably inlined and the result to be no worse than a manual call to delete. If that's not true, though, I'd definitely like to know, so please let me know if I've missed something.
- rbehrends 10y agoIt has runtime overhead whenever a move assignment occurs. You have to check if the target of the assignment is null (or otherwise call the destructor) and you have to assign null to the source. This is all overhead compared to a plain pointer assignment. An optimizing compiler will of course try to eliminate most of that overhead, but that's not always possible.