วิธีบีบรหัสสำหรับ Flash และ RAM เพิ่มเติม [ปิด]


14

ฉันทำงานเกี่ยวกับการพัฒนาฟีเจอร์สำหรับผลิตภัณฑ์เฉพาะของเรา มีการร้องขอให้ย้ายฟีเจอร์เดียวกันไปยังผลิตภัณฑ์อื่น ผลิตภัณฑ์นี้ใช้ไมโครคอนโทรลเลอร์ M16C ซึ่งมี 64K Flash และ RAM 2k

มันเป็นผลิตภัณฑ์สำหรับผู้ใหญ่และดังนั้นจึงเหลือ 132 ไบต์ของแฟลชและเหลือ 2 ไบต์ของ RAM

ในการพอร์ตคุณสมบัติที่ร้องขอ (คุณสมบัติได้รับการปรับให้เหมาะสมแล้ว) ฉันต้องการ 1,400 ไบต์ของแฟลชและ RAM ขนาด 200 ~

ไม่มีใครมีข้อเสนอแนะใด ๆ เกี่ยวกับวิธีการดึงข้อมูลไบต์เหล่านี้ด้วยการกระชับโค้ด? ฉันจะมองหาสิ่งที่เฉพาะเจาะจงเมื่อฉันพยายามที่จะกระชับรหัสการทำงานที่มีอยู่แล้ว?

ความคิดใด ๆ ที่จะได้รับการชื่นชมจริงๆ

ขอบคุณ


1
ขอบคุณทุกคนสำหรับคำแนะนำ ฉันจะแจ้งให้คุณทราบถึงความคืบหน้าของฉันและทำรายการขั้นตอนที่ได้ผลและที่ไม่ได้ทำ
IntelliChick

ตกลงดังนั้นนี่คือสิ่งที่ฉันได้ลองใช้งานได้: เลื่อนรุ่นคอมไพเลอร์ การเพิ่มประสิทธิภาพได้พัฒนาขึ้นอย่างมากซึ่งทำให้ฉันมี Flash ประมาณ 2K เข้าสู่รายการไฟล์เพื่อตรวจสอบฟังก์ชันการทำงานที่ซ้ำซ้อนและไม่ได้ใช้งาน (สืบทอดมาเพราะฐานรหัสทั่วไป) สำหรับผลิตภัณฑ์เฉพาะและได้รับ Flash มากขึ้น
IntelliChick

สำหรับ RAM ฉันทำสิ่งต่อไปนี้: เดินผ่านไฟล์แผนที่เพื่อตรวจสอบฟังก์ชั่น / โมดูลที่ใช้ RAM ส่วนใหญ่ ฉันพบฟังก์ชั่นที่หนักมาก (12 ช่องสัญญาณแต่ละอันมีจำนวนหน่วยความจำที่จัดสรรไว้แล้ว) ของรหัสดั้งเดิมเข้าใจว่ามันพยายามทำอะไรบ้างและเพิ่มประสิทธิภาพการใช้ RAM โดยการแบ่งปันข้อมูลระหว่างช่องทางที่เป็นเรื่องธรรมดา สิ่งนี้ให้ฉัน ~ 200 ไบต์ที่ฉันต้องการ
IntelliChick

หากคุณมีไฟล์ ASCII คุณสามารถใช้การบีบอัด 8 ถึง 7 บิต ช่วยให้คุณประหยัด 12.5% การใช้ไฟล์ zip จะใช้รหัสเพิ่มเติมในการ zip และยกเลิกการซิปมากกว่าให้ปล่อยไว้
Sparky256

คำตอบ:


18

คุณมีสองทางเลือก: อันดับแรกคือมองหารหัสที่ซ้ำซ้อนและย้ายไปที่สายเดียวเพื่อกำจัดการซ้ำซ้อน ประการที่สองคือการลบฟังก์ชั่น

ลองดูที่ไฟล์. map และดูว่ามีฟังก์ชั่นที่คุณสามารถกำจัดหรือเขียนซ้ำได้หรือไม่ ตรวจสอบให้แน่ใจว่าการเรียกใช้ไลบรารีที่จำเป็นต้องใช้จริง ๆ

สิ่งต่าง ๆ เช่นการหารและการคูณสามารถนำมาในรหัสจำนวนมาก แต่การใช้กะและการใช้ค่าคงที่ดีกว่าสามารถทำให้รหัสมีขนาดเล็กลง ดูสิ่งต่าง ๆ เช่นค่าคงที่สตริงและprintfs ตัวอย่างเช่นแต่ละคนprintfจะกินรอมของคุณ แต่คุณอาจมีสตริงรูปแบบที่ใช้ร่วมกันสองชุดแทนที่จะทำซ้ำค่าคงที่ของสตริงนั้นซ้ำแล้วซ้ำอีก

สำหรับหน่วยความจำดูว่าคุณสามารถกำจัดกลมและใช้รถยนต์ในฟังก์ชั่นแทน ยังหลีกเลี่ยงตัวแปรจำนวนมากในฟังก์ชั่นหลักที่เป็นไปได้เพราะสิ่งเหล่านี้กินหน่วยความจำเช่นเดียวกับ globals


1
ขอบคุณสำหรับคำแนะนำฉันสามารถลองใช้ส่วนใหญ่ได้อย่างแน่นอนยกเว้นข้อแนะนำสำหรับค่าคงที่สตริง มันเป็นอุปกรณ์ที่ฝังตัวอย่างหมดจดโดยไม่มี UI และดังนั้นจึงไม่มีการเรียกไปที่ printf () ภายในรหัส หวังว่าคำแนะนำเหล่านั้นจะช่วยให้ฉันได้รับ 1,400 Bytes Flash ของฉัน / 200 bytes RAM ที่ฉันต้องการ
IntelliChick

1
@IntelliChick คุณจะประหลาดใจกับจำนวนผู้ใช้ printf () ภายในอุปกรณ์ฝังตัวเพื่อพิมพ์สำหรับการดีบักหรือส่งไปยังอุปกรณ์ต่อพ่วง ดูเหมือนว่าคุณจะรู้ดีกว่านี้ แต่ถ้าใครเขียนโค้ดในโครงการไว้ข้างหน้าคุณมันจะไม่เป็นอันตรายอะไรที่จะตรวจสอบมัน
Kellenjb

5
และเพื่อขยายความคิดเห็นก่อนหน้าของฉันคุณจะประหลาดใจกับจำนวนผู้ที่เพิ่มคำสั่งการดีบัก แต่อย่าลบพวกเขาออก แม้แต่คนที่ทำ #ifdefs ก็ยังขี้เกียจอยู่ตลอดเวลา
Kellenjb

1
เยี่ยมมากขอบคุณ! ฉันได้รับรหัสฐานนี้แล้วดังนั้นฉันจึงควรมองหาสิ่งเหล่านั้น ฉันจะทำให้พวกคุณโพสต์อยู่ระหว่างดำเนินการและพยายามติดตามจำนวนหน่วยความจำหรือแฟลชที่ฉันได้รับจากการทำเช่นเดียวกับการอ้างอิงสำหรับใครก็ตามที่อาจจำเป็นต้องทำสิ่งนี้ในอนาคต
IntelliChick

แค่คำถามนี้ - สิ่งที่เกี่ยวกับการเรียกฟังก์ชั่นที่ซ้อนกันกระโดดจากชั้นหนึ่งไปอีกชั้น นั่นเพิ่มค่าใช้จ่ายเท่าไหร่? มันจะดีกว่าที่จะรักษา modularity โดยมีการเรียกใช้ฟังก์ชั่นหลายรายการหรือลดการเรียกใช้ฟังก์ชั่นและบันทึกบางไบต์ และนั่นสำคัญหรือไม่
IntelliChick

8

มันคุ้มค่าที่จะดูการแสดงรายการไฟล์ (แอสเซมเบลอร์) เพื่อค้นหาสิ่งที่คอมไพเลอร์เฉพาะของคุณไม่ดีโดยเฉพาะ

ตัวอย่างเช่นคุณอาจพบว่าตัวแปรโลคัลมีราคาแพงมากและหากแอปพลิเคชันนั้นง่ายพอที่จะคุ้มค่ากับความเสี่ยงการย้ายลูปตัวนับสองสามตัวไปเป็นตัวแปรแบบสแตติกอาจบันทึกโค้ดจำนวนมาก

หรือการจัดทำดัชนีอาร์เรย์อาจมีราคาแพงมาก แต่การดำเนินการตัวชี้ถูกกว่ามาก หรือในทางกลับกัน

แต่การดูภาษาแอสเซมบลีเป็นขั้นตอนแรก


3
มันสำคัญมากที่คุณจะรู้ว่าคอมไพเลอร์ของคุณทำอะไร คุณควรดูว่าตัวแบ่งของฉันคืออะไร มันทำให้เด็กร้องไห้ (รวมตัวเอง)
Kortuk

8

การปรับให้เหมาะสมของคอมไพเลอร์เช่น-Osใน GCC ให้ความสมดุลที่ดีที่สุดระหว่างความเร็วและขนาดโค้ด หลีกเลี่ยง-O3เนื่องจากอาจเพิ่มขนาดรหัส


3
หากคุณทำเช่นนี้คุณจะต้องสอบใหม่ทุกอย่าง! การปรับให้เหมาะสมอาจทำให้รหัสการทำงานไม่สามารถใช้งานได้เนื่องจากมีสมมติฐานใหม่ที่คอมไพเลอร์สร้างขึ้น
Robert

@Robert นั่นเป็นจริงหากคุณใช้คำสั่งที่ไม่ได้กำหนด: เช่น a = a ++ จะรวบรวมใน -O0 และ -O3 ต่างกัน
โทมัสโอ

5
@ โทมัสไม่เป็นความจริง หากคุณมีการวนรอบเพื่อหน่วงรอบสัญญาณนาฬิกาตัวเพิ่มประสิทธิภาพจำนวนมากจะรู้ว่าคุณไม่ได้ทำอะไรเลย นี่เป็นเพียง 1 ตัวอย่าง
Kellenjb

1
@thomas O คุณต้องให้แน่ใจว่าคุณระมัดระวังเกี่ยวกับคำจำกัดความของฟังก์ชั่นที่เปลี่ยนแปลงได้ เครื่องมือเพิ่มประสิทธิภาพจะระเบิดผู้ที่คิดว่าพวกเขารู้จัก C ดี แต่ไม่เข้าใจความซับซ้อนของการปฏิบัติการปรมาณู
Kortuk

1
ทุกจุดที่ดี ฟังก์ชั่น / ตัวแปรระเหยตามนิยามจะต้องไม่ได้รับการปรับให้เหมาะสม เครื่องมือเพิ่มประสิทธิภาพใด ๆ ที่ดำเนินการเพิ่มประสิทธิภาพในเช่น (รวมถึงเวลาโทรและอินไลน์) จะใช้งานไม่ได้
โทมัสโอ

8

สำหรับ RAM ให้ตรวจสอบช่วงของตัวแปรทั้งหมดของคุณ - คุณใช้ ints ซึ่งคุณสามารถใช้ถ่านได้หรือไม่? บัฟเฟอร์ใหญ่กว่าที่พวกเขาต้องการหรือไม่

การบีบอัดรหัสเป็นแอพพลิเคชั่นมากและขึ้นอยู่กับรูปแบบการเข้ารหัส จำนวนเงินที่เหลือของคุณแนะนำว่าอาจรหัสได้ผ่านไปแล้วแม้ว่าจะมีการบีบบางอย่างซึ่งอาจหมายความว่ามีเหลืออีกเล็กน้อย

ดูการใช้งานโดยรวม - มีบางอย่างที่ไม่ได้ใช้จริง ๆ และสามารถถูกโยนทิ้งได้หรือไม่?


8

หากเป็นโครงการเก่า แต่คอมไพเลอร์ได้รับการพัฒนาตั้งแต่นั้นอาจเป็นไปได้ว่าคอมไพเลอร์ล่าสุดอาจผลิตรหัสขนาดเล็กลง


ขอบคุณไมค์! ฉันเคยลองสิ่งนี้ในอดีตและพื้นที่ที่ได้รับได้ถูกใช้ไปแล้ว! :) เลื่อนขึ้นจาก IAR C คอมไพเลอร์ 3.21d เป็น 3.40
IntelliChick

1
ฉันย้ายรุ่นเพิ่มขึ้นอีกหนึ่งเวอร์ชันและจัดการเพื่อให้มี Flash เพิ่มขึ้นเพื่อให้พอดีกับคุณลักษณะระบบฉันกำลังดิ้นรนกับ RAM จริง ๆ ซึ่งยังคงไม่เปลี่ยนแปลง :(
IntelliChick

7

การตรวจสอบคู่มือคอมไพเลอร์ของคุณเป็นสิ่งที่คุ้มค่าเสมอ

สำหรับ gcc -ffunction-sectionsและ-fdata-sectionsด้วยการ--gc-sectionsเชื่อมโยงธงเป็นสิ่งที่ดีสำหรับการปอกรหัสตาย

นี่คือเคล็ดลับที่ยอดเยี่ยมอื่น ๆ(มุ่งสู่ AVR)


ใช้งานได้จริงหรือ เอกสารกล่าวว่า "เมื่อคุณระบุตัวเลือกเหล่านี้แอสเซมเบลอร์และตัวเชื่อมโยงจะสร้างวัตถุและไฟล์ที่สามารถเรียกทำงานได้ที่ใหญ่ขึ้นและจะช้าลงด้วย" ฉันเข้าใจว่าการมีส่วนแยกต่างหากเหมาะสมสำหรับไมโครที่มีส่วนแฟลชและ RAM - คำสั่งนี้ในเอกสารไม่สามารถใช้ได้กับไมโครคอนโทรลเลอร์หรือไม่?
Kevin Vermeer

ประสบการณ์ของฉันคือมันทำงานได้ดีสำหรับ AVR
Toby Jaffey

1
มันใช้งานไม่ได้กับคอมไพเลอร์ส่วนใหญ่ที่ฉันใช้ มันเหมือนกับการใช้คีย์เวิร์ด register คุณสามารถบอกคอมไพเลอร์ว่าตัวแปรเข้าสู่การลงทะเบียน แต่เครื่องมือเพิ่มประสิทธิภาพที่ดีจะทำสิ่งนี้ได้ดีกว่ามนุษย์มาก (เถียงว่าบางคนอาจถือว่าเป็นการยอมรับที่ไม่สามารถทำได้ในทางปฏิบัติ)
Kortuk

1
เมื่อคุณเริ่มกำหนดสถานที่คุณกำลังบังคับให้คอมไพเลอร์วางสิ่งของในบางตำแหน่งสำคัญมากสำหรับโค้ดบูตโหลดเดอร์ขั้นสูง แต่น่ากลัวที่จะจัดการกับเครื่องมือเพิ่มประสิทธิภาพในขณะที่คุณตัดสินใจขั้นตอนของการปรับให้เหมาะสม ทำได้ ในคอมไพเลอร์บางตัวพวกเขาออกแบบให้มีส่วนสำหรับรหัสที่ใช้สำหรับกรณีนี้เป็นการบอกให้คอมไพเลอร์ทราบข้อมูลเพิ่มเติมเพื่อทำความเข้าใจการใช้งานของคุณซึ่งจะช่วยได้ หากคอมไพเลอร์ไม่แนะนำให้ทำอย่าทำ
Kortuk

6

คุณสามารถตรวจสอบจำนวนพื้นที่สแต็กและพื้นที่ฮีปที่จัดสรร คุณอาจได้รับ RAM เป็นจำนวนมากหากมีการจัดสรรอย่างใดอย่างหนึ่งหรือทั้งสองอย่าง

ฉันเดาสำหรับโครงการที่เหมาะสมใน 2k ของแรมจะเริ่มต้นด้วยไม่มีการจัดสรรหน่วยความจำแบบไดนามิก (ใช้malloc, callocฯลฯ ) หากเป็นกรณีนี้คุณสามารถกำจัดฮีปของคุณไปด้วยกันโดยสมมติว่าผู้เขียนต้นฉบับทิ้งแรมไว้บางส่วนสำหรับฮีป

คุณต้องระมัดระวังอย่างมากในการลดขนาดสแต็กเนื่องจากอาจทำให้เกิดข้อบกพร่องที่หาได้ยาก อาจเป็นประโยชน์ในการเริ่มต้นด้วยการเริ่มต้นพื้นที่สแต็กทั้งหมดให้เป็นค่าที่รู้จัก (สิ่งอื่นนอกเหนือจาก 0x00 หรือ 0xff เนื่องจากค่าเหล่านี้เกิดขึ้นโดยทั่วไปแล้ว) จากนั้นเรียกใช้ระบบในขณะที่ดูจำนวนสแตกพื้นที่ที่ไม่ได้ใช้


นี่เป็นตัวเลือกที่ดีมาก ฉันจะทราบว่าคุณไม่ควรใช้ malloc ในระบบฝังตัว
Kortuk

1
@Kortuk นั้นขึ้นอยู่กับคำจำกัดความของคุณเกี่ยวกับการฝังตัวและงานที่กำลังดำเนินการ
Toby Jaffey

1
@joby ใช่ฉันเข้าใจว่า ในระบบที่มีการรีสตาร์ท 0 และการไม่มี OS เช่น linux นั้น Malloc อาจเลวร้ายมาก
Kortuk

ไม่มีการจัดสรรหน่วยความจำแบบไดนามิกไม่มีตำแหน่งที่ malloc, calloc กำลังถูกใช้ ฉันได้ตรวจสอบการจัดสรรฮีปแล้วและตั้งค่าเป็น 0 แล้วดังนั้นจึงไม่มีการจัดสรรฮีป ขนาดสแต็กที่จัดสรรในปัจจุบันคือ 254 ไบต์และขนาดสแต็กอินเทอร์รัปต์เป็น 128 ไบต์
IntelliChick

5

รหัสของคุณใช้เลขทศนิยมหรือไม่? คุณสามารถนำอัลกอริทึมของคุณกลับมาใช้ใหม่ได้โดยใช้เลขจำนวนเต็มเท่านั้นและกำจัดค่าโสหุ้ยในการใช้ไลบรารี C floating point เช่นในบางแอปพลิเคชันฟังก์ชันเช่น sine, log, exp สามารถถูกแทนที่ด้วยการประมาณพหุนามจำนวนเต็ม

รหัสของคุณใช้ตารางค้นหาขนาดใหญ่สำหรับอัลกอริทึมใด ๆ เช่นการคำนวณ CRC หรือไม่ คุณสามารถลองแทนที่อัลกอริทึมรุ่นอื่นที่คำนวณค่าได้ทันทีแทนที่จะใช้ตารางค้นหา ข้อแม้คืออัลกอริธึมที่เล็กกว่านั้นช้ากว่าดังนั้นโปรดตรวจสอบให้แน่ใจว่าคุณมีรอบการทำงานของ CPU ที่เพียงพอ

รหัสของคุณมีข้อมูลคงที่จำนวนมากเช่นตารางสตริงหน้า HTML หรือกราฟิกพิกเซล (ไอคอน) หรือไม่ ถ้ามันมีขนาดใหญ่พอ (พูดถึง 10 kB) มันอาจคุ้มค่าที่จะใช้รูปแบบการบีบอัดที่ง่ายมากเพื่อลดขนาดของข้อมูลและขยายขนาดเมื่อจำเป็น


มีตารางการค้นหาขนาดเล็ก 2 ตารางซึ่งไม่น่าจะมีค่าเท่ากับ 10K และไม่มีการใช้คณิตศาสตร์เลขทศนิยม . :( ขอบคุณสำหรับคำแนะนำแม้ว่าพวกเขาจะดี.
IntelliChick

2

คุณสามารถลองจัดเรียงรหัสใหม่ให้มีขนาดกะทัดรัดขึ้น มันขึ้นอยู่กับว่าโค้ดกำลังทำอะไรอยู่มากมาย กุญแจสำคัญคือการค้นหาสิ่งที่คล้ายกันและนำไปใช้ใหม่ในแง่ของกันและกัน สุดขีดจะใช้ภาษาระดับที่สูงขึ้นเช่น Forth ซึ่งจะง่ายต่อการบรรลุความหนาแน่นของรหัสที่สูงกว่าใน C หรือแอสเซมเบลอร์

นี่คือForth สำหรับ M16C


2

ตั้งค่าระดับการปรับให้เหมาะสมของคอมไพเลอร์ IDE หลายตัวมีการตั้งค่าที่อนุญาตให้ปรับขนาดโค้ดได้โดยเสียเวลารวบรวม (หรืออาจใช้เวลาประมวลผลในบางกรณี) พวกเขาสามารถทำให้การบีบอัดโค้ดทำได้โดยใช้เครื่องมือเพิ่มประสิทธิภาพของพวกเขาสองครั้งค้นหารูปแบบที่สามารถเพิ่มประสิทธิภาพที่พบได้น้อยลงและเทคนิคอื่น ๆ อีกมากมายที่อาจไม่จำเป็นสำหรับการคอมไพล์แคช / ดีบัก โดยปกติแล้วคอมไพเลอร์จะถูกตั้งค่าให้อยู่ในระดับที่เหมาะสมของการเพิ่มประสิทธิภาพ ขุดรอบในการตั้งค่าที่คุณควรจะสามารถหาระดับการเพิ่มประสิทธิภาพตามจำนวนเต็ม


1
ปัจจุบันได้รับการปรับให้เหมาะสมกับขนาดสูงสุด :) ขอบคุณสำหรับคำแนะนำว่า :)
IntelliChick

