Benchmarkin
7749 ワード
意味のある仕事を完成させるには抽象が重要だが、副作用をもたらす.仕事がうまくいくためには、バッチ処理の具体的な論理を決定するために、いくつかの細部を洞察する必要があります.特定のコンテキストの有用な情報を見つけることは非常に重要であり、挑戦的であり、効率的なプログラミングの核心である.
「
プログラミングでは、
科学的方法は一連の論理ステップをカバーして問題を推論する.質問 構造仮説 予想結果 検証仮説 分析結果 プログラミングに適用すると、一般的に2つの問題が提起されます.このコードの絶対効率はいくらですか?計算力とメモリの上限に達しましたか?異なるサンプルサイズを適用する際のボトルネック操作は何ですか? このコードの相対効率はいくらですか?方法Aと方法Bのどちらが速いですか?
オペレーティングシステム自体のすべての基本的な要因は可変性が非常に強いため、性能は大量の試験によって測定されるべきである.ほとんどの用途では、サンプル数は105〜108で直接的に妥当である.
ここでは、可変配列に要素を追加する効率を見てみましょう.
申請メモリをテストする時間は必要ありませんので、benchmarkの外部で配列に追加された要素を一度だけ宣言します.
この
具体的な手順は次のとおりです.
この例ではstartTimeとendTimeの間のblockコードは必要ありませんが、可読性を向上させるために、変数を分離して大規模な突然変異が発生するコードをより明確にすることができます.
公開された関数定義がないため、
あなたは自分ですべての
man dispatch_benchmark(3)
The dispatch_benchmark function executes the given block multiple times according to the count variable and then returns the average number of nanoseconds per execution. This function is for debugging and performance analysis work. For the best results, pass a high count value to dispatch_benchmark. Please look for inflection points with various data sets and keep the following facts in mind: Code bound by computational bandwidth may be inferred by proportional changes in performance as concurrency is increased. Code bound by memory bandwidth may be inferred by negligible changes in performance as concurrency is increased. Code bound by critical sections may be inferred by retrograde changes in performance as concurrency is increased. Intentional: locks, mutexes, and condition variables. Accidental: unrelated and frequently modified data on the same cache-line.
これらのドキュメントを省略した場合は、必ずもう一度読んでください.これらのドキュメントは非常に役に立ちます.この関数の使い方をよりよく説明するために、ドキュメントには非常に指導的なガイドラインも書かれています.
前の例を
見たでしょう、だいぶよくなったでしょう.以前の秒計時よりもミリ秒が正確であり、
この例では、「
一緒に見てみましょう.
結果
iOS 7.1を搭載したiPhoneシミュレータをテストした結果、以下のようになりました.
大規模なサンプルのテストを経て、
結果は論争の余地はないが(私たちのこれらの効率消費は何を意味しますか?不適切な最適化を避けるために,この効率の違いを大規模なシステムで無視できるかどうか考えてみよう. 配列要素の個数を変えると、何か違う結論が出ますか?capacityパラメータを用いたため,配列要素の増加を回避したと推測できるが,大規模データのn値消費を計算するにはどの程度の大きさがあるのだろうか. 他の集合タイプ、例えばNSMutableSetやNSMutableDictionaryのcapacityパラメータの初期化効率はどうでしょうか.民衆には真実が必要だ!
あなたが答えなければならない問題を知っています.私たちは常に不思議な思考の代わりに思考に力を入れていますが、科学的な方法の水没から自分を守らなければなりません.不完全な推理を支持するべきではありません.結論を出す前に、大きな背景の下であなたの結果が何を意味するかを理解するのに時間がかかります. appのコミットコードにbenchmarkingを追加しないでください. Instrumentsを使用して、より有用な結果を得る.一連の計算プロセスの実行絶対時間は確かに価値があることが分かったが、メモリの使用を減らすための完全な参考にはならないかもしれない. 実際のデバイス上でbenchmark.他のいかなる効率測定ツールのように、測定は結局本当の機械を走らなければならない.多くの場合、シミュレータと実際の装置の効率測定結果は一致するが、念のためにそうする価値がある. を早期に最適化しないでください.この言葉はいくら強調しても過言ではない.エンジニアの一般的な傾向は、本当の原因を発見する前に、彼らが考えている「遅いコード」に注目しすぎることです.ベテランでも応用のボトルネックを予測しやすい.影を追うのに時間を無駄にしないでください.
これらの計算世界に隠されている抽象層の具体的な実装の詳細を持っており、大規模なコード階層で何がその核心的な役割を果たしているのか、私たちが何を得ることができるのかを理解することができる場合があります.
科学と基準統計の厳格なプログラムを通じて、開発者はそのコードの性能のお父さんの方面で理由の十分な結論を出すことができます.これらの原則と慣例を自分のプロジェクトに適用して、自分に合った結論を出して、それに基づいて最適化します.
benchmarking
を使用すると、エンジニアは、コード内の実行効率のベールを剥がし、得られた情報を利用して最適化することができる.これは、appをより速く実行したいエンジニア(または自重したいエンジニア)一人一人にとって必須のツールです.「
benchmark
」という言葉は19世紀にさかのぼることができる.benchmark
は、石を平板に切断するカッターまたは測定用のブラケットである.後にこの語の「物を測る基準」の比喩義が様々な分野に応用された.プログラミングでは、
benchmark
とbenchmarking
は少し意味的な違いがあります.benchmark
は、プログラムがハードウェアおよびソフトウェア上の実行効率を明確に測定し、比較するものである.対照的にbenchmarking
は、測定効率のコードである.Objective-CでのBenchmarkingによる効率測定
Benchmark
は他の認知論と同じ法則に従うべきで、統計量のような科学的方法のように共通の理解がある.科学的方法は一連の論理ステップをカバーして問題を推論する.
オペレーティングシステム自体のすべての基本的な要因は可変性が非常に強いため、性能は大量の試験によって測定されるべきである.ほとんどの用途では、サンプル数は105〜108で直接的に妥当である.
初回:CFAbsoluteTimeGetCurrent
ここでは、可変配列に要素を追加する効率を見てみましょう.
benchmark
を確立するために、count
は、追加する必要がある要素の数を示し、iterations
は、このテストが何回実行されるかを示します.static size_t const count = 1000;
static size_t const iterations = 10000;
申請メモリをテストする時間は必要ありませんので、benchmarkの外部で配列に追加された要素を一度だけ宣言します.
id object = @"";
この
benchmarking
を作るのは簡単です.コードの実行前に1回の時間を記録し、実行後に1回記録し、時間差を比較します.mach_absolute_time
を包装したCACurrentMediaTime()
法を簡単に使用して、秒単位で時間を測定することができます.NSDate
またはCFAbsoluteTimeGetCurrent()
のオフセット量とは異なり、mach_absolute_time()
およびCACurrentMediaTime()
は内蔵クロックに基づいており、より正確により原子化的に測定することができ、外部時間の変化(例えば、時間領域の変化、夏時間制、秒の突然変異など)によって変化することはない.for
サイクルは、count
およびiterations
を増加させるために使用される.各サイクルは@autoreleasepool
に包まれ、メモリ消費量を低減するために使用される.具体的な手順は次のとおりです.
CFTimeInterval startTime = CACurrentMediaTime();
{
for (size_t i = 0; i < iterations; i++) {
@autoreleasepool {
NSMutableArray *mutableArray = [NSMutableArray array];
for (size_t j = 0; j < count; j++) {
[mutableArray addObject:object];
}
}
}
}
CFTimeInterval endTime = CACurrentMediaTime();
NSLog(@"Total Runtime: %g s", endTime - startTime);
この例ではstartTimeとendTimeの間のblockコードは必要ありませんが、可読性を向上させるために、変数を分離して大規模な突然変異が発生するコードをより明確にすることができます.
2発目:dispatch_benchmark
dispatch_benchmark
はlibdispatch
(Grand Central Dispatch)の一部である.しかし、厳粛に言えば、この方法は公開されていないので、自分で宣言しなければなりません.extern uint64_t dispatch_benchmark(size_t count, void (^block)(void));
公開された関数定義がないため、
dispatch_benchmark
はXcode
にも公開されていない.幸いなことにman
ページがあります.あなたは自分ですべての
man
を理解するべきで、完全なman
の内容はここを见ますman dispatch_benchmark(3)
The dispatch_benchmark function executes the given block multiple times according to the count variable and then returns the average number of nanoseconds per execution. This function is for debugging and performance analysis work. For the best results, pass a high count value to dispatch_benchmark. Please look for inflection points with various data sets and keep the following facts in mind:
これらのドキュメントを省略した場合は、必ずもう一度読んでください.これらのドキュメントは非常に役に立ちます.この関数の使い方をよりよく説明するために、ドキュメントには非常に指導的なガイドラインも書かれています.
前の例を
dispatch_benchmark
で書くとこうなりますuint64_t t = dispatch_benchmark(iterations, ^{
@autoreleasepool {
NSMutableArray *mutableArray = [NSMutableArray array];
for (size_t i = 0; i < count; i++) {
[mutableArray addObject:object];
}
}
});
NSLog(@"[[NSMutableArray array] addObject:] Avg. Runtime: %llu ns", t);
見たでしょう、だいぶよくなったでしょう.以前の秒計時よりもミリ秒が正確であり、
dispatch_benchmark
も手動書き込みサイクルのCFAbsoluteTimeGetCurrent()
よりも文法構造的に優れているように見える.NSMutableArray array対決arrayWithCapacity:!
Objective-C
で直接benchmark
を実行する方法がわかりました.では、比較速度のテストをしましょう.この例では、「
capacity
パラメータの入力と直接初期化の違い」という問題を依然として考えています.あるいはもっと直接的に「-arrayWithCapacity:
を使うか使わないか(これは問題です)」です.一緒に見てみましょう.
uint64_t t_0 = dispatch_benchmark(iterations, ^{
@autoreleasepool {
NSMutableArray *mutableArray = [NSMutableArray array];
for (size_t i = 0; i < count; i++) {
[mutableArray addObject:object];
}
}
});
NSLog(@"[[NSMutableArray array] addObject:] Avg. Runtime: %llu ns", t_0);
uint64_t t_1 = dispatch_benchmark(iterations, ^{
@autoreleasepool {
NSMutableArray *mutableArray = [NSMutableArray arrayWithCapacity:count];
for (size_t i = 0; i < count; i++) {
[mutableArray addObject:object];
}
}
});
NSLog(@"[[NSMutableArray arrayWithCapacity] addObject:] Avg. Runtime: %llu ns", t_1);
結果
iOS 7.1を搭載したiPhoneシミュレータをテストした結果、以下のようになりました.
[[NSMutableArray array] addObject:]: Avg. Runtime 26119 ns
[[NSMutableArray arrayWithCapacity] addObject:] Avg. Runtime: 24158 ns
大規模なサンプルのテストを経て、
capacity
を用いたかどうかは7%の効率差をもたらした.結果は論争の余地はないが(私たちの
benchmark
は完璧に働いている)、本当に重要なのはこの結果が発生した原因を説明することだ.誤った考えは,benchmark
パラメータで初期化することが最適であると結論した.正しい考えは、このcapacity
の結果は、私たちが質問を続けるべきであることを示しています.Benchmarking常識ガイド
benchmark
は、appがApp Storeによって拒否される可能性があります.dispatch_benchmark
コードは、最終的にコミットされた製品に追加されるべきではありません.benchmark
は、個別のプロジェクトブランチまたは独立したテスト例に分離されるべきである.Benchmarking
を使用して、疑問のあるコードのスタック呼び出しとメモリ使用量を分析すると、このコードに何が起こったのかよく理解できます.Instruments
にあなたのアプリケーションがどこで最も多くの時間を費やしたのかを教えてあげましょう.Instruments
はかつて物理学の中の粒子化実験を「時計がどのように作られたのか、機械がどのように時計の山を組み合わせて無駄な歯車を取り除いて動いたのか」にたとえたことがある.Richard Feynman
コードの感じはこれのようです.これらの計算世界に隠されている抽象層の具体的な実装の詳細を持っており、大規模なコード階層で何がその核心的な役割を果たしているのか、私たちが何を得ることができるのかを理解することができる場合があります.
科学と基準統計の厳格なプログラムを通じて、開発者はそのコードの性能のお父さんの方面で理由の十分な結論を出すことができます.これらの原則と慣例を自分のプロジェクトに適用して、自分に合った結論を出して、それに基づいて最適化します.