12

実際のプロジェクトにおける ARC パフォーマンスの影響に関する客観的な研究は見つかりませんでした。 公式ドキュメントによると

コンパイラは多くの余分な保持/解放呼び出しを効率的に排除し、Objective-C ランタイム全般の高速化に多くの努力が注がれています。特に、一般的な「retain/autoreleased オブジェクトを返す」パターンははるかに高速であり、メソッドの呼び出し元が ARC コードの場合、実際にはオブジェクトを自動解放プールに入れません。

これは、ハイテクファンボーイによって「ARCの方が速い」に中継/変形されました。

私が確かに知っていることは、私が測定したものです。-Os最近、iOS プロジェクトを ARC に移行し、コードの一部の CPU 集中型領域 (もちろん、フラグを使用してコンパイルされた製品コード) の前後でパフォーマンス測定を行いました。

70% (はい 70%) のパフォーマンス低下が見られました。インストルメントを使用して保持/解放イベントを追跡すると、(ARC 以前の環境では) 実行しない領域にコンパイラが多数の保持/解放ペアを導入することに気付きました。基本的にどんな一時でも強くなります。それがパフォーマンス低下の原因だと思います。

移行前の元のコードは、すでにかなり最適化されていました。自動解放はほとんど行われませんでした。したがって、ARC に切り替えても改善の余地はほとんどありませんでした。

幸いなことに、Instrumentsは、ARC によって導入された最もコストのかかる保持/解放を表示することができ、__unsafe_unretained を使用してそれらを非アクティブ化することができました。これにより、パフォーマンスの低下がわずかに緩和されます。

パフォーマンスの低下を回避するためのこの手法またはその他の手法に関する情報はありますか? (ARC の無効化は別として)

ありがとう。

編集:パフォーマンスへの影響のためにARCが悪いと言っているのではありません。ARC を使用する利点は、パフォーマンスの回帰よりもはるかに優れています (私たちのコードでは、回帰は目に見える効果がなかったため、手放しました)。ARC は非常に優れたテクノロジーだと思います。MRCには二度と戻りません。私は好奇心からこの質問をしています。

私は、多かれ少なかれARCコードがMRCコードよりも高速になるという印象を与えるトピックに関する大多数のブログ(ここ とそこのような)に少しイライラしています(私がそれを手に入れる前に私が信じていたことです) )。そして、これはいくつかのマイクロベンチマーク以外では当てはまらないと本当に感じています. せいぜい、MRC と同等であることを期待できますが、それより速くはなりません。オブジェクト操作 (ドキュメント内の単語のカウントなど) を含むいくつかの簡単なテストを行いました。ARCが遅くなるたびに(最初に話していた70%のパフォーマンス低下ほど悪くないと思いました)

\begin{皮肉}

実際、前述のドキュメントは質問に答えました:

ARCは遅いですか?

何を測定しているかにもよりますが、一般的には「いいえ」です。...

これは明らかに次のように理解されるべきです

\begin{パロディ}

うーん...うーん...これは新しいクールなテクノであり、採用してもらいたいので、遅いとは言えません。したがって、集団訴訟を避けるために、二重引用符で「いいえ」と答えます。そしてくだらない質問はやめましょう。

\end{パロディ}

\end{皮肉}

4

5 に答える 5

6

これが私のARCとMRCのパフォーマンス測定値です。パフォーマンス テスト プロジェクトはgithub で入手できるので、独自のテストを追加できます。必ずデバイスで実行してください。シミュレーターでの結果は歪んでおり、多くの場合、MRC に有利です。

要約する:

ARC と MRC は通常、同じ速度です。一般に、コードは ARC の下で高速になるはずですが、タイトなループは大幅に遅くなる可能性があります。

低レベルのテストでは、最適化 (autorelease リターン、@autoreleasepool) により、ARC は MRC よりも速度が優れています。

アプリがシングルスレッドである限り、ARC が追加の保持/解放を挿入するコードがいくつかありますが、これは MRC では厳密には必要ありません。このようなコードは ARC では遅くなる可能性がありますが、タイトなループでのみ違いがあり、問題のコードに大きく依存します。

たとえば、オブジェクトを受け取るメソッドは、メソッドの実行中にマルチスレッド アプリケーションでオブジェクトが解放される可能性があるため、MRC の下でもそれを保持する必要があります。MRC でそのコードを省略できるという事実により、高速化されますが、本質的に安全ではありません (ただし、そのような問題に遭遇することはめったにありませんが、したくない場合は OTOH)。例:

-(void) someMethod:(id)object
{
    [object retain]; // inserted by ARC, good practice under MRC
    [object doSomething];
    [object doAnotherThing];
    [object release]; // inserted by ARC, good practice under MRC
}

このため、テスト プロジェクトで使用した遺伝的アルゴリズムは、ARC では約 40% 遅くなります。これは悪い (極端な) 例です。そのようなアルゴリズムでは、NSMutableArray に対する挿入/削除操作が多く、NSNumber オブジェクトが作成されるため、C で重要なコード セクションを書き直すことによって、パフォーマンスが大幅に向上するはずだからです。

