ฉันสามารถรับคืนได้เท่าไหร่ ฉันสามารถรับคืนได้เท่าไหร่ เท่าไหร่ ca! @ # QFSD @ $ RFW


19

บอร์ด Arduino Uno มี RAM จำกัด ซึ่งหมายความว่ามีสแต็กการโทรที่ จำกัด บางครั้งการเรียกซ้ำเป็นเพียงตัวเลือกด่วนในการใช้อัลกอริทึมที่แน่นอน ดังนั้นเนื่องจาก call stack มีข้อ จำกัด อย่างรุนแรงสิ่งที่จะเป็นวิธีการค้นหาว่ามีบางโปรแกรมที่ทำงานอยู่บนบอร์ดคุณสามารถซื้อได้หลายครั้งก่อนที่จะเกิด stack overflow (และสิ่งเลวร้ายเกิดขึ้น)?


2
คุณสามารถยกตัวอย่างของอัลกอริทึมที่ตัวเลือกระหว่างโซลูชันแบบเรียกซ้ำและไม่เรียกซ้ำนั้นชัดเจนได้อย่างไร
Cybergibbons

1
@Cybergibbons เมื่อเร็ว ๆ นี้ฉันต้องเขียนและทดสอบRDPในช่วงเวลาสั้น ๆ ดังนั้นฉันจึงดำเนินการเรียกใช้ซ้ำ ฉันไม่ได้บอกว่ามันเป็นไปไม่ได้ที่จะวนซ้ำมันอาจจะง่ายกว่าที่จะไปข้างหน้ากับเวอร์ชั่นซ้ำ
asheeshr

สิ่งนี้หมายความว่าอย่างไรในชื่อคำถาม: How much ca!@#QFSD@$RFW? ฉันอยากรู้ว่าทำไมไม่มีใครแก้ไขสิ่งนั้นให้มีความหมายมากกว่านี้ (ในช่วง 4 ปีที่ผ่านมา)
Nick Gammon

คำตอบคือ211เวลา (ขึ้นอยู่กับปัจจัยหลายอย่าง) :) ดูคำตอบของฉันที่นี่: arduino.stackexchange.com/a/51098/7727 @ NickGammon เขาแกล้ง "สาปแช่ง" ฉันคิดว่า มันคือการเล่นกับคำว่า "recurse" ใช้เวลาสักครู่เพื่อคิดออกเช่นกัน ตอนแรกค่อนข้างสับสน
Gabriel Staples

อ้อเข้าใจแล้ว. ฉันคิดว่าเซิร์ฟเวอร์ SE อาจมีข้อผิดพลาด :)
นิคแกมมอน

คำตอบ:


15

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

สำหรับสิ่งนี้คุณควรรู้ก่อนว่า SRAM จัดอยู่ใน Arduino ที่ใช้ AVR (จะไม่นำไปใช้กับ Arduino Galileo โดย Intel) แผนภาพต่อไปนี้จาก Adafruitแสดงให้เห็นอย่างชัดเจน:

องค์กร SRAM

จากนั้นคุณต้องรู้ขนาดโดยรวมของ SRAM ของคุณ (ขึ้นอยู่กับ Atmel MCU ดังนั้นคุณมีบอร์ด Arduino ชนิดใด)

ในแผนภาพนี้มันเป็นเรื่องง่ายที่จะหาขนาดของบล็อกข้อมูลแบบคงที่เพราะมันเป็นที่รู้จักกันในเวลารวบรวมและจะไม่เปลี่ยนแปลงในภายหลัง

กองขนาดอาจเป็นเรื่องยากมากที่จะรู้ว่ามันสามารถแตกต่างกันไปที่รันไทม์ขึ้นอยู่กับการจัดสรรหน่วยความจำแบบไดนามิก ( mallocหรือnew) ดำเนินการโดยร่างหรือไลบรารีที่ใช้ของคุณ การใช้หน่วยความจำแบบไดนามิกค่อนข้างหายากบน Arduino แต่ฟังก์ชั่นมาตรฐานบางอย่างทำ ( Stringฉันใช้ประเภทมันฉันคิดว่า)

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

ดังนั้นสมมติว่าrecurse()ฟังก์ชั่นของคุณใช้ 12 ไบต์สำหรับตัวแปรและอาร์กิวเมนต์ในเครื่องจากนั้นการเรียกใช้ฟังก์ชั่นนี้ (ครั้งแรกจากผู้เรียกภายนอกและผู้เรียกซ้ำ) จะใช้12+2ไบต์

