Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[SR-4548] Swift needs a fixed-size array #47125

Open
swift-ci opened this issue Apr 9, 2017 · 1 comment
Open

[SR-4548] Swift needs a fixed-size array #47125

swift-ci opened this issue Apr 9, 2017 · 1 comment
Labels
compiler The Swift compiler in itself feature A feature request or implementation generics Feature: generic declarations and types standard library Area: Standard library umbrella swift evolution proposal needed Flag → feature: A feature that warrants a Swift evolution proposal

Comments

@swift-ci
Copy link
Collaborator

swift-ci commented Apr 9, 2017

Previous ID SR-4548
Radar None
Original Reporter smartgo (JIRA User)
Type New Feature
Additional Detail from JIRA
Votes 9
Component/s Compiler
Labels New Feature, LanguageFeatureRequest
Assignee None
Priority Medium

md5: a8d7bd10a0fb7f98dd6ea758e9bd4b0f

relates to:

  • SR-5048 Tuples of homogenous contents should adopt a common protocol

Issue Description:

Swift needs a 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 C++. After several weeks of optimizing, profiling, and digging through disassembly, I’ve gotten to within a factor of 2.

The main issues I had to overcome:

  • avoid heap allocation

  • avoid reference counting

  • avoid indirection

  • avoid initialization checks

For my application, these issues would all be addressed by having a fixed-size array data structure in Swift. 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.

(Arrays in Swift are implemented as a header pointing to an object on the heap. Thus every array involves a heap allocation, and accessing an element in the array requires an extra indirection. Same for ContiguousArray.)

Workaround: Import C array

You can define a fixed-size array in C, then import that to Swift. For example, define the following in a C header file:

const int NumWords = 6;
struct Bitset {
uint64_t bits[NumWords];
};

Then in Swift:

struct PointSet {
var bits = Bitset()
}

The problem is that Bitset is imported into Swift as a tuple (Int, Int, Int, Int, Int, Int), and there is no direct way to use an index to access these members. You can hack it by going through UnsafeRawPointer:

struct FixedSizeArray<T> {

var array = SomeArrayImportedFromC()
let count = ...

private func setElement(_ pointer: UnsafeMutableRawPointer, at index: Int, to value: T) {
let a = pointer.bindMemory(to: T.self, capacity: count)
a[index] = value
}

private func getElement(_ pointer: UnsafeRawPointer, at index: Int) -> T {
let a = pointer.bindMemory(to: T.self, capacity: count)
return a[index]
}

subscript(_ index: Int) -> T {
// This 'mutating' is necessary due to bug SR-4542.
mutating get { return getElement(&array, at: index) }
set { setElement(&array, at: index, to: newValue) }
}
}

This is really ugly, and the SR-4542 bug that forces the getter to be mutable impacts client code. It does, however, address the performance issue.

Array initialization

I have some arrays that are statically initialized, e.g. the following one to map a set of directions into a sequence of offsets, used for efficient iteration through neighbor points (goN, goW, goE, goS are constants):

let nbOffsetForSet: [Int8] = [
/* 0*/ 0, 0, 0, 0,
/* 1*/ goN, 0, 0, 0,
/* 2*/ goW, 0, 0, 0,
/* 3*/ goN, goW, 0, 0,
/* 4*/ goE, 0, 0, 0,
/* 5*/ goN, goE, 0, 0,
/* 6*/ goW, goE, 0, 0,
/* 7*/ goN, goW, goE, 0,
/* 8*/ goS, 0, 0, 0,
/* 9*/ goN, goS, 0, 0,
/10/ goW, goS, 0, 0,
/11/ goN, goW, goS, 0,
/12/ goE, goS, 0, 0,
/13/ goN, goE, goS, 0,
/14/ goW, goE, goS, 0,
/15/ goN, goW, goE, goS, 0, 0, 0, 0 ]

When declaring this constant array, Swift will initialize it once. However, before accessing it, the code needs to check whether it’s initialized. I get a significant performance boost by declaring the same array in C and using the following function:

func getNbOffsetForSet(_ index: Int) -> Int8 {

func getElement(_ pointer: UnsafeRawPointer, at index: Int) -> Int8 {
let a = pointer.bindMemory(to: Int8.self, capacity: 17*4)
return a[index]
}
return getElement(&nbOffsetForSetImportedFromC, at: index)
}

This kind of hack should just not be necessary to make Swift code perform well.

Proposal

Adding support for a fixed-size array in Swift would make it possible to write performant code without resorting to C and ugly hacks.

var a = FixedArray(repeating: 0, count: 10)

There are many possibilities for how to include this in Swift, e.g. a separate FixedArray class that behaves mostly like Array, except that the compiler knows that count and capacity are constant. But whichever way it’s done, I feel like this is the most important feature currently missing from Swift.

@swift-ci
Copy link
Collaborator Author

Comment by Rejean Lamy (JIRA)

This is becoming more and more important especially since a new limitation appears with Xcode 11 / Swift 5, we can not access a C array of size over 4096!

With Xcode 10 this was possible, but no longer with Xcode 11 (see SR-11763).

The reason seems because large tuple are limiting compiler performance.

So the Swift compiler team decided to put a limit at 4096 on the size of a C array that can be exposed to Swift.

An array of size 4097 is too expensive but 4096 is ok !

My guess is they must decide of a limit because there is no Swift fixed-size array and they have no choice to rely on tuple to expose a C array.

This is very bad. Now for each C array larger than 4096 we must write glue code in C to access such array from Swift.

Yes, Swift needs a fixed-size array !

@swift-ci swift-ci transferred this issue from apple/swift-issues Apr 25, 2022
@AnthonyLatsis AnthonyLatsis added standard library Area: Standard library umbrella swift evolution proposal needed Flag → feature: A feature that warrants a Swift evolution proposal generics Feature: generic declarations and types and removed new feature labels Nov 11, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
compiler The Swift compiler in itself feature A feature request or implementation generics Feature: generic declarations and types standard library Area: Standard library umbrella swift evolution proposal needed Flag → feature: A feature that warrants a Swift evolution proposal
Projects
None yet
Development

No branches or pull requests

2 participants