エンコード時にできることは、運転時まで遅らせないでください
2386 ワード
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プログラミング世界のエントロピー増加原理