Skip to content
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

Unexpected allocation on ternary operation #56893

Open
theogf opened this issue Dec 23, 2024 · 3 comments
Open

Unexpected allocation on ternary operation #56893

theogf opened this issue Dec 23, 2024 · 3 comments
Labels
performance Must go faster

Comments

@theogf
Copy link

theogf commented Dec 23, 2024

I was surprised to see that the ternary operation creates an allocation in this example. Should this be considered a bug?

struct A
  val::Bool
  unrelated::Base.RefValue{Nothing}
end

f(a) = a.val ? a : nothing
g(a) = a

a = A(true, Ref(nothing))
@btime f($a);
# 6.563 ns (1 allocation: 32 bytes)
@btime g($a);
#  2.461 ns (0 allocations: 0 bytes)

The behaviour has at least these three requirements:

  • A contains a non-isbitstype member type
  • f has unstable output type
  • f has one branch returning an A instance

If any of them is not satisfied, no allocation happens.

@bvdmitri
Copy link
Contributor

f has unstable output type

Given you description it makes sense that it allocates the storage for the output?

@nsajko nsajko added the performance Must go faster label Dec 23, 2024
@theogf
Copy link
Author

theogf commented Dec 24, 2024

Given you description it makes sense that it allocates the storage for the output?

Right but the type instability is still handled without allocations if e.g. A is defined as

struct B
  val::Bool
  unrelated::Int
end
 b = B(true, 2)

@btime f($b);
# 2.691 ns (0 allocations: 0 bytes)
@btime g($b);
# 2.065 ns (0 allocations: 0 bytes)

It is confusing to not know what will allocate or not

@bvdmitri
Copy link
Contributor

Hm, I see, that's indeed confusing, just to add to the collection:

julia> mutable struct C
           val::Bool
           unrelated::Int
       end

julia> c = C(true, 2)
C(true, 2)

julia> @btime f($c)
  1.666 ns (0 allocations: 0 bytes)
C(true, 2)

julia> @btime g($c)
  1.666 ns (0 allocations: 0 bytes)
C(true, 2)

julia> mutable struct D
         val::Bool
         unrelated::Base.RefValue{Nothing}
       end

julia> d = D(true, Ref(nothing))
D(true, Base.RefValue{Nothing}(nothing))

julia> @btime f($d)
  1.666 ns (0 allocations: 0 bytes)
D(true, Base.RefValue{Nothing}(nothing))

julia> @btime g($d)
  1.666 ns (0 allocations: 0 bytes)
D(true, Base.RefValue{Nothing}(nothing))

Interestingly enough making the struct mutable also eliminates the intermediate allocation. D also produces nice and clean @code_llvm output while the @code_llvm for A is overly complex

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
performance Must go faster
Projects
None yet
Development

No branches or pull requests

3 participants