5
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

Swiftで初期値を持つ配列を初期化するときのパフォーマンス比較(`Array(repeating:count:)` or `Array.reserveCapacity(_:)`)

Last updated at Posted at 2020-03-03

概要

最近Swiftを書き始めてふと初期値をもつ配列を初期化するコードの最適解が知りたくなったので、思いついた以下の2+1つの方法について、パフォーマンスを計測して比較してみた

  • Array(repeating:count:)したあと初期値をセットしていく
  • 空の配列を作成後、Array.reserveCapacity(_:)してから初期値を追加していく
  • (一応)空の配列を作成後、初期値を追加していく

tl; dr

  • 最適化オプションをつけないと、Array.reserveCapacity(_:)が速い
  • 最適化オプションをつけると、Array(repeating:count:)が速い

実行環境

環境作成当時、最新のSwift Docker Official Images

# cat /etc/lsb-release
DISTRIB_ID=Ubuntu
DISTRIB_RELEASE=18.04
DISTRIB_CODENAME=bionic
DISTRIB_DESCRIPTION="Ubuntu 18.04.3 LTS"
# swiftc --version
Swift version 5.1.3 (swift-5.1.3-RELEASE)
Target: x86_64-unknown-linux-gnu

ソースコード

BenchmarkInitializeSequence.swift
import Foundation

func repeating(n: Int) -> Double {
  let start = Date()
  var array = [Int](repeating: 0, count: n)
  for i in 0..<n {
    array[i] = i
  }
  return Date().timeIntervalSince(start)
}

func reserveCapacity(n: Int) -> Double {
  let start = Date()
  var array = [Int]()
  array.reserveCapacity(n)
  for i in 0..<n {
    array.append(i)
  }
  return Date().timeIntervalSince(start)
}

func append(n: Int) -> Double {
  let start = Date()
  var array = [Int]()
  for i in 0..<n {
    array.append(i)
  }
  return Date().timeIntervalSince(start)
}

func benchmarkInitializeSequence() {
  let testCases: [(n: Int, count: Int)] = [
    (10,100000),
    (100,100000),
    (1000,100000),
    (10000,10000),
    (100000,10000),
    (1000000,1000),
  ]

  for testCase in testCases {
    print("--- n = \(testCase.n)")

    // repeating
    var time = 0.0
    for _ in 0..<testCase.count {
      time += repeating(n: testCase.n)
    }
    print("repeating: \(time / Double(testCase.count))")

    // reserveCapacity
    time = 0.0
    for _ in 0..<testCase.count {
      time += reserveCapacity(n: testCase.n)
    }
    print("reserveCapacity: \(time / Double(testCase.count))")

    // append
    time = 0.0
    for _ in 0..<testCase.count {
      time += append(n: testCase.n)
    }
    print("append: \(time / Double(testCase.count))")
  }
}

benchmarkInitializeSequence()

計測方法についてはこちらの記事を参考にしました
Swiftで処理時間を計測 - Qiita

結果 その1(コンパイルオプションの指定なし)

# swiftc BenchmarkInitializeSequence.swift
# ./BenchmarkInitializeSequence
--- n = 10
repeating: 5.331254005432129e-06
reserveCapacity: 5.172144174575806e-06
append: 5.743725299835205e-06
--- n = 100
repeating: 6.829563379287719e-06
reserveCapacity: 6.2863850593566896e-06
append: 7.3317515850067134e-06
--- n = 1000
repeating: 1.9573562145233155e-05
reserveCapacity: 1.7683861255645753e-05
append: 1.97959041595459e-05
--- n = 10000
repeating: 0.00014686577320098876
reserveCapacity: 0.00012827222347259521
append: 0.00017528159618377686
--- n = 100000
repeating: 0.0013892885684967042
reserveCapacity: 0.0012642447113990784
append: 0.001911275053024292
--- n = 1000000
repeating: 0.01432170021533966
reserveCapacity: 0.012672142624855042
append: 0.017902109384536742

結果 その2(最適化オプションあり)

# swiftc -O BenchmarkInitializeSequence.swift
# ./BenchmarkInitializeSequence
--- n = 10
repeating: 5.071830749511719e-06
reserveCapacity: 5.0783669948577884e-06
append: 5.610878467559814e-06
--- n = 100
repeating: 5.182200670242309e-06
reserveCapacity: 5.008577108383178e-06
append: 6.255366802215577e-06
--- n = 1000
repeating: 5.936795473098755e-06
reserveCapacity: 6.086642742156982e-06
append: 8.90161633491516e-06
--- n = 10000
repeating: 1.1534333229064942e-05
reserveCapacity: 1.8170416355133056e-05
append: 6.744261980056762e-05
--- n = 100000
repeating: 7.372238636016845e-05
reserveCapacity: 0.00014843966960906982
append: 0.0008369592905044555
--- n = 1000000
repeating: 0.001538015604019165
reserveCapacity: 0.001612482786178589
append: 0.007066493153572082

結論

  • 最適化オプションをつけないと、Array.reserveCapacity(_:)を使うほうがArray(repeating:count:)を使うより速い
  • 最適化オプションをつけると、Array(repeating:count:)を使うほうがArray.reserveCapacity(_:)を使うより速い
  • 動的に長さを伸ばしていく方法(Array.appendのみを使う)は遅い

その他

最適化オプションをつけたときにどういったコードが吐き出されるかまでは確認していないので、どういった最適がされた結果、Array(repeating:count:)のほうが早くなったのかは分かっていません
Swiftについては何もわからんなので、有識者の方、各種コメントありましたらぜひいただければ幸いに思います

5
3
4

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?