2017-05-17を更新。私は、この質問の発端となった会社で働いていません。DelphiXExにアクセスできません。私がそこにいた間、問題は、FPC + GCCの混合(Pascal + C)に移行することで解決しました。いくつかのルーチンのNEON組み込み関数は、違いを生みました。 (FPC + GCCは、標準ツール、特にValgrindの使用を可能にするため、強くお勧めします。)信頼できる例で、Delphi XExから最適化されたARMコードを実際に生成できる方法を示すことができればm答えを喜んで受け入れます。
EmbarcaderoのDelphiコンパイラは、LLVMバックエンドを使用して、ARMデバイス用のネイティブAndroidコードを生成します。 Androidアプリケーションにコンパイルする必要のある大量のPascalコードがあり、Delphiでより効率的なコードを生成する方法を知りたいです。現時点では、自動SIMD最適化などの高度な機能についても話しているのではなく、妥当なコードを生成するだけです。 LLVM側にパラメーターを渡す方法があるはずですか、それとも何らかの形で結果に影響しますか?通常、どのコンパイラにもコードのコンパイルと最適化に影響する多くのオプションがありますが、DelphiのARMターゲットは単に「最適化のオン/オフ」であるように見えます。
LLVMは適度にタイトで賢明なコードを生成できるはずですが、Delphiはその機能を奇妙な方法で使用しているようです。 Delphiはスタックを非常に頻繁に使用することを望んでおり、一般的にプロセッサのレジスタr0〜r3を一時変数としてのみ使用します。おそらく最もクレイジーなのは、通常の32ビット整数を4つの1バイトのロード操作としてロードしているようです。 DelphiでARMコードをより良く生成し、Android向けにバイト単位の手間をかけずに作成するにはどうすればよいですか?
最初は、バイト単位の読み込みはビッグエンディアンからバイトオーダーをスワップするためだと思っていましたが、そうではなく、実際には4つのシングルバイトロードで32ビットの数値を読み込むだけです。アライメントされていないWordサイズのメモリロードを実行せずに32ビット全体。 (それを避けるべきかどうかは別のことであり、これはコンパイラのバグであることを示唆しています)*
この単純な関数を見てみましょう:
function ReadInteger(APInteger : PInteger) : Integer;
begin
Result := APInteger^;
end;
最適化がオンになっている場合でも、アップデートパック1が適用されたDelphi XE7およびXE6は、その関数の次のARMアセンブリコードを生成します。
Disassembly of section .text._ZN16Uarmcodetestform11ReadIntegerEPi:
00000000 <_ZN16Uarmcodetestform11ReadIntegerEPi>:
0: b580 Push {r7, lr}
2: 466f mov r7, sp
4: b083 sub sp, #12
6: 9002 str r0, [sp, #8]
8: 78c1 ldrb r1, [r0, #3]
a: 7882 ldrb r2, [r0, #2]
c: ea42 2101 orr.w r1, r2, r1, lsl #8
10: 7842 ldrb r2, [r0, #1]
12: 7803 ldrb r3, [r0, #0]
14: ea43 2202 orr.w r2, r3, r2, lsl #8
18: ea42 4101 orr.w r1, r2, r1, lsl #16
1c: 9101 str r1, [sp, #4]
1e: 9000 str r0, [sp, #0]
20: 4608 mov r0, r1
22: b003 add sp, #12
24: bd80 pop {r7, pc}
Delphiが必要とする命令とメモリアクセスの数を数えるだけです。そして、4つのシングルバイトロードから32ビット整数を構築しています...関数を少し変更し、ポインターの代わりにvarパラメーターを使用すると、少し複雑さが少なくなります。
Disassembly of section .text._ZN16Uarmcodetestform14ReadIntegerVarERi:
00000000 <_ZN16Uarmcodetestform14ReadIntegerVarERi>:
0: b580 Push {r7, lr}
2: 466f mov r7, sp
4: b083 sub sp, #12
6: 9002 str r0, [sp, #8]
8: 6801 ldr r1, [r0, #0]
a: 9101 str r1, [sp, #4]
c: 9000 str r0, [sp, #0]
e: 4608 mov r0, r1
10: b003 add sp, #12
12: bd80 pop {r7, pc}
ここでは逆アセンブリを含めませんが、iOSの場合、Delphiはポインターとvarパラメーターのバージョンに対して同一のコードを生成しますが、Android varパラメーターのバージョンとほとんど同じですが、まったく同じではありません。 編集:明確にするため、バイト単位の読み込みはAndroidでのみ可能です。また、Androidでのみ、ポインターとvarパラメーターのバージョンは互いに異なります。 iOSでは、両方のバージョンがまったく同じコードを生成します。
比較のために、FPC 2.7.1(2014年3月からのSVNトランクバージョン)が最適化レベル-O2の関数について考えるものを以下に示します。ポインターとvarパラメーターのバージョンはまったく同じです。
Disassembly of section .text.n_p$armcodetest_$$_readinteger$pinteger$$longint:
00000000 <P$ARMCODETEST_$$_READINTEGER$PINTEGER$$LONGINT>:
0: 6800 ldr r0, [r0, #0]
2: 46f7 mov pc, lr
また、Android NDKに付属のCコンパイラーで同等のC関数をテストしました。
int ReadInteger(int *APInteger)
{
return *APInteger;
}
これは、FPCが作成したものと本質的に同じものにコンパイルされます。
Disassembly of section .text._Z11ReadIntegerPi:
00000000 <_Z11ReadIntegerPi>:
0: 6800 ldr r0, [r0, #0]
2: 4770 bx lr
この問題を調査しています。要するに、ポインターによって参照される整数の潜在的な不整列(32境界まで)に依存します。すべての答えを得るにはもう少し時間が必要です...そしてこれに対処する計画が必要です。
MarcoCantù、 Delphi Developersのモデレーター
また、 64ビットでDelphi zlibおよびZipライブラリが非常に遅いのはなぜですか Win64ライブラリは最適化なしで出荷されているためです。
QPレポート: RSP-9922コンパイラによって生成されたARMコードが正しくありません。$ Oディレクティブは無視されますか? 、マルコは次の説明を追加しました。
ここには複数の問題があります:
- 示されているように、最適化設定はユニットファイル全体にのみ適用され、個々の機能には適用されません。簡単に言えば、同じファイルで最適化をオンまたはオフにしても効果はありません。
- さらに、単に「デバッグ情報」を有効にすると、最適化がオフになります。したがって、デバッグ中に最適化を明示的にオンにしても効果はありません。したがって、IDEのCPUビューは、最適化されたコードの逆アセンブルされたビューを表示できません。
- 第三に、アライメントされていない64ビットデータをロードすることは安全ではなく、エラーが発生するため、特定のシナリオで必要な4つの1バイト操作が個別に行われます。