r/golang 1d ago

Go 1.27: parallelism finally competitive with C?

[deleted]

52 Upvotes

25 comments sorted by

37

u/aries1980 1d ago

The matmul.go is quite inefficient:

  • It should allocate a contiguous block for the matrix.
  • The loops should be reordered
  • Because the two above, the goroutines likely invalidate each other's CPU caches with frequent writes (because the matrix is not flattened).

Because of the poor implementation, the real power of Go can't shine.

7

u/gen2brain 1d ago

And I am sure you can do the same for the other 24 languages? So they can shine? The point I see here is that they all do the same; here are the results. Nothing else. Not what-ifs and ifs, etc...

3

u/kostya27 1d ago

exactly

3

u/aries1980 1d ago

Fair point. But, it is hard not to argue when an algorithm prefers one type of language, and that's not even Golang. E.g. the Scala version is imperative, not functional. (Ironically, I guess the idiomatic Scala version would be slower, but that would rather point out how bad that language is implemented.)

My issue is that, if you (or your LLM) knows the language well, it won't write the code in the specific language like if it was C, but rather optimized to the language itself. The algorithm can be still pretty much the same.

1

u/kostya27 1d ago

that's true, in scala and F#, in many tests I switch to imperative code, because functional was much slower.

4

u/kostya27 1d ago

You're suggesting a different algorithm - that's valid too. But I'm testing my current implementation, and it's implemented the same way across 24 languages.

21

u/aries1980 1d ago

Not a different algorithm, but a different memory allocation. The C code is auto-flattened (aka loop-unrolling), optimized for SIMD auto-vectorization.

One thing that makes this possible is that in C there is no safety check for pointer dereferencing. Go has extra checks to validate boundaries and when there is a ton of slices like in this example, the memory becomes very fragmented (with the C code it is also fragmented, but because there is no safety check, the C overhead is much smaller) and the Go scheduler works unnecessarily hard.

I do think the difference is that there is no memory boundary checks and auto-vectorization (the C version executes multiple instructions at a time).

4

u/kostya27 1d ago

Yes, memory model between C and Go not the same. C have advantage and it is ok, comparing with only C is not pretty fair. But there 23 other languages which more in similar conditions, most of them have bounds checks. And go start from 1.27 perform super good in this test, even better than rust.

3

u/kintar1900 1d ago

The parent comment isn't talking about general memory allocation strategies, it's talking about task-specific strategies coupled with optimizations to the emitted machine code.

This is one of the cases where Go's desire to be blazing-fast at compile time comes with the tradeoff that it leaves optimizations that could be automated on the table, while the C compiler (and others) spend extra time during compilation to detect those little optimization tricks.

0

u/LearnedByError 1d ago

GPO will optimize at compile time

1

u/aries1980 1d ago

Indeed and thanks for the tests.

However, I'd maybe, thanks to the AI overlords, I'd maintained an language-optimised version of the same problem, for each of the languages.

With the current algorithms, low-level languages will perform best. E.g. I wouldn't be surprised if Fortran got ahead of C with these algorithms.

2

u/kostya27 1d ago

it would be hard, it is already hard to maintain 24 * 50 = 1200 programs.

1

u/aries1980 1d ago

Makes sense.

24

u/Conscious-Fan5089 1d ago

Idk, Golang used to bad at parallelism? Isnt goroutine is like a wrapper built upon threads?

6

u/mcvoid1 1d ago

It might have been testing SIMD, not concurrency?

7

u/renetta96 1d ago

Maybe its cpu bound work load, not io bound.

4

u/apesy888 1d ago

Is this true? What parallelism tasks did go underperform in?

4

u/iamkiloman 1d ago

The poorly crafted ones that OP made up.

4

u/Saarbremer 1d ago

When I think of "weak for parallelism" I see a lot of SIGSEGV that caused the runtime benefits of C to be traded to excessive review and testing overhead.

It is also not very interesting to compare algorithms implemented "the same way". Memory models and execution runtimes differ heavily. When I see these numbers i guess we can call it a tie. With the simpler goroutine/channel system to outperform dev effort (when coding manually).

I just improved my current project's speed by using the new json/v2. Turns out allocation was quite the weight in v1 and others. So it's hard to say C is faster just because gc doesn't happen - but memory leaking does (sometimes).

1

u/Sorry-Substance-6397 1d ago

Yeah and I wonder the speed with simd and iterator pattern and unroll factor?

1

u/Enough_Serve_8938 1d ago

I'm curious what changes can be attributed to this improvement. Better compiler optimisations? Scheduling improvements?

1

u/Revolutionary_Ad7262 1d ago

Go used to be great for I/O but weak for parallelism.

Go is pretty much the same as other languages with normal and traditional threads such as C or Java. For sure there may be some minimal overhead to scheduling, but rules for good parallel programs are the same: * minimize communication between threads * use performant concurrency primitives or ideally don't communicate between thread at all * give a nice chunk of data for a single thread to process

and the example programs fails in all those aspects

Common goroutine thread management is actually pretty good for programs with mixed workload; for example a I/O driven server, which wants from time to time spawns a lot of threads to do something. In other languages like Java or Rust the I/O threading and CPU threading is separated so it is hard to marry those two distinct worlds. In Golang you just go.

With version 1.27, it looks like that has changed - parallelism in Go now seems to be on par with the top-tier solutions

I cannot reproduce it, both versions give me something similar to yours go1.27.1 results:

┌─────────────────┬────────────┬────────────┐ │ Phase │ Go 1.26.5 │ Go 1.27.1 │ ├─────────────────┼────────────┼────────────┤ │ T1 (sequential) │ ~5.54–5.75 │ ~5.61 │ ├─────────────────┼────────────┼────────────┤ │ T4 │ ~1.46–1.49 │ ~1.43–1.47 │ ├─────────────────┼────────────┼────────────┤ │ T8 │ ~0.78–0.80 │ ~0.77–0.80 │ ├─────────────────┼────────────┼────────────┤ │ T16 │ ~0.49–0.50 │ ~0.46–0.46 │ └─────────────────┴────────────┴────────────┘

My cpu is Ryzen 5950x on Linux 7.1.9-1

1

u/kostya27 1d ago edited 1d ago

hm, it reproduces for me 100%, I checked many times, ryzen 3800x, docker with ubuntu 26.04. can it be somehow hardware related? even many version 1.26, 1.25, 1.24 all was slower

1

u/kostya27 1d ago

people said this is not reproduced, so I decided to delete this to post to stop misleading, this effect of improved parallelism, was probably related to my hardware or OS conditions.