[swift-users] Design and performance of Vector2/3/4 and Matrix

David Turnbull dturnbull at gmail.com
Fri Dec 18 14:07:57 CST 2015


I'm working on a math library for OpenGL. At the core of this are, of
course, scalar types. The scalars are grouped into vectors of length 2, 3,
and 4. The vectors are used to build matrices of all variations from 2x2 to
4x4.

In order to be performant, scalars, vectors, and matrices must all be
values types aka structs. This way, for example, an Array<Vector3<Float>>
can be passed directly to OpenGL without any copying. In my testing so
far, Swift does this quite well.

Ideally, I'd do something like this:

public struct Vector2<T:ScalarType> : Array<T, 2> {

    public var x:T { get {return self[0]} set {self[0] = newValue} }

    public var y:T { get {return self[1]} set {self[1] = newValue} }

}

But there's so much wrong with that. You can't use inheritance with
structs. Array isn't really a struct; the docs say it is but really it's a
reference to a special copy-on-write value type. Array can't be a fixed
size. You can't use literals with generic placeholders. Ok, fine, I accept
this isn't C++, let's move on to something Swifty.

public struct Vector2<T:ScalarType> {

    public var x:T, y:T


    public var r:T { get {return x} set {x = newValue} }

    public var g:T { get {return y} set {y = newValue} }


    public var s:T { get {return x} set {x = newValue} }

    public var t:T { get {return y} set {y = newValue} }


    public subscript(i: Int) -> T {

        get {

            switch(i) {

            case 0: return x

            case 1: return y

            default: fatalError()

            }

        }

        set {

            switch(i) {

            case 0: x = newValue

            case 1: y = newValue

            default: fatalError()

            }

        }

    }

}

Functionally, this works fine. The x and y properties are the struct data.
I can access vectors with subscripts, both coordinate properties, and
color properties  It's exactly what someone using an OpenGL vector type
would expect. You can make these into arrays, use them in other structs
which are made into arrays, and pass them to OpenGL just fine.

But I hit some performance issues. Let's use myvec.x as a baseline. This is
always inlined and as fast as C.

You might expect myvec.r to have the same performance. It does in other
languages but not Swift. The performance penalty is 20X. Yes, twenty times
slower than myvec.x. The performance hit comes entirely from dynamic
dispatch. 10X because it's not inlined, and another 10X because Vector2 is
a template.

I can get rid of 10X of that by writing my own preprocessor for the
template. There's only four scalar types that are valid for OpenGL so this
really isn't that hard. But it's not a complete solution and preprocessing
core languages features only to gain performance is an indication the
compiler isn't doing optimization as well as it could.

Subscript access is the same 20X slower for the same reasons. The switch
disappears into the noise. But I'm still tempted to use a precondition and
cast to an UnsafePointer.

I'm aware you can mark a class final and a method private to enable
inlining. Except this isn't a class and making the API private, well, it's
not an API then. Forcing @inline(__always) doesn't seem to do anything.

Perhaps I could just not make this a module and leave everything internal.
Supposedly it'd be inlined when whole module optimization is enabled.
Except that doesn't happen.

How can I get these property aliases to be inlined? Is it possible today?
Will it be possible in the future? Is there a different pattern for vectors
that's better suited to the task?

-david (https://github.com/AE9RB/SwiftGL)
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.swift.org/pipermail/swift-users/attachments/20151218/f0a363e2/attachment.html>


More information about the swift-users mailing list