2

หากคุณใช้คอมไพเลอร์ระดับมืออาชีพเช่น IAR อยู่แล้วฉันคิดว่าคุณจะต้องดิ้นรนเพื่อให้เกิดการประหยัดอย่างจริงจังด้วยการปรับแต่งโค้ดระดับล่างเล็กน้อย - คุณจะต้องมองหาวิธีกำจัดฟังก์ชั่นหรือทำเรื่องสำคัญ ๆ เขียนชิ้นส่วนใหม่อย่างมีประสิทธิภาพมากขึ้น คุณจะต้องเป็น coder ที่ฉลาดกว่าใครก็ตามที่เขียนเวอร์ชันดั้งเดิม ... สำหรับ RAM คุณต้องดูอย่างจริงจังว่ามันใช้งานอย่างไรในปัจจุบันและดูว่ามีขอบเขตสำหรับการใช้งาน RAM ซ้อนทับกันหรือไม่สำหรับ สิ่งต่าง ๆ ในเวลาต่าง ๆ (สหภาพมีประโยชน์สำหรับเรื่องนี้) ฮีปและขนาดสแต็คเริ่มต้นของ IAR ใน ARM / AVR ที่ฉันมีแนวโน้มจะใจกว้างเกินไปดังนั้นสิ่งเหล่านี้จะเป็นสิ่งแรกที่ต้องพิจารณา


