-
Notifications
You must be signed in to change notification settings - Fork 149
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
Generic conversion of AbstractArrays? #31
Comments
Well, that depends. At the moment, I'm doing my best to make sure the package users always get fast, type-safe code. When Arguments for this definition include (a) convenience at the REPL and (b) interop with other statically-sized array packages. (a) might be resolved by a special function like |
I don't think we want to define this, for the reasons of type-stability you describe. In my recent code I'm becoming a fan of dropping helpful hints, like this: convert(::Type{SVector}, x::AbstractVector) = error("convert(SVector, x) is not defined; you must specify the length, e.g., convert(SVector{$(length(x))}, x)") There's some risk that users could find that annoying, because they'll wonder why that isn't done automatically. An alternative would be a link to the docs? |
Hmm, it's context-dependent on whether this is ok for performance or not. If you immediately pass it off to a function it's ok, but in other cases it's probably a bad idea. So on balance, probably just a bad idea to define this generically. Tim's option seems pretty good to me. |
Thanks, all, for the responses. The use case I had in mind was dispatching to a function, so it would not have been horribly inefficient in my case, but I can see how other uses could cause gross inefficiencies. Suppose I have the following function: function sphere_to_cart(sphere::SVector{3})
r, θ, ϕ = sphere
sinθ = sin(θ)
x = r * sinθ * cos(ϕ)
y = r * sinθ * sin(ϕ)
z = r * cos(θ)
SVector(x, y, z)
end I would like to make it easier to call this function generically, but I also have a need to dispatch on the size of incoming What is the recommended approach for writing this function so that it can be called with any Would defining |
You can definitely do this, but I bet you'll be pretty unhappy with the performance---transitioning between the world of inferrable length and noninferrable length is certain to cause performance problems. Personally, I'd write a separate |
@timholy Thanks. I was really hoping to write the code only once. I'm okay with it returning Edit: I guess I'm pushing on this question because writing two versions feels like a losing proposition in terms of code maintenance (and also antithetical to promise of generic programming). |
Answering my own question. I see that the dynamic dispatch is the problem. |
Yes, a big chunk is the time required for Julia to look up which method to call. You can help, considerably, with "manual-dynamic" dispatch (how's that for a confusing term?): if length(v) == 3
return cart_to_sphere(convert(SVector{3}, v)) # julia knows the specialization in advance
elseif length(v) == 6
return cart_to_sphere(convert(SVector{6}, v))
... However, the return value will also not be inferrable, so you basically have to do this in every function that uses the output of this function. |
Yes, I would definitely recommend writing different functions with potentially different return types for With the function sphere_to_cart(sphere::StaticVector)
# Eliminated at compile-time (** in theory **)
size(typeof(sphere)) === (3,) || error("Input must be a 3-vector")
@inbounds (r, θ, ϕ) = (sphere[1], sphere[2], sphere[3]) # much faster
sinθ = sin(θ)
x = r * sinθ * cos(ϕ)
y = r * sinθ * sin(ϕ)
z = r * cos(θ)
SVector(x, y, z)
end
function sphere_to_cart(sphere::AbstractVector)
size(sphere) === (3,) || error("Input must be a 3-vector")
@inbounds (r, θ, ϕ) = (sphere[1], sphere[2], sphere[3]) # much faster
sinθ = sin(θ)
x = r * sinθ * cos(ϕ)
y = r * sinθ * sin(ϕ)
z = r * cos(θ)
# In this case the return type is always length 3
# (but in other cases you will be better off returning `Vector` if length is unknown)
SVector(x, y, z)
end Of course you could factor it better with the second function calling the first. I just realized this isn't a great example, but in the general case the second function might need to return a PS - if you are interested in this particular transformation, please check out CoordinateTransformations.jl which is already integrated with StaticArrays. EDIT - fixed size checking to be faster - this is part of the "in theory" part above, where constant propagation and branch prediction are still a little touch-and-go in Julia |
@andyferris why do we need two variants here? Why doesn't the second variant just work for SVector input as well? |
Ok, Andy has just told me why you might want two versions - it's nothing to do with the semantics of the language, and everything to do with current compiler optimizer limitations. The generic version works perfectly well, it's just slow. In particular the lack of interaction between constant propagation and inlining will currently lead to the So depending on whether you care about that, you might get away with a single version here. |
Right, it's just Julia implementation details at the moment. Also, like I said in my edited post above, this example is actually rather poor for explaining why you might ever want two versions. (There's just very little difference and that one minor change makes very little difference to codegen in this case also - but other cases where the input or output size is unknown beforehand the distinction may be quite important.) |
There's definitely a disconnect between mutable vs immutable We should start a separate issue about this, I think it'd be nice to have a place where we document the differences between mutable and immutable |
@andyferris Thanks for all your help. I've decided that I'm better off supporting StaticArrays as the native data type for my transformations. By the way, I didn't notice any speed difference when adding |
Ahh, well that's embarrassing. In some types/functions I have actually deleted the bounds checking. It's quite annoying - sometimes the compiler will check the bounds of a tupple for you (sometimes not) and sometimes codegen wasn't removing the bounds checks properly (in complex code, but it usually worked fine). In this case I suspect the compiler knows you are indexing a tupple with literals that are inbounds. |
Would it make sense to define:
and perhaps similar for
SMatrix
or other arrays?The text was updated successfully, but these errors were encountered: