The warning used to be a lot less alarming but then people complained when redefining const
bindings caused problem, so it was made more alarming.
A load of global is currently unordered
which is even weaker than the weakest monotonic
ordering we can use in the Julia programs. monotonic
is called relaxed
in C++ and it doesnāt have any power of ācross-thread communicationā on its own (it basically only guarantees that no load/store tearing happen). Furthermore, itās formally proven that various compiler optimizations are legal under C++ memory model (which does not necessarily mean LLVM does that for us [1]). In particular, hoisting out relaxed loads out of the loop is allowed (unless there are stronger atomic operations in the loop). Since unordered
is weaker than monotonic
, the load can be hoisted out. Indeed, in 1.7.1:
julia> mutable struct Var{X}
x::X
end
julia> const V = Var(Int[]);
julia> function f(n)
a = nothing
for x in 1:max(1, n)
a = V.x
end
a
end;
julia> @code_llvm debuginfo=:none f(1)
define nonnull {}* @julia_f_137(i64 signext %0) #0 {
top:
%1 = load atomic {}*, {}** inttoptr (i64 139758282988048 to {}**) unordered, align 16
ret {}* %1
}
(But no, Iām not recommending global mutable states.)
You are right, we currently only emit unorderd
loads for global bindings.
I donāt think this example actually illustrates it though, since T
is a constant and constants are special-cased in codegen so that the actual load is elided. All you are seeing here is the load from V
of the field x
, not the actual lookup of the global binding V
from the module, right?
Iām using getfield
instead of GlobalRef
to illustrate my point since I was too lazy to build your branch. Once the codegen emits the unordered
load in the loop, what I pointed out holds independent of what syntax was used in the frontend. For example, I could have used
function f(v::Var, n)
a = nothing
for x in 1:max(1, n)
a = v.x
end
a
end;
as an example. (A more important point is that Var
holds a GC-manged object.)
Yes, this is exactly what I wanted to show.
Ah, I see! I think I was just a bit confused since you made V
a constant global.
Indeed, you can actually observe the same behavior with #43671:
julia> v::Int = 1;
julia> function f(n)
a = 0
for x in 1:max(1, n)
a = v
end
a
end;
julia> @code_llvm debuginfo=:none f(10)
define i64 @julia_f_1262(i64 signext %0) #0 {
top:
%1 = load atomic i64*, i64** inttoptr (i64 140085820903256 to i64**) unordered, align 8
%2 = load i64, i64* %1, align 8
ret i64 %2
}
Thanks for checking this! And yes, I agree it was confusing now that I think that this thread has been discussing const
vs global
. I shouldāve used f(v::Var, n)
as an example.
Thanks for asking! I donāt think youāre missing anything but itās clear we have different perspectives (probably related to our disagreement on stacked enviroments). This has been on my mind for a while, Iāll probably write a blog post at some point, but hereās a first attempt to explain my point of view:
Itās hard to write reliable software, letās say, software that has very low probability of behaving wrongly. There are so many ways to make mistakes and so many things to keep in mind while coding. Compilers and other tools can find problems, and of course writing tests helps a lot, but for the rest itās on me: when writing serious code Iām always thinking ādid I think of every possibility? is there anything that can go wrong here?ā etc. Itās a huge mental load.
In this context, the most valuable thing a language or tool can give me is āDonāt worry about that, it canāt happen!ā. But this only works if correctness comes first. Not as a tradeoff with convience and performance. Imagine a FastLock
type that is 50% faster and works correctly 99.999% of the timeā¦ Iām not interested at all. Zero percent. So back to our const
rebinding, ātypical that everything worksā is a big no-no.
About the warning, I was happy when it was improved. I remember when I discovered the const
rebinding feature it used to say simply WARNING: redefining constant
and I felt bad in the stomachā¦ I wanted Julia to be perfect, and here was a case where the compiler finds a problem that can cause undefined behavior, and I just get a casual warning? I felt like āno way! how could the devs be so careless about correctness?ā The new warning however suggests that more weight is given to correctness.
Maybe thatās what bothers me the most: what the feature communicates about the languageās priorities. It suggests that a convenient REPL experience is more important than a correct program. Worse: the feature is also available in package code, so it seems that restricting the feature to interactive use was deemed not worth the effort (or the weight of convenience was too pressing to wait).
Somehow this really hurt my sensibilitiesā¦ My perspective is influenced by a background in fields that put a premium on reliability (industrial robotics and drone control software), but Iām probably not alone. Maybe Julia is losing something here in terms of contributions: I can imagine that a number of software engineers have looked at the language and were turned off by the relatively small consideration given to reliability. They might be precisely the people who would contribute to make important packages more āprofessionalā in terms of reliability, documentation, etc. (no offense to the developers of these packages, their work is much appreciated!).
To end on a positive note: I have the feeling that things are moving in a good direction. The most offensive footguns are progressively retired (cf. the scary warning weāre discussing here and the documentation changes here and here for @pure
).
To elaborate on the const
rebinding itself:
I donāt really see the value of the feature when as the warning says the correctness of the program is not guaranteed afterwards. Even for development, I would be annoyed when anything goes wrong that I must wonder if maybe thatās because of a const
rebinding. And in the odd case where I want to copy-paste const
definitions several times in the REPL, itās not that hard to wrap the whole thing in module XXX ... end
. Not worth having the language seem so careless about correctness.
An option to make the warning an error doesnāt make much sense for me, but the opposite, having an opt-in flag to allow const
rebinding would be an improvement. As would be restricting the feature to interactive use.
I donāt mind if we allow executing const a = 4
several times. Thereās no problem as long as the new assignment is ===
identical to the previous one.
It gets trickier with something like const A = [1,2,3]
. I suppose even in this simple case a reassignment can produce an invalid program?
I like the idea of @StefanKarpinski of having full support for const
rebinding, i.e. as a heavy operation that recompiles all necessary code, for interactive use. It still contradicts the semantics of const
and would be terrible for readability, so I guess it should produce an error in non-interactive use.
I really do not understand how do you shift the blame to the devs, if you use this feature, the burden is on you, just do not use it. Also, undefined behavior was always (in my C/C++ knowledge) exactly this undefined behaviour, the compiler is free to do whatever, but in this case, instead, it guarantees that you will receive an warning.
It suggests that a convenient REPL experience is more important than a correct program.
Again, you can never use this feature. I use it. It is a probability thing. I redefine the const
, re-run the code, and if what I have changed does exactly what I expected, then I know no problem happened, and carry on; otherwise, I reload the REPL. It is not even that I do not care about reliability, it is that there is a test suit that will check things for me anyway at the end of the development cycle; I do not understand what I would gain from being slowed down in a previous step of the development cycle.
Sorry for the confusion! With this:
How could the devs be so careless about correctness?
I was trying to reproduce an inner dialogue to describe how I felt, almost emotionally you could say, years ago on that day when I first stumbled on this feature. Itās not meant as an actual criticism of the developers. I have slightly edited my post to clarify it hopefullly.
if you use this feature, the burden is on you, just do not use it
In general this doesnāt scale well to a team and even worse to an ecosystem that I donāt control. But in this particular case that might actually workā¦ Assuming Iām also warned when const
is āmisusedā in one of my dependencies, and assuming the warning doesnāt get lost in precompilation messages or whatever.
Anyway the point of my post was to answer @StefanKarpinski who was trying to understand my point of view. I actually worked a long time on that post trying to analyze and articulate my position and still apparently just produced more misunderstanding. Sorry.
Is there any package out there that actually redefines a constant? I agree that would be bad, but I have never seen that in the wild, which makes this a purely theoretical concern to me.
Agreed, I think itās quite unlikely. If it ever happens I guess it will be found quickly by users that see the warning. So it seems like one of those case where the āif you donāt like it, donāt do itā idea can work.
Thank you for taking the time to articulate it. I think the very high order reassurance that I can offer is this: you only need to worry if code prints a warning. No warning, no worry. If there are any warnings when your code runs, read them and take them seriously.