[swift-evolution] [Pitch] Swift needs fixed-size array
Jens Persson
jens at bitcycle.com
Mon Apr 17 16:07:53 CDT 2017
I've used code like the following example in similar situations and I've
always (after a lot of profiling, trial and error) managed to get the
(ugly) Swift code as fast as the C/C++ code.
struct UnsafeStatic19x19<E> {
var storage: (
E, E, E, E, E, E, E, E, E, E, E, E, E, E, E, E, E, E, E,
E, E, E, E, E, E, E, E, E, E, E, E, E, E, E, E, E, E, E,
E, E, E, E, E, E, E, E, E, E, E, E, E, E, E, E, E, E, E,
E, E, E, E, E, E, E, E, E, E, E, E, E, E, E, E, E, E, E,
E, E, E, E, E, E, E, E, E, E, E, E, E, E, E, E, E, E, E,
E, E, E, E, E, E, E, E, E, E, E, E, E, E, E, E, E, E, E,
E, E, E, E, E, E, E, E, E, E, E, E, E, E, E, E, E, E, E,
E, E, E, E, E, E, E, E, E, E, E, E, E, E, E, E, E, E, E,
E, E, E, E, E, E, E, E, E, E, E, E, E, E, E, E, E, E, E,
E, E, E, E, E, E, E, E, E, E, E, E, E, E, E, E, E, E, E,
E, E, E, E, E, E, E, E, E, E, E, E, E, E, E, E, E, E, E,
E, E, E, E, E, E, E, E, E, E, E, E, E, E, E, E, E, E, E,
E, E, E, E, E, E, E, E, E, E, E, E, E, E, E, E, E, E, E,
E, E, E, E, E, E, E, E, E, E, E, E, E, E, E, E, E, E, E,
E, E, E, E, E, E, E, E, E, E, E, E, E, E, E, E, E, E, E,
E, E, E, E, E, E, E, E, E, E, E, E, E, E, E, E, E, E, E,
E, E, E, E, E, E, E, E, E, E, E, E, E, E, E, E, E, E, E,
E, E, E, E, E, E, E, E, E, E, E, E, E, E, E, E, E, E, E,
E, E, E, E, E, E, E, E, E, E, E, E, E, E, E, E, E, E, E
)
subscript(x: Int, y: Int) -> E {
get {
// No index out of bounds check
var m = self // <-- This workaround will be optimized away.
return withUnsafeBytes(of: &m) {
let byteOffset = MemoryLayout<E>.stride * (x + y*19)
return $0.load(fromByteOffset: byteOffset, as: E.self)
}
}
set {
withUnsafeMutableBytes(of: &self) {
let byteOffset = MemoryLayout<E>.stride * (x + y*19)
$0.storeBytes(of: newValue, toByteOffset: byteOffset, as:
E.self)
}
}
}
}
It isn't pretty but it works (haven't tried this code example though, but
you get the idea).
I wish it was possible to write something like
struct StaticArray<Element, Count> {
...
}
instead (a statically allocated array with type-level Count as well as
Element).
/Jens
On Mon, Apr 17, 2017 at 7:52 PM, Anders Kierulf via swift-evolution <
swift-evolution at swift.org> wrote:
> Swift needs a datatype that contains a fixed number of a given type;
> basically a simple fixed-size array.
>
> Motivation: I’ve been porting code for Monte Carlo Tree Search in my
> Go-playing program from C++ to Swift. Performance is crucial for this code,
> as more simulations lead to better play. After the initial port, the Swift
> code was more than 10x slower than my C++ code. After several weeks of
> optimizing, profiling, and digging through disassembly, I’ve gotten to
> within a factor of 2. Most of that gain came from using the ugly workaround
> of importing fixed-size arrays from C.
>
> My app is designed for a 19x19 (or smaller) Go board, not an arbitrary N x
> N board, so I don’t want to pay the high cost of variable-size data
> structures in the lowest levels of my app. Most apps are not like this, and
> most of my app is not, but this kernel of my app needs to be fast. Heap
> allocations, reference counting, and indirections all slow down the code. I
> just need a fixed size of memory that I can access like an array, and Swift
> doesn’t let me do that.
>
> Workaround: By importing an array from C, I can allocate a blob of memory
> on the stack or include it in a struct. I can then use UnsafeRawPointer to
> access that blob like an array (see details in SR-4548). This is ugly, but
> it works, and it is much faster than using a Swift array. However, I’m
> stymied by SR-4542, which causes mutability to spread like a plague through
> client code.
>
> (SR-4542: Calling a function taking an UnsafeRawPointer forces the
> parameter to be passed as inout, which means the method must be mutating.
> UnsafeMutableRawPointer should require inout, UnsafeRawPointer should not.)
>
> Proposal: UnsafeMutablePointer almost provides what I need. However, it
> can only allocate memory on the heap, or it can take a given blob of memory
> and interpret it as something else. What’s missing is a way to allocate
> typed memory of a certain size on the stack or in a struct. For example,
> something like this, with support for subscripts, limited to value types:
>
> var foo = UnsafeMemory<Int64>(count: 6)
> or
> var bar = FixedSizeArray<UInt32>(repeating: 0, count: 380)
>
> Alternatives:
> (1) C arrays are currently imported as tuples, so extending tuples with
> subscripts and adding a way to create tuples with a specific count of the
> same type could address this need. However, I don’t think this fits well
> with the concept of tuples.
> (2) Changing the Array type to allow a fixed size could be considered in
> the future. ‘count’ and ‘capacity’ would be fixed and only known to the
> compiler, not stored with the data. However, I suspect the consequences
> would be far-reaching, and thus I don’t see this happening soon.
>
> An UnsafeMemory type would be a limited addition that fits in well with
> the existing low-level Pointer module, and fills a gap in the capabilities
> of Swift. The Pointer module helps implement low-level,
> performance-critical code, and not being able to create data on the stack
> is a serious omission. Jumping through C hoops is not a solution.
>
> Anders Kierulf
>
> _______________________________________________
> swift-evolution mailing list
> swift-evolution at swift.org
> https://lists.swift.org/mailman/listinfo/swift-evolution
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.swift.org/pipermail/swift-evolution/attachments/20170417/ee388525/attachment.html>
More information about the swift-evolution
mailing list