ขอบคุณไมค์ รหัสกำลังใช้สหภาพอยู่แล้วในสถานที่ส่วนใหญ่ แต่ฉันจะดูสถานที่อื่น ๆ ซึ่งอาจช่วยได้ ฉันจะดูขนาดสแต็คที่เลือกและดูว่าสามารถปรับให้เหมาะสมได้ทั้งหมดหรือไม่
IntelliChick

ฉันจะรู้ได้อย่างไรว่าขนาดของสแต็กขนาดใดที่เหมาะสม
IntelliChick

2

อย่างอื่นในการตรวจสอบ - คอมไพเลอร์บางอย่างเกี่ยวกับสถาปัตยกรรมบางคัดลอกคงแรม - โดยปกติจะใช้เมื่อมีการเข้าถึงการคงแฟลชช้า / ยาก (เช่น AVR) เช่น IAR ของ AVR คอมไพเลอร์ต้องมี _ _flash qualifer จะไม่คัดลอกอย่างต่อเนื่องเพื่อแรม)


ขอบคุณไมค์ ใช่ฉันได้ตรวจสอบแล้ว - มันเรียกว่าตัวเลือก 'ค่าคงที่เขียนได้' สำหรับคอมไพเลอร์ M16C IAR C มันจะคัดลอกค่าคงที่จาก ROM ไปยัง RAM ตัวเลือกนี้ไม่ถูกตรวจสอบสำหรับโครงการของฉัน แต่การตรวจสอบที่ถูกต้องจริงๆ! ขอบคุณ
IntelliChick

1

หากตัวประมวลผลของคุณไม่ได้รับการสนับสนุนฮาร์ดแวร์สำหรับพารามิเตอร์ / สแต็กท้องถิ่น แต่คอมไพเลอร์พยายามที่จะใช้กองซ้อนพารามิเตอร์ทำงานอยู่แล้วและถ้ารหัสของคุณไม่จำเป็นต้องเข้าใหม่คุณอาจจะสามารถบันทึกรหัส พื้นที่โดยการจัดสรรตัวแปรอัตโนมัติแบบคงที่ ในบางกรณีต้องดำเนินการด้วยตนเอง ในกรณีอื่น ๆ คำสั่งคอมไพเลอร์สามารถทำได้ การจัดสรรด้วยตนเองที่มีประสิทธิภาพจะต้องมีการแชร์ตัวแปรระหว่างรูทีน การแชร์ดังกล่าวจะต้องทำอย่างระมัดระวังเพื่อให้แน่ใจว่าไม่มีรูทีนใช้ตัวแปรที่รูทีนอื่นพิจารณาว่าเป็น "อยู่ในขอบเขต" แต่ในบางกรณีผลประโยชน์ที่มีขนาดรหัสอาจมีความสำคัญ

โปรเซสเซอร์บางตัวมีการเรียกประชุมที่อาจทำให้รูปแบบการส่งพารามิเตอร์บางอย่างมีประสิทธิภาพมากกว่าคนอื่น ๆ ตัวอย่างเช่นในคอนโทรลเลอร์ PIC18 หากรูทีนใช้พารามิเตอร์ one-byte เดียวมันอาจถูกส่งผ่านในรีจิสเตอร์ หากใช้มากกว่านั้นพารามิเตอร์ทั้งหมดจะต้องถูกส่งผ่านใน RAM หากรูทีนจะใช้พารามิเตอร์หนึ่งไบต์สองพารามิเตอร์อาจมีประสิทธิภาพมากที่สุดในการ "ผ่าน" หนึ่งในตัวแปรส่วนกลางแล้วส่งผ่านพารามิเตอร์อื่นเป็นพารามิเตอร์ ด้วยกิจวัตรที่ใช้กันอย่างแพร่หลายการประหยัดสามารถเพิ่มขึ้น อาจมีความสำคัญอย่างยิ่งหากพารามิเตอร์ที่ส่งผ่าน global เป็นแฟล็กบิตเดียวหรือหากโดยปกติจะมีค่าเป็น 0 หรือ 255 (เนื่องจากมีคำแนะนำพิเศษในการจัดเก็บ 0 หรือ 255 ไว้ใน RAM)

บน ARM การวางตัวแปรส่วนกลางที่ใช้บ่อยในโครงสร้างอาจลดขนาดรหัสอย่างมีนัยสำคัญและปรับปรุงประสิทธิภาพ ถ้า A, B, C, D และ E เป็นตัวแปรส่วนกลางที่แยกกันดังนั้นรหัสที่ใช้ทั้งหมดจะต้องโหลดที่อยู่ของแต่ละรายการลงในการลงทะเบียน หากมีการลงทะเบียนไม่เพียงพออาจจำเป็นต้องโหลดที่อยู่เหล่านั้นซ้ำหลายครั้ง ในทางตรงกันข้ามหากพวกเขาเป็นส่วนหนึ่งของโครงสร้างทั่วโลกเดียวกัน MyStuff แล้วรหัสที่ใช้ MyStuff.A, MyStuff.B ฯลฯ สามารถโหลดที่อยู่ของ MyStuff เพียงครั้งเดียว ชนะครั้งใหญ่


1

1. หากรหัสของคุณต้องอาศัยโครงสร้างจำนวนมากตรวจสอบให้แน่ใจว่าสมาชิกโครงสร้างได้รับคำสั่งจากหน่วยความจำส่วนใหญ่ให้น้อยที่สุด

ตัวอย่าง: "uint32_t uint16_t uint8_t" แทน "uint16_t uint8_t uint32_t"

สิ่งนี้จะช่วยให้มั่นใจว่าโครงสร้างขั้นต่ำ

2. ใช้ const สำหรับตัวแปรที่เกี่ยวข้อง เพื่อให้มั่นใจว่าตัวแปรเหล่านั้นจะอยู่ใน ROM และไม่กิน RAM


1

เทคนิคเล็กน้อย (อาจชัดเจน) ที่ฉันใช้ประสบความสำเร็จในการบีบอัดรหัสลูกค้าบางส่วน:

  1. กระชับแฟล็กลงในฟิลด์บิตหรือบิตมาสก์ สิ่งนี้อาจเป็นประโยชน์เพราะโดยปกติแล้ว booleans จะถูกเก็บเป็นจำนวนเต็มดังนั้นการสูญเสียความทรงจำ สิ่งนี้จะบันทึกทั้งแรมและ ROM และคอมไพเลอร์มักไม่ได้ทำ

  2. ค้นหาความซ้ำซ้อนในรหัสและใช้ลูปหรือฟังก์ชั่นเพื่อดำเนินการคำสั่งซ้ำ

  3. ฉันยังบันทึก ROM บางส่วนโดยแทนที่if(x==enum_entry) <assignment>คำสั่งจำนวนมากจากค่าคงที่ด้วยอาร์เรย์ที่จัดทำดัชนีโดยดูแลว่ารายการ enum สามารถใช้เป็นดัชนีอาร์เรย์ได้


