While asking about inlining in another post, I decided to add some extensions to SCNQuaternion. Fortunately, GLKQuaternion implements them in the header in C, so I could look at their implementations to copy.
Here’s one for adding quaternions:
GLK_INLINE GLKQuaternion GLKQuaternionAdd(GLKQuaternion quaternionLeft, GLKQuaternion quaternionRight)
{
#if defined(GLK_SSE3_INTRINSICS)
__m128 v = _mm_load_ps(&quaternionLeft.q[0]) + _mm_load_ps(&quaternionRight.q[0]);
return *(GLKQuaternion *)&v;
#else
GLKQuaternion q = {{ quaternionLeft.q[0] + quaternionRight.q[0],
quaternionLeft.q[1] + quaternionRight.q[1],
quaternionLeft.q[2] + quaternionRight.q[2],
quaternionLeft.q[3] + quaternionRight.q[3] }};
return q;
#endif
}
I was ready to try to massage the pointers to make use of SSE, but wanted to also play nice on Apple silicon, and remembered simd exists. SCNQuaternion is a SCNVector4 and there's already support for simd_float4 with that, so I gave that a try, and compared it to a simple Swift implementation to add the components. To my surprise, at least in the Playground, the simple Swift code seems substantially faster. I can't figure out if Xcode is optimizing Playground code or not (the code is in a separate file in the Sources dir, and the test methods are called from the Playground proper). I wonder if it's just failing to inline some calls, or what. Here's my code:
import SceneKit
import Foundation
import simd
public
func
simdAdd(_ inLHS: SCNQuaternion, _ inRHS: SCNQuaternion)
-> SCNQuaternion
{
let l = simd_float4(inLHS)
let r = simd_float4(inRHS)
let s = l + r
return SCNQuaternion(s)
}
public
func
swiftAdd(_ inLHS: SCNQuaternion, _ inRHS: SCNQuaternion)
-> SCNQuaternion
{
return SCNQuaternion(inLHS.x + inRHS.x, inLHS.y + inRHS.y, inLHS.z + inRHS.z, inLHS.w + inRHS.w)
}
public
func
testSIMD()
{
let a = SCNQuaternion(1, 2, 3, 1)
var c = SCNQuaternion()
let start:Double = CFAbsoluteTimeGetCurrent()
for _ in 0 ..< 10000
{
c = simdAdd(c, a)
}
let end: Double = CFAbsoluteTimeGetCurrent()
print("SIMD took: \(end - start) s")
}
public
func
testSwift()
{
let a = SCNQuaternion(1, 2, 3, 1)
var c = SCNQuaternion()
let start:Double = CFAbsoluteTimeGetCurrent()
for _ in 0 ..< 10000
{
c = swiftAdd(c, a)
}
let end: Double = CFAbsoluteTimeGetCurrent()
print("Swift took: \(end - start) s")
}
Running the playground produces this output:
SIMD took: 0.005373954772949219 s
Swift took: 0.0032699108123779297 s
I can't get Xcode to show me the assembly to see what it's doing. But I'm surprised at the result. Any ideas?