エンコード時にできることは、運転時まで遅らせないでください


TL.DR


ソフトウェアは巨大な有限状態機です.エンジニアが日常的に行うバグ修復、性能調整は、本質的にコードが秩序ある状態にあることをできるだけ保証することである.OCもSwiftも強力なコンパイラをサポートしています.できるだけ多くの状態を符号化に固定すると,実行期間の状態が減少し,ソフトウェアの状態総数が減少する.
ステータス総数が少なくなると、エラーが少なくなり、パフォーマンスが向上します.

Case 1:OC RuntimeのNon fragile ivars


この例のキーはパフォーマンスです.
OCはABI互換性を実現するために、C、C++以降のオブジェクトのメモリ構造を変更し、各メンバー変数にアクセスするには2回のアドレスが必要です.例えばobjのn番目の変数のオフセットアドレスを取得するには
*((&obj->isa.cls->data()->ro->ivars->first)[N]->offset);

ゆっくり爆発しているように見えます.
実装では、LLVMは、各クラスの各メンバー変数にグローバル変数を割り当て、そのメンバー変数のオフセット値を格納します.このように、各変数にアクセスするには2回のアドレスが必要であり、まずグローバル変数を取得し、次にグローバル変数の値をアドレスとして真の変数を見つける.
コンパイル済み
obj->myInt = 42;

以下の簡単なC言語コードに対応
int32_t g_ivar_MyClass_myInt = 40;  //  
*(int32_t *)((uint8_t *)obj + g_ivar_MyClass_myInt) = 42;

だからivar_t.offsetは、intを直接置く理由ではなく、intポインタでオフセット値を格納します.
struct ivar_t {
    int32_t *offset;  // , 
    const char *name;
    const char *type;
    
    //...
}

本当にオフセット値を格納するアドレスは固定されており,コンパイル時に決定される.そのため、ダイナミックレイアウトのメンバー変数を2つの命令で解決することができます.

Case 2:行列乗算


この例のポイントはエラーを減らすことです.
図に示すように、1番目のマトリクスの列数が2番目のマトリクスの行数と同じであることを保証します.簡単な方法はランタイムチェックです
struct Matrix {
  let rows: Int
  let columns: Int
}

func multiply(m1: Matrix, _ m2: Matrix) -> Matrix? {
  // do the matrices have the correct sizes?
  precondition(m1.columns == m2.rows)
  
  // bunch of math...
}

より良い方法は、符号化時に行列が等しくないことを許さないことです.
protocol Dimension {
  static var size: Int { get set }
}

func multiply
             (m1: Matrix, _ m2: Matrix) -> Matrix {
  // bunch of math...
  return Matrix()
}

実行結果
struct NumExamples: Dimension { static var size = 20 }
struct NumFeatures: Dimension { static var size = 10 }
struct OneDimensional: Dimension { static var size = 1 }

let A = Matrix()
let B = Matrix()

let C = multiply(A, B)   // yay!

let D = multiply(B, A)   // compiler error

完全な最適化過程は本論文の議論の範囲内ではなく,興味のあるものはここを見ることができる.

リファレンスリンク


Mobjc explain:Non-fragile ivarsDynamic ivars:solving a fragile base class problemプログラミング世界のエントロピー増加原理