-
-
Notifications
You must be signed in to change notification settings - Fork 5.5k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Change isnull field of Nullable into hasvalue #18510
Conversation
2618e99
to
8da3c10
Compare
@@ -3,7 +3,7 @@ | |||
immutable NullException <: Exception | |||
end | |||
|
|||
Nullable{T}(value::T, isnull::Bool=false) = Nullable{T}(value, isnull) | |||
Nullable{T}(value::T, hasvalue::Bool=true) = Nullable{T}(value, hasvalue) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
xref #18511 (comment) cc @kshyatt
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
is the second argument meant to be used?
@tkelman Indeed the two-argument form is mostly useful for tests, where using ===
requires controlling the contents of value
(cf. #16923). Maybe it can also help for performance, instead of writing ifelse(hasvalue, Nullable(value), Nullable())
, I would have to check. I guess @johnmyleswhite can tell.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We make use of this for null-safe operations where we can generate the value
and hasvalue
fields without branching.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
okay, then it should be documented?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes. We should document it in such a way that it's clear that you have more freedom than you usually need and that its use is a performance optimization.
@@ -53,20 +53,20 @@ otherwise, returns `y` if provided, or throws a `NullException` if not. | |||
""" | |||
@inline function get{S,T}(x::Nullable{S}, y::T) | |||
if isbits(S) | |||
ifelse(x.isnull, y, x.value) | |||
ifelse(isnull(x), y, x.value) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we maybe run benchmarks to make sure any !
operations are successfully eliminated? Or perhaps this is nothing compared to the cost of the branch, so even if they aren't removed, it doesn't matter.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not a bad idea in general, but I don't think we have any benchmarks for Nullable
yet. We really need to add some. We could also check the generated code manually in some cases.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've opened a PR to add benchmarks: JuliaCI/BaseBenchmarks.jl#24. Please suggest cases to add measurements for.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I see no cause for concern in the generated code. It has not changed at all with this patch.
julia> @code_llvm get(Nullable{Int}(10), 100)
define i64 @julia_get_64687(%Nullable.7*, i64) #0 {
top:
%2 = getelementptr inbounds %Nullable.7, %Nullable.7* %0, i64 0, i32 0
%3 = load i8, i8* %2, align 1
%4 = and i8 %3, 1
%5 = icmp eq i8 %4, 0
%6 = getelementptr inbounds %Nullable.7, %Nullable.7* %0, i64 0, i32 1
%7 = load i64, i64* %6, align 8
%8 = select i1 %5, i64 %7, i64 %1
ret i64 %8
}
2ba180a
to
fcd065d
Compare
fcd065d
to
672c01c
Compare
The first commit only changes the field but keeps the constructor as it was, while the second also change the constructor. I think we want both for consistency, but separating them helps measuring the breakage it creates. Overall, very little changes are needed, which is promising as regards the broader ecosystem. The two most disruptive cases are:
Luckily, these two cases appear to be relatively rare. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks great. Since generated code for get
has not changed, I suspect there will be no performance regressions (except possibly isnull
, but that's unavoidable).
This is consistent with most other languages and formats.
This method is not currently documented, and the new form is more consistent with the new structure of the type.
672c01c
to
32a08c7
Compare
I've run the benchmarks locally. As expected, they are slower with this PR if we keep the So now it remains to find out whether many packages would be broken by the second commit. I've made a PR against NullableArrays, and it wasn't too painful. I think most packages should work fine. @tkelman Could you please run PkgEval on this PR? Thanks! |
Sure. I messed something up in a rebase of my first attempt, running again now. |
Shouldn't be too surprised by this: CategoricalArrays, CSV, and SQLite are new failures. Git was a timeout, DynamicDiscreteModels is flaky. https://gist.github.com/7d1882b630429019667fece0cc82172f |
Great! These are all packages which were supposed to fail, and whose maintainers are in the loop. Will have a look. Cc: @quinnj |
Noted. Will this be backported? Or just on 0.6? |
No, just on 0.6. |
Great. Merge away. Should be a pretty easy compat. |
Should we merge this then? @johnmyleswhite? @davidagold? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good by me.
@@ -3,7 +3,7 @@ | |||
immutable NullException <: Exception | |||
end | |||
|
|||
Nullable{T}(value::T, isnull::Bool=false) = Nullable{T}(value, isnull) | |||
Nullable{T}(value::T, hasvalue::Bool=true) = Nullable{T}(value, hasvalue) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We make use of this for null-safe operations where we can generate the value
and hasvalue
fields without branching.
This is consistent with most other languages and formats.
Fixes #18507.