Live data from Hacker News

The Go Compiler Needs to Be Smarter

lemire.me

111–119 of 119 posts

Re: The Go Compiler Needs to Be Smarter

#111
post #63

Earlier quoted context omitted.

1. That's a rare use case 2. There are ways of measuring that without hampering readability. I mean, are programmers supposed to add all the variables' sizes up in their head??

Also, unless your thread has only a single function that has local variables or arguments and that function doesn’t contain sub blocks that declare variables (say inside a while block), All variable declarations at top of block doesn’t help much in gauging stack space usage of a thread. Actually, not even that helps, you would also have to know how much stack a function call takes (might be non-trivial in the presenc…

> if you declare all your locals at the start of a function, chances are the compiler will check whether it can make some of them share memory, so you’d have to take that into account, too.

And that's ignoring registers. Not every local ever needs to reside in memory.

Re: The Go Compiler Needs to Be Smarter

#112

Earlier quoted context omitted.

> All variable declarations at top of block. And they say they want their C code to suck less ? Why voluntarily open the door to read-before-write undefined-behaviour? The lifetime of a local should be as short as it reasonably can be.

My take on this: pro: - Storage concerns are separated and grouped at the top. - Logic code is more compact, helping readability. - Stack use can be determined at a glance. - Looking up a variable's type is faster in cases where a variable is used throughout the code block. This is good to optimize, as you constantly need to look up variables when reading C code. - Reflects that object lifetime of any stack storage o…

> Storage concerns are separated and grouped at the top.

That's rather circular. That's what defines the style, it's not in itself an advantage or a disadvantage.

> Logic code is more compact, helping readability

It's slightly more compact, but it harms readability, as I describe at [0]. Using the new style, especially with the const keyword, can make code much more readable.

> Stack use can be determined at a glance.

It can't, that's up to the compiler. Perhaps all locals will be enregistered. Even if it were true, it's extremely rare to need to do this.

> Looking up a variable's type is faster in cases where a variable is used throughout the code block.

In certain circumstances this may be true, but there may still be multiple levels you may have to check. For short-lived locals, it will be quicker to look up the type using the new style, as that keeps the declaration close to where it is used.

Using a modern IDE, it's a draw - just hover over a local and it will tell you its type.

> Reflects that object lifetime of any stack storage object is the same regardless of where it is declared in the code block.

That doesn't sound right. That will be platform-dependent. I see no reason a compiler couldn't generate incremental pushes, rather than an upfront block allocation. Also, depending on what the compiler generates, different locals may end up occupying the same place in the stack (at different times), and a given local may be held in different places in the stack at different times. And that's if we're ignoring registers. No matter what we do, we can't rely on the generated assembly looking anything like the source code.

Beyond that, it's 'dishonest' about locals' real lifetimes, in a way that can be harmful. Compare:

    { // Old style
        int i;
        int j;

        i = j; // Undefined behaviour
        j = 7;  
    }
 
    { // New style
        int i = j; // Compile-time error. 'j' is not in scope.
        int j = 7;
    }
> "-Wuninitialized" exists, but passing pointer and defining 'default' value style can still cause read-before-write.

I'm afraid I don't get what you're saying here. Are you referring to the danger of passing a 'default' value to a function, say, in a way that might be dangerous?

Re: The Go Compiler Needs to Be Smarter

#113

Earlier quoted context omitted.

> All variable declarations at top of block. And they say they want their C code to suck less ? Why voluntarily open the door to read-before-write undefined-behaviour? The lifetime of a local should be as short as it reasonably can be.

But can't the compiler work that bit out? Source code is for humans. Put all the vars at the top to tell the humans "here's all the scratch space I'll be needing in this block" and then let the compiler observe "var x is only used twice, so let's optimize that lifetime..."

> can't the compiler work that bit out

Technically no, not in all cases, due to the halting problem. In practical terms, read-before-write issues do happen in real C code, so it makes sense to take steps to avoid it. (Languages like Java force the programmer to write code where the compiler can guarantee the absence of read-before-write errors, sometimes just synthesising an assignment of zero, but it's still possible the programmer will assign a dummy value and accidentally end up using it.)

> Source code is for humans.

Yes, that's precisely my point. It's about making the code readable and easy for a programmer to reason about. It's unlikely there will be any performance impact either way; decent compilers should be good at lifetime-analysis and register-allocation.

It's more readable to declare a short-lived local on its first use. This makes its precise type more apparent, as you don't need to scroll up to its declaration. This is particularly important in C, where using the wrong type can have especially nasty consequences.

The new style also makes it immediately clear over what scope the variable is relevant, as the local does not exist in scope until it is declared and assigned. That is to say, it only exists when it should. I expand on this in my other comment in this thread.

Related to this, the new style helps prevent undefined behaviour by making it less likely you'll accidentally introduce a read-before-write. Again, those errors do happen in real production code. It's the kind of error static analysers pick up in long-trusted codebases.

The old style makes your code less dense, artificially increasing the number of lines in a function.

The new style also enables you to use const, which of course requires assignment at the point of declaration. If you use const with your locals, you do not have to scan the code to determine if the local is modified later on, you know at a glance that it will not be. This lets you reason about values, rather than the current state of a local. If you can access the local, you know it holds the right value. [0]

If it turns out the lifetime of a local needs to be broadened, you can move the declaration up to a broader scope, but in my experience this is surprisingly rare.

It's not exactly relevant, but in C++, with RAII, you don't really have a choice, and you pretty much must use the new style rather than the old-school C style. But that doesn't tell us much here. In a similar vein, Java and C# programmers could use the old-school declare-at-the-top style, but none of them ever do.

It's just a style that used to be necessary in old versions of the C standard, which people got accustomed to. For what it's worth, the Linux kernel seems to use both styles. [1] [2]

> here's all the scratch space I'll be needing in this block

For the reasons I've given above, I don't think this is a good way to approach locals. It makes sense to leverage scope and constness to improve readability, not to just introduce a free-form set of uninitialised locals with overly broad lifetimes. That approach opens the door to avoidable bugs, and needlessly burdens the reader with having to scan the code to determine basic properties of the locals (which they may then get wrong).

[0] https://www.infoq.com/presentations/Value-Values/

[1] https://github.com/torvalds/linux/blob/master/init/do_mounts...

[2] https://github.com/torvalds/linux/blob/master/kernel/sched/c...

Re: The Go Compiler Needs to Be Smarter

#114
post #110
post #99

Earlier quoted context omitted.

Wait until a webasm thread comes around again and you'll see a real rabbit hole of extreme conflation, false claims, bad faith arguments and willful ignoring of facts and evidence.

Glad not to disappoint the accolades of WebAssembly knighthood.

Please don't do any more flamewars like this on HN ever again. What a train wreck.

https://news.ycombinator.com/newsguidelines.html

Re: The Go Compiler Needs to Be Smarter

#115
post #47
post #18

Earlier quoted context omitted.

Even Java and .NET do better on auto vectorization. Intel has recently published an article on using Go, their advice? Manually use cgo or Go Assembly for the AVX.

Apples to orange comparison. Java and .NET have different goals, around 2 decades of optimization and a lot more money thrown at them. Also, hi again pjmlp! Another Go thread, another nonconstructive Go bashing coming from you. From your other comments here I see you got pretty combative this time.

Please don't ever start or feed flamewars like this on HN again. We ban accounts that do that, and what you did here was egregious.

https://news.ycombinator.com/newsguidelines.html

Re: The Go Compiler Needs to Be Smarter

#116
post #103

Earlier quoted context omitted.

Generics, I would certainly be ecstatic for if it was done in a way that is novel and feels Go-like , so not Java or C# or C++'s implementations. This has certainly been the stance of the Go team since the beginning of time. It has never been "anti-generics" it's always been "we've studied all the generics implementations out there, and didn't feel like any one of them were good enough, so rather than shoehorn them i…

What's wrong with the way that Java, C# a, and C++ handle generics? I see this complaint fairly regularly, but I was never sure why. Is it how the compiler handles it, or how the language defines it that is the reason for this dislike? Genuine curiosity.

This is the lazy answer I know, but I wanted to at least answer your genuine curiosity: There is ample research available online on the subject that explains it in depth and far better than I ever could. However, I will leave you with a few quotes that help at least paint the picture.

From the Golang FAQ:

> Generics are convenient but they come at a cost in complexity in the type system and run-time. We haven’t yet found a design that gives value proportionate to the complexity

Quote from Russ Cox:

> The generic dilemma is this: do you want slow programmers, slow compilers and bloated binaries, or slow execution times?

Finally, one of the reasons the C++ or Java approach has never been palatable to Go core devs is summarized from this Rob Pike quote from his famous "Less is Exponentially More" blog post:

> If C++ and Java are about type hierarchies and the taxonomy of types, Go is about composition

So therefore it really is also a matter of finding a generics approach that lives up to that spirit as well. Contracts I think are approaching that.

All that said, user defined generics (since technically speaking, Go has many generic capabilities today already) are coming to Go, they just aren't being rushed. I think we will be happy with the generics implementation in Go within the next year.

Re: The Go Compiler Needs to Be Smarter

#117

Earlier quoted context omitted.

> Source code is for humans The reason why making the scope as local as possible is important is for humans , not compilers. > Put all the vars at the top to tell the humans "here's all the scratch space I'll be needing in this block" Why would humans care about how much scratch space is needed? That's for the compiler to know.

We're not talking about scope. The scope is already decided. "Vars at the top of the block" doesn't mean "take those extra vars out of the while{} scope and elevate them to the next enclosing scope." You've taken my "scratch space" too literally. Very few people need to count bytes for local vars. I'm talking about future maintainers reading and understanding code. Grouping the current block's variables at the top sa…

The scope of a variable is from where it is declared to the end of the block. Moving a variable to the top of the enclosing block means that it can be referenced from more places in the code, which increases its scope.

Warnings about uninitialized variables help, but don't catch everything. For example, you don't usually get a warning for passing the address of an uninitialized variable to an external function (since it might be an output parameter), but that would be undefined behavior if the function expects the variable to be initialized. Initializing variables at the point where they are declared ensures that they can't be referenced at all in an uninitialized state.

Rust has a slightly more nuanced (and IMHO superior) system: non-mutable ("const") variables can be assigned exactly once, possibly but not necessarily at the point where they are declared, and all variables must be initialized before use, including passing references to other functions. This permits more flexibility in how the code is arranged while simultaneously offering stronger guarantees against undefined or otherwise erroneous behavior.

Re: The Go Compiler Needs to Be Smarter

#118
post #115
post #47

Earlier quoted context omitted.

Apples to orange comparison. Java and .NET have different goals, around 2 decades of optimization and a lot more money thrown at them. Also, hi again pjmlp! Another Go thread, another nonconstructive Go bashing coming from you. From your other comments here I see you got pretty combative this time.

Please don't ever start or feed flamewars like this on HN again. We ban accounts that do that, and what you did here was egregious. https://news.ycombinator.com/newsguidelines.html

I assume full responsibility. I started it.

Regardless of pjmlp's behavior in Go threads it doesn't justify me lowering the bar like this.

You wont see this kind of content coming from me in the future.

Re: The Go Compiler Needs to Be Smarter

#119
post #13
post #7

It will be a whole lot smarter on June 8th at 7am pacific, https://www.youtube.com/watch?v=Dq0WFigax_c

I believe it when I see it on https://golang.org/doc/devel/release.html Until then, it is just yet another "we are working on it" like so many that have come up during the last 10 years.

It has Lambda Man and Griesemer on the case. I think this one will stick because it was requested by the Go team and there is precedence with featherweight java and that Wadler designed the generics for Java. [1]

There are prototypes you can play with now.

https://blog.tempus-ex.com/generics-in-go-how-they-work-and-...

[1] http://homepages.inf.ed.ac.uk/wadler/

Post reply on HN