Skip to content
{{ message }}

Instantly share code, notes, and snippets.

# jayschwa/ImmutableArrays.jl

Created Mar 27, 2013
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.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

### twadleigh commented Mar 27, 2013

 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.

### jayschwa commented Mar 27, 2013

 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 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 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 commented Mar 27, 2013

 @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.
to join this conversation on GitHub. Already have an account? Sign in to comment