ถ้าเราคิดว่า:

  • คุณอยู่บน Arduino UNO (SRAM = 2K)
  • ร่างของคุณไม่ได้ใช้การจัดสรรหน่วยความจำแบบไดนามิก (ไม่มีฮีป )
  • คุณรู้ขนาดของข้อมูลคงที่ของคุณ(สมมุติว่า 132 ไบต์)
  • เมื่อrecurse()ฟังก์ชันของคุณถูกเรียกจากร่างของคุณสแต็กปัจจุบันจะมีความยาว 128 ไบต์

แล้วคุณจะเหลือ2048 - 132 - 128 = 1788ไบต์ที่มีอยู่ในสแต็ค จำนวนการเรียกซ้ำไปยังฟังก์ชันของคุณจึง1788 / 14 = 127รวมถึงการโทรเริ่มต้น (ซึ่งไม่ใช่การเรียกซ้ำ)

อย่างที่คุณเห็นนี่เป็นเรื่องยากมาก แต่ไม่สามารถจะค้นหาสิ่งที่คุณต้องการได้

วิธีที่ง่ายกว่าในการเรียกขนาดสแต็กที่มีอยู่ก่อนหน้าrecurse()นี้คือการใช้ฟังก์ชั่นต่อไปนี้ (พบได้ที่ศูนย์การเรียนรู้ Adafruit; ฉันไม่ได้ทดสอบด้วยตัวเอง):

int freeRam () 
{
  extern int __heap_start, *__brkval; 
  int v; 
  return (int) &v - (__brkval == 0 ? (int) &__heap_start : (int) __brkval); 
}

ฉันขอแนะนำให้คุณอ่านบทความนี้ที่ศูนย์การเรียนรู้ Adafruit


ฉันเห็น peter-r-Bloomfield โพสต์คำตอบของเขาในขณะที่ฉันกำลังเขียนของฉัน; คำตอบของเขาดูดีขึ้นเนื่องจากอธิบายเนื้อหาของสแต็คอย่างสมบูรณ์หลังจากการโทร (ฉันลืมสถานะการลงทะเบียน)
jfpoilpret

ทั้งคำตอบที่มีคุณภาพดีมาก
Cybergibbons

ข้อมูลคงที่ = .bss + .data และสิ่งที่รายงานโดย Arduino ว่า "RAM ถ่ายโดยตัวแปรทั่วโลก" หรืออะไรก็ตามที่ถูกต้อง?
Gabriel Staples

1
@ GabrielStaples ใช่แน่นอน ในรายละเอียดเพิ่มเติม.bssแสดงถึงตัวแปรทั่วโลกโดยไม่มีค่าเริ่มต้นในรหัสของคุณในขณะที่dataสำหรับตัวแปรทั่วโลกที่มีค่าเริ่มต้น แต่ในที่สุดพวกเขาใช้พื้นที่เดียวกัน: ข้อมูลแบบคงที่ในแผนภาพ
jfpoilpret

1
@GabrielStaples ลืมสิ่งหนึ่งซึ่งในทางเทคนิคแล้วสิ่งเหล่านี้ไม่ใช่แค่ตัวแปรทั่วโลกที่ไปที่นั่นคุณยังมีตัวแปรที่ประกาศstaticในฟังก์ชัน
jfpoilpret

8

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


คอมไพเลอร์ระดับสูงบางตัวเช่น IAR (ผู้ที่สนับสนุน AVR) และ Keil (ผู้ที่ไม่สนับสนุน AVR) มีเครื่องมือที่จะช่วยคุณตรวจสอบและจัดการพื้นที่สแต็ก ไม่แนะนำให้ใช้กับบางสิ่งที่เล็กเหมือน ATmega328
Cybergibbons

7

มันขึ้นอยู่กับฟังก์ชั่น

ทุกครั้งที่มีการเรียกใช้ฟังก์ชันเฟรมใหม่จะถูกผลักลงบนสแต็ก มันมักจะมีรายการที่สำคัญต่าง ๆ รวมถึง:

  • ที่อยู่ผู้ส่ง (จุดในรหัสที่ใช้เรียกฟังก์ชัน)
  • ตัวชี้อินสแตนซ์ท้องถิ่น ( this) ถ้าเรียกใช้ฟังก์ชันสมาชิก
  • พารามิเตอร์ที่ส่งผ่านไปยังฟังก์ชัน
  • ลงทะเบียนค่าที่จำเป็นต้องกู้คืนเมื่อฟังก์ชั่นสิ้นสุด
  • ช่องว่างสำหรับตัวแปรโลคัลภายในฟังก์ชันที่เรียกใช้

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

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

เป็นที่น่าสังเกตว่าการเพิ่มประสิทธิภาพการเรียกซ้ำแบบหางมีอยู่ในคอมไพเลอร์บางตัว (แม้ว่าฉันไม่แน่ใจว่า avr-gcc รองรับหรือไม่) หากการเรียกซ้ำเป็นสิ่งสุดท้ายในฟังก์ชั่นนั่นหมายความว่าบางครั้งก็เป็นไปได้ที่จะหลีกเลี่ยงการแก้ไขเฟรมสแต็คเลย คอมไพเลอร์สามารถใช้เฟรมที่มีอยู่ได้อีกครั้งเนื่องจากการโทร 'พาเรนต์' (เพื่อพูด) เสร็จสิ้นการใช้งาน นั่นหมายความว่าคุณสามารถท่องวนทางทฤษฎีซ้ำได้นานเท่าที่คุณต้องการตราบใดที่ฟังก์ชั่นของคุณไม่ได้เรียกสิ่งอื่นใด


1
avr-gcc ไม่รองรับการเรียกซ้ำแบบหาง
asheeshr

@AsheeshR - ดีที่รู้ ขอบคุณ ฉันคิดว่ามันไม่น่าจะเป็นไปได้
Peter Bloomfield

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

1
โพสต์โดย @TheDoctor ขัดแย้งกับ "avr-gcc ไม่สนับสนุนการเรียกซ้ำหาง" เช่นเดียวกับการทดสอบรหัสของฉัน คอมไพเลอร์ได้ใช้การเรียกซ้ำแบบหางซึ่งเป็นวิธีที่เขาสามารถเรียกซ้ำได้ถึงล้านครั้ง ปีเตอร์ถูกต้อง - มันเป็นไปได้สำหรับคอมไพเลอร์ที่จะมาแทนที่โทร / ผลตอบแทน (ตามที่โทรล่าสุดในฟังก์ชั่น) ที่มีเพียงแค่กระโดด มันมีผลลัพธ์ที่เหมือนกันและไม่ใช้พื้นที่สแต็ก
Nick Gammon

2

ฉันมีคำถามเดียวกันนี้อย่างแน่นอนขณะที่ฉันกำลังอ่านJumping เป็น C ++ โดย Alex Allain , Ch 16: การเรียกซ้ำ, p.230 ดังนั้นฉันจึงทำการทดสอบ

TLDR;

Arduino Arduino ของฉัน (ATmega328 mcu) สามารถเรียกใช้ฟังก์ชันเรียกซ้ำได้ 211 ครั้ง (สำหรับโค้ดที่ให้ไว้ด้านล่าง) ก่อนที่จะมีการล้นสแต็กและการล่ม

ก่อนอื่นให้ฉันจัดการข้อเรียกร้องนี้:

บางครั้งการเรียกซ้ำเป็นเพียงตัวเลือกด่วนในการใช้อัลกอริทึมที่แน่นอน

[อัพเดท: อ่าฉันอ่านคำว่า "เร็ว" ในกรณีนี้คุณมีความถูกต้อง อย่างไรก็ตามฉันคิดว่ามันคุ้มค่าที่จะพูดต่อไปนี้]

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

ตอนนี้บันทึกไม่กี่:

จำนวนการโทรซ้ำหรือ "สแต็กเฟรม" ที่คุณสามารถรับได้ขึ้นอยู่กับปัจจัยหลายประการรวมถึง:

  • ขนาดของ RAM ของคุณ
  • มีสิ่งของอยู่ในสแต็กจำนวนมากหรือมีอยู่ในกอง (เช่น: RAM ฟรีของคุณมีความสำคัญ; free_RAM = total_RAM - stack_used - heap_usedหรือคุณอาจพูดfree_RAM = stack_size_allocated - stack_size_used)
  • ขนาดของ "สแต็กเฟรม" ใหม่แต่ละอันที่จะวางลงบนสแต็กสำหรับการเรียกใช้ฟังก์ชันเรียกซ้ำทุกครั้ง สิ่งนี้จะขึ้นอยู่กับฟังก์ชันที่เรียกใช้และตัวแปรและข้อกำหนดของหน่วยความจำเป็นต้น

ผลลัพธ์ของฉัน:

  • 20171106-2054hrs - Toshiba Satellite พร้อม RAM 16 GB; quad-core, Windows 8.1: ค่าสุดท้ายพิมพ์ก่อนที่จะเกิดปัญหา: 43166
    • ใช้เวลาหลายวินาทีในการหยุดทำงาน - อาจจะ 5 ~ 10 ใช่ไหม
  • 20180306-1913hrs แล็ปท็อประดับสูงของ Dell ที่มี RAM 64 GB; 8-core, Linux Ubuntu 14.04 LTS: ค่าสุดท้ายพิมพ์ก่อนที่จะเกิดปัญหา: 261752
    • ตามด้วยวลี Segmentation fault (core dumped)
    • ใช้เวลาประมาณ 4 ~ 5 วินาทีในการขัดข้อง
  • 20180306-1930hrs Arduino Nano: TBD --- อยู่ที่ ~ 250000 และยังคงนับ - การตั้งค่าการเพิ่มประสิทธิภาพ Arduino ต้องทำให้มันเกิดประสิทธิภาพสูงสุดในการเรียกซ้ำ ... ??? ใช่เป็นกรณีนี้
    • เพิ่ม#pragma GCC optimize ("-O0")ที่ด้านบนของไฟล์และทำซ้ำ:
  • 20180307-0910hrs Arduino Nano: 32 kB แฟลช, 2 kB SRAM, กระบวนการ 16 MHz: ค่าสุดท้ายที่พิมพ์ก่อนที่จะเกิดความผิดพลาด: 211 Here are the final print results: 209 210 211 ⸮ 9⸮ 3⸮
    • ใช้เวลาเพียงเสี้ยววินาทีเมื่อเริ่มการพิมพ์ที่อัตราการรับส่งข้อมูลแบบต่อเนื่อง 115200 - อาจจะ 1/10 วินาที
    • 2 kiB = 2048 ไบต์ / 211 เฟรมสแต็ก = 9.7 ไบต์ / เฟรม (สมมติว่า RAM ทั้งหมดของคุณกำลังถูกใช้โดยสแต็ก - ซึ่งจริง ๆ แล้วไม่ใช่กรณี) - แต่นี่ดูเหมือนจะสมเหตุสมผลมาก

รหัส:

แอปพลิเคชันพีซี:

/*
stack_overflow
 - a quick program to force a stack overflow in order to see how many stack frames in a small function can be loaded onto the stack before the overflow occurs

By Gabriel Staples
www.ElectricRCAircraftGuy.com
Written: 6 Nov 2017
Updated: 6 Nov 2017

References:
 - Jumping into C++, by Alex Allain, pg. 230 - sample code here in the chapter on recursion

To compile and run:
Compile: g++ -Wall -std=c++11 stack_overflow_1.cpp -o stack_overflow_1
Run in Linux: ./stack_overflow_1
*/

#include <iostream>

void recurse(int count)
{
  std::cout << count << "\n";
  recurse(count + 1);
}

int main()
{
  recurse(1);
}

โปรแกรม "Sketch" ของ Arduino:

/*
recursion_until_stack_overflow
- do a quick recursion test to see how many times I can make the call before the stack overflows

Gabriel Staples
Written: 6 Mar. 2018 
Updated: 7 Mar. 2018 

References:
- Jumping Into C++, by Alex Allain, Ch. 16: Recursion, p.230
*/

// Force the compiler to NOT optimize! Otherwise this recursive function below just gets optimized into a count++ type
// incrementer instead of doing actual recursion with new frames on the stack each time. This is required since we are
// trying to force stack overflow. 
// - See here for all optimization levels: https://gcc.gnu.org/onlinedocs/gcc/Optimize-Options.html
//   - They include: -O1, -O2, -O3, -O0, -Os (Arduino's default I believe), -Ofast, & -Og.

// I mention `#pragma GCC optimize` in my article here: http://www.electricrcaircraftguy.com/2014/01/the-power-of-arduino.html
#pragma GCC optimize ("-O0") 

void recurse(unsigned long count) // each call gets its own "count" variable in a new stack frame 
{
  // delay(1000);
  Serial.println(count);

  // It is not necessary to increment count since each function's variables are separate (so the count in each stack
  // frame will be initialized one greater than the last count)
  recurse (count + 1);

  // GS: notice that there is no base condition; ie: this recursive function, once called, will never finish and return!
}

void setup()
{
  Serial.begin(115200);
  Serial.println(F("\nbegin"));
  // First function call, so it starts at 1
  recurse (1);
}

void loop()
{
}

อ้างอิง:

  1. กระโดดเข้าสู่ C ++ โดย Alex Allain , Ch 16: Recursion, p.230
  2. http://www.electricrcaircraftguy.com/2014/01/the-power-of-arduino.html - แท้จริง: ฉันอ้างอิงเว็บไซต์ของตัวเองในช่วง "โครงการ" นี้เพื่อเตือนตัวเองว่าจะเปลี่ยนระดับการเพิ่มประสิทธิภาพของคอมไพเลอร์ Arduino สำหรับไฟล์ที่กำหนด ด้วย#pragma GCC optimizeคำสั่งตั้งแต่ฉันรู้ว่าฉันมีเอกสารไว้ที่นั่น

1
โปรดทราบว่าตามเอกสารของ avr-lib คุณไม่ควรคอมไพล์หากไม่มีการปรับให้เหมาะสมที่สุดที่อาศัย avr-libc เนื่องจากบางสิ่งไม่รับประกันว่าจะปิดการใช้งานได้ ฉันจึงแนะนำคุณกับที่#pragmaคุณใช้งานอยู่ที่นั่น แต่คุณอาจจะเพิ่ม__attribute__((optimize("O0")))ไปยังฟังก์ชั่นเดียวคุณต้องการ unoptimize
Edgar Bonet

ขอบคุณเอ็ดการ์ คุณรู้หรือไม่ว่า AVR libc มีเอกสารนี้อยู่ที่ใด
Gabriel Staples

1
เอกสารเกี่ยวกับ <util / delay.h>รัฐ:“ในการสั่งซื้อสำหรับฟังก์ชั่นเหล่านี้จะทำงานตามที่ตั้งใจไว้, การเพิ่มประสิทธิภาพคอมไพเลอร์จะต้องถูกเปิดใช้งาน [ ... ]” (เน้นในต้นฉบับ) ฉันไม่ค่อยแน่ใจว่าฟังก์ชั่นอื่น ๆ ของ avr-libc มีข้อกำหนดนี้หรือไม่
Edgar Bonet

1

ฉันเขียนโปรแกรมทดสอบอย่างง่ายนี้:

void setup() {
  // put your setup code here, to run once:
  Serial.begin(115200);
  recurse(1);
}

void loop() {
  // put your main code here, to run repeatedly: 

}

void recurse(long i) {
  Serial.println(i);
  recurse(i+1);
}

ฉันรวบรวมมันเพื่อ Uno และในขณะที่ฉันเขียนมันมีการเรียกซ้ำกว่า 1 ล้านครั้ง! ฉันไม่รู้ แต่คอมไพเลอร์อาจปรับโปรแกรมนี้ให้เหมาะสม


ลองส่งคืนหลังจากการโทรครบจำนวน ~ 1000 ควรสร้างปัญหาแล้ว
asheeshr

1
คอมไพเลอร์ได้ดำเนินการวนรอบการเรียกซ้ำแบบหางบนร่างของคุณอย่างที่คุณจะเห็นว่าคุณถอดแยกชิ้นส่วนหรือไม่ สิ่งที่หมายถึงนี้ก็คือว่ามันแทนที่ลำดับcall xxx/ โดยret jmp xxxสิ่งนี้มีจำนวนเท่ากันยกเว้นว่าวิธีการของคอมไพเลอร์จะไม่ใช้สแต็ก ดังนั้นคุณสามารถชดเชยจำนวนพันล้านครั้งด้วยรหัสของคุณ (สิ่งอื่น ๆ เท่ากัน)
Nick Gammon

คุณสามารถบังคับให้คอมไพเลอร์ไม่ปรับการสอบถามซ้ำให้เหมาะสม ฉันจะกลับมาและโพสต์ตัวอย่างในภายหลัง
Gabriel Staples

ทำ! ตัวอย่างที่นี่: arduino.stackexchange.com/a/51098/7727 ความลับคือการป้องกันการเพิ่มประสิทธิภาพโดยการเพิ่ม#pragma GCC optimize ("-O0") ไปด้านบนของโปรแกรม Arduino ของคุณ ฉันเชื่อว่าคุณต้องทำสิ่งนี้ที่ด้านบนสุดของแต่ละไฟล์ที่คุณต้องการให้มันนำไปใช้ - แต่ฉันไม่ได้ค้นหามันมาหลายปีดังนั้นควรค้นคว้าด้วยตัวเองเพื่อให้แน่ใจ
Gabriel Staples
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.