0

หากทำได้ให้ใช้ฟังก์ชั่นอินไลน์หรือมาโครคอมไพเลอร์แทนฟังก์ชั่นเล็ก ๆ มีค่าใช้จ่ายขนาดและความเร็วด้วยการส่งผ่านข้อโต้แย้งและสามารถแก้ไขได้โดยการทำให้ฟังก์ชั่นอินไลน์


1
ผู้รวบรวมที่ดีควรทำสิ่งนี้โดยอัตโนมัติสำหรับ funcitons ที่ถูกเรียกเพียงครั้งเดียว
mikeselectricstuff

5
ฉันพบว่าการใช้งานอินไลน์มักจะมีประโยชน์มากกว่าสำหรับการปรับความเร็วให้เหมาะสมที่สุดและโดยปกติแล้วจะมีขนาดเพิ่มขึ้น
Craig McQueen

โดยทั่วไปการทำอินไลน์จะเพิ่มขนาดรหัสยกเว้นฟังก์ชันที่น่าสนใจเช่นint get_a(struct x) {return x.a;}
Dmitry Grigoryev

0

เปลี่ยนตัวแปรโลคัลให้มีขนาดเท่ากันกับ CPU ของคุณ

หาก CPU เป็น 32 บิตให้ใช้ตัวแปร 32 บิตแม้ว่าค่าสูงสุดจะไม่ได้รับเกิน 255 ผมใช้ตัวแปร 8 บิตคอมไพเลอร์จะเพิ่มรหัสเพื่อปกปิดรหัส 24 บิตด้านบน

ที่แรกที่ฉันจะดูคือตัวแปร for-loop

for( i = 0; i < 100; i++ )

นี่อาจดูเหมือนเป็นสถานที่ที่ดีสำหรับตัวแปร 8 บิต แต่ตัวแปร 32 บิตอาจสร้างโค้ดน้อยลง


นั่นอาจบันทึกรหัส แต่มันจะกิน RAM
mikeselectricstuff

มันจะกิน RAM ถ้าการเรียกใช้ฟังก์ชันนั้นอยู่ในสาขาที่ยาวที่สุดของการติดตามการโทร มิฉะนั้นจะใช้พื้นที่สแต็กที่ฟังก์ชันอื่นต้องการอยู่แล้ว
Robert

2
มักจะให้ความจริงมันเป็นตัวแปรท้องถิ่น ถ้าสั้น ๆ กับ RAM ขนาดของ vars ทั่วโลกโดยเฉพาะอย่างยิ่งอาร์เรย์เป็นสถานที่ที่ดีในการเริ่มมองหาการประหยัด
mikeselectricstuff

1
ความเป็นไปได้อีกประการหนึ่งที่น่าสนใจคือการแทนที่ตัวแปรที่ไม่ได้ลงนามด้วยการเซ็นชื่อ หากคอมไพเลอร์ปรับให้สั้นที่สุดที่ไม่ได้ลงชื่อกับการลงทะเบียนแบบ 32 บิตนั้นจะต้องเพิ่มรหัสเพื่อให้แน่ใจว่าค่าของมันตัดจาก 65535 เป็นศูนย์ อย่างไรก็ตามหากคอมไพเลอร์ปรับแต่งลายเซ็นที่สั้นที่สุดให้กับการลงทะเบียนไม่จำเป็นต้องใช้รหัสดังกล่าว เนื่องจากไม่มีการรับประกันว่าจะเกิดอะไรขึ้นหากมีการเพิ่มจำนวนสั้น ๆ เกิน 32767 คอมไพเลอร์จึงไม่จำเป็นต้องปล่อยรหัสเพื่อจัดการกับสิ่งนั้น ในคอมไพเลอร์ ARM อย่างน้อยสองตัวที่ฉันได้ดูรหัสสั้นที่ลงนามแล้วอาจมีขนาดเล็กกว่าโค้ดสั้นที่ไม่ได้ลงชื่อเนื่องจากเหตุผลนั้น
supercat
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.