Skip to content

Instantly share code, notes, and snippets.

@jayschwa
Created March 27, 2013 00:42
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jayschwa/5250636 to your computer and use it in GitHub Desktop.
Save jayschwa/5250636 to your computer and use it in GitHub Desktop.
ImmutableArrays prototype using metaprogramming
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
@jayschwa
Copy link
Author

In any case, this is a really good starting point, and, with your permission, I'll paste it into my module.

Knock yourself out.

@dronir
Copy link

dronir commented Mar 27, 2013

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?

@dronir
Copy link

dronir commented Mar 27, 2013

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.

@twadleigh
Copy link

@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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment