Sethmr Sethmr - 1 month ago 10x
Swift Question

Assign count variable over calling .count on Array in Swift

I occasionally come to a place where I will not be changing the contents of an array, but I need to know its count several times over a function. Is it more efficient to assign the .count of the array to a variable and use it multiple times, or does the compiler make the efficiency equivalent?


Let's investigate! Is myArray.count equivalent to accessing a stored property or is it a computed property performing some "unnecessary" calculations if called repeatedly for a non-mutated array? (Disregarding compiler cleverness)

/// The number of elements in the array.
public var count: Int {
  return _getCount()

// ... what is function _getCount()?

internal func _getCount() -> Int {
  return _buffer.count

// ... what is property _buffer?
internal var _buffer: _Buffer

// ... what is type _Buffer? (Swift)
internal typealias _Buffer = _ContiguousArrayBuffer<Element>

// ... what is type _ContiguousArrayBuffer?
// --> switch source file
import SwiftShims

/// Class used whose sole instance is used as storage for empty
/// arrays.  The instance is defined in the runtime and statically
/// initialized.  See stdlib/runtime/GlobalObjects.cpp for details.
internal struct _ContiguousArrayBuffer<Element> : _ArrayBufferProtocol {

  // ... conformance to _ArrayBufferProtocol

  /// The number of elements the buffer stores.
  internal var count: Int {
    get {
      return __bufferPointer.header.count
    // ...
  // ...

// ... what is property __bufferPointer?
var __bufferPointer: ManagedBufferPointer<_ArrayBody, Element>

// what is type _ArrayBody?
internal final class _EmptyArrayStorage
  : _ContiguousArrayStorageBase {
  // ...

  var countAndCapacity: _ArrayBody // telling name for a tuple? :)

// Now, this _ArrayBody type is not defined in this source file,
// so lets look at SwiftShims, GlobalObjects.cpp (as mentioned in
// comments above), specifically
// --> switch source file
// compare to _EmptyArrayStorage in ContiguousArrayBuffer.swift above
struct _SwiftEmptyArrayStorage {
  struct HeapObject header;
  struct _SwiftArrayBodyStorage body;

// this would be the type of 'countAndCapacity' (_ArrayBody)!
struct _SwiftArrayBodyStorage {
  __swift_intptr_t count;              // 'countAndCapacity.0'
  __swift_uintptr_t _capacityAndFlags;

// Yay, we found a stored property!

So in the end count is a stored property and is not computed per call, so it should be no reason to explicitly store the arr.count property by yourself.

To wrap up, by the tuple first element access 'countAndCapacity.0' above, I refer to an example buffer given in

/// Contains a buffer object, and provides access to an instance of
/// `Header` ...

/// Example Buffer Class
/// --------------------
///      class MyBuffer<Element> { // non-@objc
///        typealias Manager = ManagedBufferPointer<(Int, String), Element>
///        ...
///        // All properties are *computed* based on members of the Header
///        var count: Int {
///          return Manager(unsafeBufferObject: self).header.0 // dfri: note the tuple access!
///        }
///      }