Swift needs fixed-size array


(Anders Kierulf) #1

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


(David Sweeris) #2

Up until 30 seconds ago, I'd thought there was a way to initialize an array with a pre-existing buffer. Apparently not, though.

There’s currently no way to restrict a generic parameter to be a value type. I suspect this is at least partly because `struct` doesn’t make any promises about whether its properties have value or reference semantics (but unless I’m forgetting something, that’s just a guess on my part). The topic of an exclusively value-semantics type, “simplestruct” for lack of a better term, has come up at least once. IIRC it was deemed to not provide enough of a benefit to justify the added language complexity (although I have a vague memory that it was only brought in connection with another topic, as it is here, so maybe if it was its own proposal the results would been different). Personally, I don’t think it adds all that much complexity (from the developer's PoV… dunno about the compiler), and barring some other reason would be in favor of it. That’s just one opinion, though (and one of someone who’s not all that experienced, as well).

Both of your alternatives have been brought up before. My recollection is we tentatively* decided that it’d be better to add fixed-size arrays and change how C arrays are imported, but that conversation was a *long* time ago, before we were as worried about backwards compatibility. Neither alternative has come to a formal proposal, though, because they were deemed out-of-scope** before things got that far. I don’t think either has come up since phase 2 started, though... Maybe it’d be worth revisiting the ideas?

- Dave Sweeris

* because of **

···

On Apr 17, 2017, at 10:52 AM, Anders Kierulf via swift-evolution <swift-evolution@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.


(Karl) #3

What happens if you use alloca?

···

On 17 Apr 2017, at 19:52, Anders Kierulf via swift-evolution <swift-evolution@swift.org> wrote:

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:


(Jens Persson) #4

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@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@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Anders Kierulf) #5

Doesn’t look like using alloca is recommended in Swift: https://bugs.swift.org/browse/SR-323

Also, while it might be used as a hack for stack allocations, it wouldn’t allow inclusion into structs.

Anders

···

On Apr 17, 2017, at 12:37 PM, Karl Wagner <razielim@gmail.com> wrote:

On 17 Apr 2017, at 19:52, Anders Kierulf via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

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:

What happens if you use alloca?


(Michael Ilseman) #6

This is the best approach that I’m aware of. It does bake in an ABI assumption that the tuple layout will be contiguous and strided/addressable. Monitor https://bugs.swift.org/browse/SR-3726 for changes here. Note that you can also a little more “pure” in a sense if you construct an UnsafeBufferPointer from your UnsafeRawPointer and operate on that. Currently, the compiler does not always elide the copy in the getter, see https://bugs.swift.org/browse/SR-4581

···

On Apr 17, 2017, at 2:07 PM, Jens Persson via swift-evolution <swift-evolution@swift.org> wrote:

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@swift.org <mailto:swift-evolution@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@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Richard Wei) #7

Huge +1. We definitely need fixed-size arrays. Just in addition to elegantly importing C arrays, this will enable static matrix/tensor shapes for shape-safe compute operations like GEMM. Moreover I think we can learn from Rust about their recent discussion on constant generics.

-Richard

···

On Apr 17, 2017, at 16:07, Jens Persson via swift-evolution <swift-evolution@swift.org> wrote:

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@swift.org <mailto:swift-evolution@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@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Karl) #8

Huh. That’s weird. I figured in your case, with an integer literal, there shouldn’t really be any difference between alloca(19*19) and a stack-allocated, fixed-size array like char[19 * 19].

alloca is usually a compiler intrinsic (I suppose that’s why the linker isn’t going to find it). It’s definitely a SIL intrinsic. Maybe those two just aren’t wired up correctly to support user-code calling “alloca”.

Could certainly be supported - we should probably support alloca directly (as we do for malloc), and we could provide a function on UMP which does the same thing (allocateNonEscaping?)

- Karl

···

On 17 Apr 2017, at 21:18, Anders Kierulf <anders@smartgo.com> wrote:

On Apr 17, 2017, at 12:37 PM, Karl Wagner <razielim@gmail.com <mailto:razielim@gmail.com>> wrote:

On 17 Apr 2017, at 19:52, Anders Kierulf via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

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:

What happens if you use alloca?

Doesn’t look like using alloca is recommended in Swift: https://bugs.swift.org/browse/SR-323

Also, while it might be used as a hack for stack allocations, it wouldn’t allow inclusion into structs.

