Skip to content

Instantly share code, notes, and snippets.

@tayloraswift
Created June 13, 2018 04:56
Show Gist options
  • Save tayloraswift/54accbf3265cb10cfb52b668173906f1 to your computer and use it in GitHub Desktop.
Save tayloraswift/54accbf3265cb10cfb52b668173906f1 to your computer and use it in GitHub Desktop.
protocol Convolvable
{
static
func convolve(_ a:Self, _ b:Self) -> Self
static
func generate() -> Self
}
extension Int:Convolvable
{
static
func convolve(_ a:Int, _ b:Int) -> Int
{
return a &+ b
}
static
func generate() -> Int
{
return random(in: 0 ..< max)
}
}
struct TestElement:Convolvable
{
let a:Int,
b:Int,
c:Float,
d:Float,
e:UnsafeMutablePointer<Double>,
f:Bool,
g:Bool
static
func convolve(_ a:TestElement, _ b:TestElement) -> TestElement
{
return TestElement(a: a.a &+ b.a, b: a.b &+ b.b, c: a.d * b.c, d: a.c * b.d,
e: a.e + b.a, f: a.f != b.f, g: a.f && b.f)
}
static
func generate() -> TestElement
{
return TestElement(a: Int.random(in: 0 ..< .max), b: Int.random(in: 0 ..< .max),
c: Float.random(in: 0 ..< 1),
d: Float.random(in: 0 ..< 1),
e: UnsafeMutablePointer<Double>(bitPattern: Int.random(in: 1 ..< .max))!,
f: Bool.random(),
g: Bool.random())
}
}
struct Array4<Element>:RandomAccessCollection, MutableCollection
{
var storage:(Element, Element, Element, Element),
_count:UInt8
static
var capacity:Int
{
return 4
}
var count:Int
{
return Int(self._count)
}
internal var startIndex:Int
{
return 0
}
internal var endIndex:Int
{
return self.count
}
@inline(__always)
func index(after i:Int) -> Int
{
return i + 1
}
@inline(__always)
func index(before i:Int) -> Int
{
return i - 1
}
subscript(i:Int) -> Element
{
get
{
guard 0 ..< self.count ~= i
else
{
fatalError("index out of range")
}
var copy:(Element, Element, Element, Element) = self.storage
return withUnsafeBytes(of: &copy)
{
(pointer:UnsafeRawBufferPointer) -> Element in
return pointer.load(fromByteOffset: MemoryLayout<Element>.stride * i,
as: Element.self)
}
}
set(v)
{
guard 0 ..< self.count ~= i
else
{
fatalError("index out of range")
}
self.withUnsafeMutableBufferPointer
{
(buffer:UnsafeMutableBufferPointer<Element>) in
buffer[i] = v
}
}
}
mutating
func withUnsafeMutableBufferPointer<Result>(
_ body: (UnsafeMutableBufferPointer<Element>) throws -> Result) rethrows
-> Result
{
let count = self.count // for exclusive access
return try withUnsafeMutableBytes(of: &self.storage)
{
(pointer:UnsafeMutableRawBufferPointer) -> Result in
let buffer:UnsafeMutableBufferPointer<Element> =
.init(start: pointer.baseAddress._unsafelyUnwrappedUnchecked
.assumingMemoryBound(to: Element.self),
count: count)
return try body(buffer)
}
}
mutating
func withUnsafeBufferPointer<Result>(
_ body: (UnsafeBufferPointer<Element>) throws -> Result) rethrows
-> Result
{
let count = self.count // for exclusive access
return try withUnsafeBytes(of: &self.storage)
{
(pointer:UnsafeRawBufferPointer) -> Result in
let buffer:UnsafeBufferPointer<Element> =
.init(start: pointer.baseAddress._unsafelyUnwrappedUnchecked
.assumingMemoryBound(to: Element.self),
count: count)
return try body(buffer)
}
}
}
struct SwitchArray4<Element>:RandomAccessCollection, MutableCollection
{
var storage:(Element, Element, Element, Element),
_count:UInt8
static
var capacity:Int
{
return 4
}
var count:Int
{
return Int(self._count)
}
internal var startIndex:Int
{
return 0
}
internal var endIndex:Int
{
return self.count
}
@inline(__always)
func index(after i:Int) -> Int
{
return i + 1
}
@inline(__always)
func index(before i:Int) -> Int
{
return i - 1
}
subscript(i:Int) -> Element
{
get
{
switch i
{
case 0:
return self.storage.0
case 1:
return self.storage.1
case 2:
return self.storage.2
case 3:
return self.storage.3
default:
fatalError("index out of range")
}
}
set(v)
{
switch i
{
case 0:
self.storage.0 = v
case 1:
self.storage.1 = v
case 2:
self.storage.2 = v
case 3:
self.storage.3 = v
default:
fatalError("index out of range")
}
}
}
}
import func Glibc.clock
func benchmark<C>(_ arrays:[C]) where C:RandomAccessCollection, C:MutableCollection,
C.Element:Convolvable, C.Index == Int
{
let start:Int = clock()
let combination:C.Element = arrays.reduce(arrays[0][0])
{
(residue:C.Element, array:C) -> C.Element in
return (0 ..< 4).map({ _ in Int.random(in: 0 ..< 4) }).reduce(residue)
{
(residue:C.Element, next:Int) -> C.Element in
C.Element.convolve(residue, array[next])
}
}
let runtime:Int = clock() - start
print(runtime, combination)
}
func generateSamples<Element>(_ count:Int) -> ([Array4<Element>], [SwitchArray4<Element>], [[Element]])
where Element:Convolvable
{
// make a bunch of fixed arrays
let a1:[Array4<Element>] = (0 ..< count).map
{
_ in
return Array4(storage:
(Element.generate(), Element.generate(), Element.generate(), Element.generate()),
_count: 4)
}
let a2:[SwitchArray4<Element>] = (0 ..< count).map
{
_ in
return SwitchArray4(storage:
(Element.generate(), Element.generate(), Element.generate(), Element.generate()),
_count: 4)
}
// normal arrays
let a3:[[Element]] = (0 ..< count).map
{
_ in
return [Element.generate(), Element.generate(), Element.generate(), Element.generate()]
}
return (a1, a2, a3)
}
let (a1, a2, a3):([Array4<Int>], [SwitchArray4<Int>], [[Int]]) =
generateSamples(100000)
benchmark(a1)
benchmark(a2)
benchmark(a3)
let (b1, b2, b3):([Array4<TestElement>], [SwitchArray4<TestElement>], [[TestElement]]) =
generateSamples(100000)
benchmark(b1)
benchmark(b2)
benchmark(b3)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment