อัพเดท 2017-05-17 ฉันไม่ทำงานให้กับ บริษัท ที่มีคำถามนี้มาแล้วและไม่สามารถเข้าถึง Delphi XEx ได้ ในขณะที่ฉันอยู่ที่นั่นปัญหาได้รับการแก้ไขโดยการโยกย้ายไปยัง FPC + GCC (Pascal + C) ผสมกับ NEON อินทรินซินส์สำหรับกิจวัตรบางอย่างที่สร้างความแตกต่าง (แนะนำให้ใช้ FPC + GCC เพราะช่วยให้สามารถใช้เครื่องมือมาตรฐานโดยเฉพาะอย่างยิ่ง Valgrind) หากมีใครสามารถสาธิตด้วยตัวอย่างที่น่าเชื่อถือว่าจริง ๆ แล้วพวกเขาสามารถสร้างรหัส ARM ที่ดีที่สุดจาก Delphi XEx ได้อย่างไรฉันยินดีที่จะตอบ .
คอมไพเลอร์ Delphi ของ Embarcadero ใช้แบ็กเอนด์ LLVM เพื่อสร้างรหัส ARM ดั้งเดิมสำหรับอุปกรณ์ Android ฉันมีรหัส Pascal จำนวนมากที่ฉันต้องรวบรวมในแอปพลิเคชัน Android และฉันต้องการทราบวิธีการทำให้ Delphi สร้างรหัสที่มีประสิทธิภาพมากขึ้น ตอนนี้ฉันไม่ได้พูดถึงคุณสมบัติขั้นสูงเช่นการเพิ่มประสิทธิภาพ SIMD อัตโนมัติเพียงแค่การสร้างรหัสที่สมเหตุสมผล แน่นอนว่าจะต้องมีวิธีการส่งผ่านพารามิเตอร์ไปยังด้าน LLVM หรือส่งผลต่อผลลัพธ์อย่างไร โดยทั่วไปคอมไพเลอร์ใด ๆ จะมีตัวเลือกมากมายที่จะส่งผลกระทบต่อการคอมไพล์และการปรับให้เหมาะสม แต่เป้าหมาย ARM ของ Delphi ดูเหมือนจะเป็นเพียง "การเพิ่มประสิทธิภาพเปิด / ปิด" และนั่นก็คือ
LLVM น่าจะมีความสามารถในการสร้างรหัสที่สมเหตุสมผลและสมเหตุสมผล แต่ดูเหมือนว่า Delphi กำลังใช้สิ่งอำนวยความสะดวกในลักษณะแปลก ๆ Delphi ต้องการใช้สแต็คอย่างหนักมากและโดยทั่วไปจะใช้การลงทะเบียนของโปรเซสเซอร์ r0-r3 เป็นตัวแปรชั่วคราวเท่านั้น บางทีสิ่งที่น่าประหลาดใจที่สุดก็คือดูเหมือนว่าจะโหลดจำนวนเต็ม 32 บิตปกติเป็นการดำเนินการโหลด 1 ไบต์สี่ครั้ง จะทำให้ Delphi ผลิตโค้ด ARM ที่ดีขึ้นได้อย่างไรและโดยไม่ต้องยุ่งยากกับไบต์ต่อไบต์ที่สร้างขึ้นสำหรับ Android
ตอนแรกฉันคิดว่าการโหลดไบต์เป็นไบต์สำหรับการแลกเปลี่ยนไบต์จาก big-endian แต่นั่นไม่ใช่กรณีจริง ๆ เพียงแค่โหลดเลข 32 บิตพร้อมโหลดแบบไบต์เดียว 4 ตัว * อาจเป็นการโหลด เต็ม 32 บิตโดยไม่ต้องโหลดหน่วยความจำขนาดเท่าคำที่ไม่ได้จัดแนว (ไม่ว่าจะเป็นควรหลีกเลี่ยงนั่นเป็นอีกสิ่งหนึ่งซึ่งจะบ่งบอกถึงสิ่งทั้งหมดที่เป็นข้อบกพร่องของคอมไพเลอร์) *
ลองดูฟังก์ชั่นง่าย ๆ นี้:
function ReadInteger(APInteger : PInteger) : Integer;
begin
Result := APInteger^;
end;
แม้ว่าจะเปิดใช้งานการปรับให้เหมาะสม Delphi XE7 พร้อมด้วยชุดการอัปเดต 1 เช่นเดียวกับ 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 และสร้างจำนวนเต็ม 32 บิตจาก 4 ไบต์เดียวโหลด ... ถ้าฉันเปลี่ยนฟังก์ชั่นเล็กน้อยและใช้พารามิเตอร์ 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 และพวกเขาเกือบจะ แต่ไม่เหมือนกับรุ่นพารามิเตอร์ var Android แก้ไข: เพื่อให้ชัดเจนยิ่งขึ้นการโหลดแบบไบต์ต่อไบต์มีเฉพาะใน Android และสำหรับ Android เท่านั้นเวอร์ชันของตัวชี้และพารามิเตอร์ var แตกต่างจากกัน บน iOS ทั้งสองรุ่นจะสร้างรหัสเดียวกันทั้งหมด
สำหรับการเปรียบเทียบนี่เป็นสิ่งที่ FPC 2.7.1 (เวอร์ชั่น trunk SVN จากมีนาคม 2014) คิดว่าของฟังก์ชั่นที่มีระดับการเพิ่มประสิทธิภาพ -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
ฉันยังทดสอบฟังก์ชั่น C ที่เทียบเท่ากับคอมไพเลอร์ C ที่มาพร้อมกับ Android NDK
int ReadInteger(int *APInteger)
{
return *APInteger;
}
และสิ่งนี้ก็รวมกันเป็นสิ่งเดียวกันกับที่ FPC ทำขึ้น:
Disassembly of section .text._Z11ReadIntegerPi:
00000000 <_Z11ReadIntegerPi>:
0: 6800 ldr r0, [r0, #0]
2: 4770 bx lr
armeabi-v7a
แทนarmeabi
(ไม่แน่ใจว่ามีตัวเลือกดังกล่าวในคอมไพเลอร์นี้) ตั้งแต่โหลด unaligned ควรได้รับการสนับสนุนตั้งแต่ ARMv6 (ในขณะที่armeabi
ถือว่า ARMv5) (ที่แสดงการถอดชิ้นส่วนดูไม่เหมือนมันอ่านค่า bigendian ก็แค่อ่านค่าน้อย endian หนึ่งไบต์ในเวลา.)