Anders


(Karl) #9

Will the compiler ever optimise an UBP allocation on to the stack? For example, I’ve written plenty of code such as:

let ptr = UnsafeRawBufferPointer.allocate(count: 128)
defer { ptr.deallocate(count: 128) }

...when interfacing with C APIs. Which is basically the definition of a stack allocation. That would perhaps be the “swiftier” way to do it; to let the compiler determine how to allocate the memory based on what it knows about its expected lifetime.

···

On 17 Apr 2017, at 23:18, Michael Ilseman via swift-evolution <swift-evolution@swift.org> wrote:

This is the best approach that I’m aware of. It does bake in an ABI assumption that the tuple layout will be contiguous and strided/addressable. Monitor https://bugs.swift.org/browse/SR-3726 for changes here. Note that you can also a little more “pure” in a sense if you construct an UnsafeBufferPointer from your UnsafeRawPointer and operate on that. Currently, the compiler does not always elide the copy in the getter, see https://bugs.swift.org/browse/SR-4581


(Michael Ilseman) #10

My understanding is that there is no mechanism yet to guarantee stack allocation for anything.

···

On Apr 17, 2017, at 3:29 PM, Karl Wagner <razielim@gmail.com> wrote:

On 17 Apr 2017, at 23:18, Michael Ilseman via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

This is the best approach that I’m aware of. It does bake in an ABI assumption that the tuple layout will be contiguous and strided/addressable. Monitor https://bugs.swift.org/browse/SR-3726 for changes here. Note that you can also a little more “pure” in a sense if you construct an UnsafeBufferPointer from your UnsafeRawPointer and operate on that. Currently, the compiler does not always elide the copy in the getter, see https://bugs.swift.org/browse/SR-4581

Will the compiler ever optimise an UBP allocation on to the stack? For example, I’ve written plenty of code such as:

let ptr = UnsafeRawBufferPointer.allocate(count: 128)
defer { ptr.deallocate(count: 128) }

...when interfacing with C APIs. Which is basically the definition of a stack allocation. That would perhaps be the “swiftier” way to do it; to let the compiler determine how to allocate the memory based on what it knows about its expected lifetime.


(Karl) #11

I’ll answer that: no, it won’t.

echo "let ptr = UnsafeMutableRawPointer.allocate(bytes: 128, alignedTo: 1); defer { ptr.deallocate(bytes: 128, alignedTo: 1) }" | swiftc -O -emit-assembly -

  .section __TEXT,__text,regular,pure_instructions
  .macosx_version_min 10, 9
  .globl _main
  .p2align 4, 0x90
_main:
  pushq %rbp
  movq %rsp, %rbp
  movl $128, %edi
  xorl %esi, %esi
  callq _swift_rt_swift_slowAlloc ; a.k.a. ‘malloc'
  movq %rax, __Tv4main3ptrSv(%rip)
  movl $128, %esi
  xorl %edx, %edx
  movq %rax, %rdi
  callq _swift_rt_swift_slowDealloc ; a.k.a ‘free'
  xorl %eax, %eax
  popq %rbp
  retq

Would be cool to do one day, though.

- Karl

···

On 18 Apr 2017, at 00:29, Karl Wagner <karl.swift@springsup.com> wrote:

On 17 Apr 2017, at 23:18, Michael Ilseman via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

This is the best approach that I’m aware of. It does bake in an ABI assumption that the tuple layout will be contiguous and strided/addressable. Monitor https://bugs.swift.org/browse/SR-3726 for changes here. Note that you can also a little more “pure” in a sense if you construct an UnsafeBufferPointer from your UnsafeRawPointer and operate on that. Currently, the compiler does not always elide the copy in the getter, see https://bugs.swift.org/browse/SR-4581

Will the compiler ever optimise an UBP allocation on to the stack? For example, I’ve written plenty of code such as:

let ptr = UnsafeRawBufferPointer.allocate(count: 128)
defer { ptr.deallocate(count: 128) }

...when interfacing with C APIs. Which is basically the definition of a stack allocation. That would perhaps be the “swiftier” way to do it; to let the compiler determine how to allocate the memory based on what it knows about its expected lifetime.


(Rick M) #12

Has anything come of this? Associated JIRA tickets haven't had any activity in over a year:

https://bugs.swift.org/browse/SR-4542
https://bugs.swift.org/browse/SR-4649