SwiftのUnsafeMutableBufferは[]演算子よりも速い

UnsafeMutableBufferを使った記事を書いていて、ふと疑問に思った事があります。

UnsafeMutableBufferを使ったコードと、[]演算子を使ったコードはパフォーマンスがどの程度変わるのか?です。

この記事ではUnsafeMutableBuffer[]演算子のパフォーマンスを比較しました。

どのように比較するか

シンプルにUInt8の配列を確保し、全要素に順次アクセスで値を設定するという処理を、UnsafeMutableBufferを使った実装と[]演算子を使った実装で時間を計測します。

確保する容量を変更して、どの程度の容量でどの程度、時間が変わるのかを観測します。

テストコード

テストコードは次のようなコードです。

import Foundation

func fillSequentialNumber(buffer: UnsafeMutableBufferPointer<UInt8>, length: Int) {
    var cur = buffer.startIndex
    let end = buffer.startIndex.advanced(by: length)
    
    while cur < end {
        buffer[cur] = UInt8((cur - buffer.startIndex) % 255)
        cur = cur.advanced(by: 1)
    }
}

func fillSequalNumber(array: inout [UInt8]) {
    for i in 0 ..< array.count {
        array[i] = UInt8(i % 255)
    }
}

func measureTime(proc: ()->()) -> TimeInterval {
    let startDate = Date()
    proc()
    let time = Date().timeIntervalSince(startDate)
    return time
}

func sequentialWriteTest(count: Int, label: String) {
    var buf = [UInt8](repeating: 0, count: count)
    
    let unsafeTime = measureTime {
        buf.withUnsafeMutableBufferPointer { (ptr) -> Void in
            fillSequentialNumber(buffer: ptr, length: count)
        }
    }
    
    let arrayTime = measureTime {
        fillSequalNumber(array: &buf)
    }
    
    print("\(label)")
    print("  unsafe: \(unsafeTime) s")
    print("  array:  \(arrayTime) s")
}


sequentialWriteTest(count: 1024, label: "1KB")
sequentialWriteTest(count: 1024 * 1024, label: "1MB")
sequentialWriteTest(count: 1024 * 1024 * 10, label: "10MB")
sequentialWriteTest(count: 1024 * 1024 * 100, label: "100MB")

実行結果

実行環境

次のような動作環境で実行しました。

  • Machine : MacBook Pro 2020 M1
  • OS : macOS Big Sur 11.2.3

出力内容

コンソールに次のように出力されました。

1KB
  unsafe: 0.00014102458953857422 s
  array:  0.0006769895553588867 s
1MB
  unsafe: 0.08228003978729248 s
  array:  0.4330480098724365 s
10MB
  unsafe: 0.8178319931030273 s
  array:  4.373885989189148 s
100MB
  unsafe: 8.186002969741821 s
  array:  43.434290051460266 s

考察

1KBのときは、差はあれど、0.1msと0.6msなので無視できるレベルです。

1MBになると、82msと433msなので、ユーザー体験としては差を感じるレベルになります。

それ以上の容量になると、もはやプログレスバーインジケーターが必要になるレベルの差があります。

プログラムの中のちょっとした配列、要素数が数千個くらいまでならどちらでも書いても大丈夫ですが、それを超えてくると明らかにUnsafeMutableBufferを使った方がパフォーマンス的に有利です。この差はユーザー体験でもはっきりと現れてくると思います。

画像処理などをSwiftで実装する場合には、[]演算子を使うのは論外と言えます。可能ならば、Swiftで実装するよりもC/C++で実装してSwiftから呼び出すラッパーをObjective-Cでクラス化するか、ラッパーをC関数で実装して、Swiftでラッパークラスやユーティリティクラスを作るのが現実的だと思います。

投稿者プロフィール

林 晃
林 晃macOS/iOSアプリ/SDK/ミドルウェア開発が専門の開発者
アールケー開発代表。macOS/iOSアプリ/SDK/ミドルウェア開発が専門の開発者。ObjC/Swift/C++使い。豊富な開発実務経験を基に、教育コンテンツ開発、技術書執筆、技術指導、技術セミナー講師、企業内研修講師、行政・自治体職員研修講師も行います。

基礎から学ぶMetal


「基礎から学ぶMetal」を執筆しました。本書はMetalを使ってGPUプログラミングを行うための最初のステップを解説するMetalの解説書です。

私が初めてGPUプログラミングを行ったとき、どこから手をつけて、学んでいけば良いのか分からず呆然としました。もし、あのとき、これを教えてくれればという部分を解説しました。本書で解説している部分はMetalの基礎となる部分で、Metalを使うときに必ず触れることになる部分です。

詳細

基礎から学ぶSwiftUI


「基礎から学ぶ SwiftUI」というタイトルの本を執筆しました。

SwiftUIの入門書です。

SwiftUIのコンセプトは「ユーザーインターフェイスを作るための最短パスを提供する」「一度学べば(Appleのプラットフォームの)どこにでも適用できる」です。

SwiftUIの概要から始まって、一つ一つのテクノロジートピックに注目しながらSwiftUIとは何か?どんなことができるのか?どのようなコードを書けば良いのかなどを丸々一冊使って解説しています。

詳細

関連記事

  1. Swift、オープンソース化、本当に出た!

  2. Swift 3のセミナー資料作成

  3. Try CLion on the Ubuntu Linux to de…

  4. OpenCVで使われている並列化を見てみる

  5. Swift逆引きハンドブックのSwift 3.0対応についての情報を公…

  6. SwiftとObjective-Cを共存させるならObjective-…

  7. GW中もいつもと同じ

  8. 「iPadで学ぶ はじめてのプログラミング」の見本誌が届きました

最近の著書

  1. 基礎から学ぶ SwiftUI

最近の記事