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でラッパークラスやユーティリティクラスを作るのが現実的だと思います。

著書紹介

よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!

この記事を書いた人

Akira Hayashi (林 晃)のアバター Akira Hayashi (林 晃) Representative(代表), Software Engineer(ソフトウェアエンジニア)

アールケー開発代表。Appleプラットフォーム向けの開発を専門としているソフトウェアエンジニア。ソフトウェアの受託開発、技術書執筆、技術指導・セミナー講師。note, Medium, LinkedIn
-
Representative of RK Kaihatsu. Software Engineer Specializing in Development for the Apple Platform. Specializing in contract software development, technical writing, and serving as a tech workshop lecturer. note, Medium, LinkedIn

目次