Swiftで数値計算を行う – WWDC2020

Session概要

計算数学の新しいSwiftパッケージのSwift Numericsについて。パッケージが提供するプロトコルとタイプを見ながら、汎用コードを書く際にどのように使用するかをお話しします。パフォーマンスを向上させ、メモリ使用量を低減する新しいFloat 16型を使う方法、タイミングについてなど

https://developer.apple.com/videos/play/wwdc2020/10217/

Swift Numeric パッケージについて

  • https://github.com/apple/swift-numerics で開発されているオープンソース
  • 標準の浮動小数点型に対して動作する数のアルゴリズムの実装を容易にするための設計
    • 対数などの関数をサポートし、あらゆる浮動小数点型に対応している

Realプロトコルについて

  • Realプロトコルに適合するデータ型は全て標準の浮動小数点を扱う能力をサポートしている
    • 下記の全てのプロトコルを1つの統合した概念に組み合わせたもので、標準の浮動小数点型の通常の能力を定義している
    • 浮動小数点のコードを自動的にそれぞれの標準の浮動小数点型をサポートする方法で定義可能
  • AdditiveArithmetric
    • 足し算と引き算をサポートする型に適用される
    • ElementaryFunctions:浮動小数点の関数の大きな集まりを規定する
      • 三角関数や対数指数累乗根べき数が含まれる
      • RealFunctions:ElementaryFunctionsプロトコルをさらに拡張したプロトコル
        • ガンマ関数やエラー関数、及び三角関数の変種など
  • SignedNumeric
    • AdditiveArithmetricをかけ算の概念に拡大したもの
    • AlgebracField:割り算の概念に拡大したもの
  • FloatingPoint
    • 実用的なコンピュータの浮動小数点数の実装に必要な概念が実装されている
    • 比較関数、指数、仮数に分解する方法および、最大値や最小値無限大円周率など便利な定数が含まれている

実用的な複素数の型について

  • 標準の複素数型の十分な機能を有する実装がされている
  • 複素数は実数と虚数の構成要素を両方持っており、これらの構成要素から新たな複素数を作る方法を持つ
  • 複素数を十分に実用的にするためには、標準の算術演算を定義する必要がある
  • 複素数はしばしば極座標で表現され、長さと位相角で表される
  • 結果として、メモリレイアウトはCやC++の複素数型のものと同じになる
  • 注意点
    • コードをCからC++へ移植する時には無限大とNaNを扱う際に注意する
    • SwiftはC, C++より単純でかなり効率の良いもの
import Numerics

// 1.0, 2.0はDoubleなので、zはComplex<Double>型となる
let z = Complex(1.0, 2.0) // z = 1 + 2 i
public struct Complex<NumberType> where NumberType: Real {
    /// The real component
    public var real: NumberType
  
    /// The imaginary component
    public var imaginary: NumberType
  
    /// Construct a complex number with specified real and imaginary parts
    public init(_ real: NumberType, _ imaginary: NumberType) {
        self.real = real
        self.imaginary = imaginary
    }
}

// 算術演算の定義
extension Complex: SignedNumeric {
    /// The sum of 'z' and 'w'
    public static func +(z: Complex, w: Complex) -> Complex {
        return Complex(z.real + w.real, z.imaginary + w.imaginary)
    }

    /// The difference of 'z' and 'w'
    public static func -(z: Complex, w: Complex) -> Complex {
        return Complex(z.real - w.real, z.imaginary - w.imaginary)
    }

    /// The product of 'z' and 'w'
    public static func *(z: Complex, w: Complex) -> Complex {
        return Complex(z.real * w.real - z.imaginary * w.imaginary,
                       z.real * w.imaginary + z.imaginary * w.real)
    }
}

extension Complex {
    /// The Euclidean norm (a.k.a. 2-norm) of the number.
    public var length: NumberType {
        return .hypot(real, imaginary)
    }
  
    /// The phase (angle, or "argument").
    ///
    /// Returns the angle (measured above the real axis) in radians.
    public var phase: NumberType {
        return .atan2(y: imaginary, x: real)
    }
  
    /// A complex value with specified polar coordinates.
    public init(length: NumberType, phase: NumberType) {
        self = Complex(.cos(phase), .sin(phase)).multiplied(by: length)
    }
}
Cと比較した際のSwiftのパフォーマンスベンチマーク

Float 16型について

  • Float 16はIEEE754の標準浮動小数点フォーマットでSwiftにとって新しいものです
    • ARMベースのプラットフォーム上のSwiftでは、既に利用可能でx86に載せる前に正しい呼び出し規約をまとめるため、Intelと作業中
  • Float 16はあらゆる点において標準であり、完全に支持された浮動小数点の型
    • 標準ライブラリからのコアプロトコル、Realプロトコルに準拠しSIMDScalarのようなものを含む
  • AppleのCPUはA11 Bionicから直接サポートする
    • 古いCPUではFloatを使う演算をシミュレーションしてFloat16をサポートする

Float 16使用のトレードオフ

  • メリット
    • 小さなデータ型なのでほとんどはSIMDレジスタかメモリのページに合わせられる
  • デメリット
    • 小さなデータ型としてより限られた正確さと範囲を持つことになる
      • そのため、DoubleやFloatをFloat 16に移植する際に注意する必要がある

BNNSの畳み込みと比較したベンチマーク
最新情報をチェックしよう!