-
-
Save jayschwa/5250636 to your computer and use it in GitHub Desktop.
module ImmutableArrays | |
export Vector2, Vector3, Vector4 | |
importall Base | |
for n = 2:4 | |
local Typ = symbol(string("Vector", n)) | |
local TypT = Expr(:curly, Typ, :T) | |
local definition = :(immutable $TypT <: AbstractVector{T} end) | |
for i = 1:n | |
element = symbol(string("e", i)) | |
push!(definition.args[3].args, :($element::T)) | |
end | |
eval(definition) | |
@eval function getindex(x::$Typ, i::Integer) | |
element = symbol(string("e", i)) | |
return x.(element) | |
end | |
@eval -(a::$Typ) = $Typ(ntuple($n, x-> -a[x])...) | |
@eval +(a::$Typ, b::$Typ) = $Typ(ntuple($n, x-> a[x]+b[x])...) | |
@eval -(a::$Typ, b::$Typ) = $Typ(ntuple($n, x-> a[x]-b[x])...) | |
@eval .*{T}(a::$TypT, b::Number) = $TypT(ntuple($n, x-> convert(T, a[x]*b))...) | |
@eval .*(a::Number, b::$Typ) = .*(b, a) | |
@eval ./{T}(a::$TypT, b::Number) = $TypT(ntuple($n, x-> convert(T, a[x]/b))...) | |
@eval similar{T}(::$TypT, t::DataType, dims::Dims) = Array(t, dims) | |
@eval size(::$Typ) = ($n,) | |
end | |
end |
In any case, this is a really good starting point, and, with your permission, I'll paste it into my module.
Knock yourself out.
I tested this one a bit and the operations are really inefficient.
Here's a benchmark with the operations as defined in this gist. It takes almost one second to add together two 3-vectors a hundred thousand times. The equivalent loop for two ordinary Julia Vectors takes about 0.02 seconds.
julia> require("src/ImmutableArrays.jl")
julia> using ImmutableArrays
julia> a = Vector3(0.1, 0.2, 5.0) ; b = Vector3(-0.1, 0.8, 15.0) ;
julia> a+b
3-element Float64 Vector3:
0.0
1.0
20.0
julia> @elapsed for i = 1:100000
c = a + b
end
0.884444887
Then I defined a sum function explicitly:
julia> p(a::Vector3, b::Vector3) = Vector3(a.e1 + b.e1, a.e2 + b.e2, a.e3 + b.e3)
# methods for generic function p
p(a::Vector3{T},b::Vector3{T}) at none:1
julia> p(a,b)
3-element Float64 Vector3:
0.0
1.0
20.0
julia> @elapsed for i = 1:100000
c = p(a,b)
end
0.004634279
Performance increase of a factor of 200 over the definition of this gist, and a factor of 5 to Julia Vectors. I suppose it's the call to ntuple
that makes it slow?
Forgot to add, it's probably not about the getindex
. I tried to get rid of it that by replacing the ntuple($n, x-> a[x]+b[x])
by ntuple($n, x-> a.(symbol(string("e", x)))+b.(symbol(string("e", x))))
and it didn't affect the numbers. But I'm not sure if I did that right.
@dronir: Would you mind retrying this exercise with what I put in the repo. Even better, if you could make a benchmark script to go in the test directory to automate what you've done here, that would be awesome.
I've defined the arithmetic in terms of a higher-order function (zipWith) that I guess I'm hoping gets inlined away. If it doesn't we'll brute force the inlining in the automatic code generation.
It looks good. Nowhere near as unreadable as I was afraid it was going to be. The only concern I'd have (and I'm not sure it's valid) is defining the arithmetic in terms of getindex. I'm sure there are better definitions of getindex, but any definition is going to involve switching on the int argument. I'm no expert, but I think the optimal definition of the arithmetic operations involves mapping over the members directly. (I presume you would also agree with me that the arithmetic needs to be defined to be as fast as possible.)
In any case, this is a really good starting point, and, with your permission, I'll paste it into my module.