場合によっては遅くなる可能性があるため、ARCを完全に無視するのはまったくの過失です。これらの状況がパフォーマンスにとって重要であることがわかった場合は、-fno-objc-arcそのコードを作成するか、C で書き直してください。

ARC は、パフォーマンス上の理由から、賛成または反対と見なされるべきではありません。ARC は、プログラマーの仕事をより簡単にするツールです。リークされたオブジェクトやダングリング ポインターのクラッシュを見つけようとして時間を無駄にするのが好きで、MRC に固執するかどうかを決めるのはあなた次第です。

于 2013-03-20T20:47:33.253 に答える
2

同様のパフォーマンス低下が発生した場合、考えられる唯一の説明は、手動で管理されたコードが「安全ではない」ということだと思います。つまり、メモリリークの可能性があり、プログラムのメモリ管理を何らかの形で安全にしない保持/解放呼び出しが少なかったことを意味します。

手動で管理されたコードが適切に記述されていて安全であれば、ARC コードが手動で管理されたコードよりもそれほど遅いとは思いません...

もちろん、よく書かれた手動で管理されたコードは、ARC コードよりもわずかに高速になる可能性があると思いますが、どのくらいの費用がかかりますか? 手でやるべきことはもっとたくさんあります... ほとんどの場合、それは価値があるよりも手間がかかります!

さらに、ARC は完全に書かれた MRCではなく、ガベージ コレクター環境と比較する必要があると思います

ただし、適切に作成された MRC コード ベースがあり、それが安全で高速であると確信している場合、なぜそれを ARC の下に置くのでしょうか。フラグを使用して、手動でメモリ管理してください-fno-objc-arc... ARCの使用は、特にこれらの理由で必須ではありません。

于 2012-09-21T09:01:28.130 に答える
1

推測に基づいて、トピック外のことを話すことを恐れています...


ARC によるパフォーマンス向上のほとんどは、retain/release 呼び出しを省略するのではなく、インライン化することによるものだと思います。

また、私が経験した限りでは、ARC は通常、追加の保持/解放呼び出しを導入します。ARC は非常に厳格で保守的であるため、保持/解放の省略はほとんど実行されません。また、新しく挿入された保持/解放呼び出しの多くは意味的に必要ですが、MRC ではプログラマーによって省略されています。(たとえば、すべての受け渡し関数パラメーターと一時変数)

そう、

  1. 保持/解放の呼び出しの数は、セマンティックの完全性を厳密に満たすために実際に大幅に増加しました。

  2. それらのいくつかは、非常に保守的な最適化によって省略されます。

  3. 保持/解放への実際の呼び出しは、最適化によってインライン化されます-動的なObjective-Cメソッド呼び出しから静的C関数呼び出しになることにより-呼び出しコスト自体が大幅に削減されます。

その結果、通常はパフォーマンスが低下します。ARC を使用する前に、多くの保持/解放呼び出しを省略していたことに気付きました。しかし、あなたが指摘したように、私たちが得たものは何でも、それは意味的完全であり、.__unsafe_unretained

于 2013-08-10T01:19:36.517 に答える
0

もちろん、一時変数はデフォルトで強力です。それは明示的で明確に文書化されています。そして、よく考えてみると、それは人々が通常望んでいることです。

 MyWidget *widget=[[MyWidget alloc]init];  // 1
 [myWidget mill]; // 2

ウィジェットが強力でない場合、新しい MyWidget が 1 行目で作成され、2 行目の前に解放してゼロにすることができます!

確かに、多くの一時変数を使用する場合 (たとえば、デメテルの法則に厳密に従っている場合)、タイトなループの途中で一時変数にパフォーマンスがないと仮定している場合は、世界にはたくさんのレジスタがあるので、まったくコストがかかりません。

そして、それはあなたが今住んでいるコーナーかもしれません.

しかし、それはエキゾチックで特別な場所です! ほとんどのコードは、タイトなループの途中にはありません。ほとんどのタイト ループは、パフォーマンスのボトルネックにはなりません。また、ほとんどのタイトなループでは、多くの中間変数は必要ありません。

逆に、ARC は、手動ではできない方法で自動解放の最適化を行うことができます (ただし、オプティマイザーでは可能です)。そのため、タイト ループ内に自動解放された変数を返す関数がある場合は、ARC を使用したほうがよい場合があります。

時期尚早の最適化は悪い考えです。あなたは避けられないパフォーマンスのコーナーにいるかもしれませんが、ほとんどの人はそうではありません. 確かに、私はほとんどの時間を OS X で過ごしていますが、答えがより良いアルゴリズムではないというパフォーマンスの問題が発生してから何年も経っています。

(最後に、ARC がアプリケーションに 70% のパフォーマンス ヒットを引き起こしている場合、クリティカル パスで非常に多くのメモリ管理を行っていることになります! 考えてみてください: 時間の 70% をオブジェクトの割り当てと解放に費やしています。これは、Flyweight やオブジェクト キャッシュ、リサイクル プールなどの教科書的なケースのように思えます!)

于 2013-03-20T21:41:50.527 に答える