ความแตกต่างด้านประสิทธิภาพระหว่างการดีบั๊กและการปล่อยบิลด์


280

ฉันต้องยอมรับว่าโดยปกติฉันไม่ได้ใส่ใจกับการสลับระหว่างการกำหนดค่าDebugและReleaseในโปรแกรมของฉันและฉันมักจะเลือกที่จะไปใช้การกำหนดค่าDebugแม้ว่าโปรแกรมจะถูกปรับใช้จริงที่ลูกค้า

เท่าที่ฉันทราบความแตกต่างเพียงอย่างเดียวระหว่างการกำหนดค่าเหล่านี้หากคุณไม่เปลี่ยนแปลงด้วยตนเองคือDebugมีDEBUGค่าคงที่ที่กำหนดไว้และReleaseจะมีการตรวจสอบรหัสเพิ่มประสิทธิภาพของ

ดังนั้นคำถามของฉันเป็นสองเท่าจริง:

  1. มีความแตกต่างด้านประสิทธิภาพอย่างมากระหว่างการกำหนดค่าทั้งสองนี้หรือไม่ มีรหัสประเภทใดที่จะทำให้ประสิทธิภาพแตกต่างกันมากที่นี่หรือที่จริงไม่สำคัญ

  2. มีรหัสประเภทใดบ้างที่จะทำงานได้ดีภายใต้การกำหนดค่าDebugที่อาจล้มเหลวภายใต้การกำหนดค่าการปล่อยหรือคุณสามารถแน่ใจได้ว่ารหัสที่ผ่านการทดสอบและทำงานได้ดีภายใต้การกำหนดค่าDebugจะทำงานได้ดีภายใต้


คำตอบ:


511

คอมไพเลอร์ C # นั้นไม่ได้เปลี่ยนแปลง IL ที่ปล่อยออกมาอย่างมากใน build build สิ่งที่น่าสังเกตคือมันไม่ปล่อย opcode ของ NOP อีกต่อไปที่ให้คุณตั้งเบรกพอยต์บนวงเล็บปีกกาแบบหยัก ตัวใหญ่คือเครื่องมือเพิ่มประสิทธิภาพที่สร้างไว้ในคอมไพเลอร์ JIT ฉันรู้ว่ามันทำให้การเพิ่มประสิทธิภาพต่อไปนี้:

  • วิธีการแบบอินไลน์ การเรียกเมธอดจะถูกแทนที่ด้วยการฉีดโค้ดของเมธอด นี่เป็นเรื่องใหญ่มันทำให้ผู้เข้าถึงอสังหาริมทรัพย์เป็นอิสระ

  • การจัดสรร CPU register ตัวแปรโลคัลและอาร์กิวเมนต์เมธอดสามารถเก็บอยู่ในการลงทะเบียน CPU โดยไม่ต้องถูกเก็บไว้ที่เฟรมสแต็ก นี่เป็นโค้ดตัวใหญ่ที่โดดเด่นในการทำการดีบักโค้ดที่ปรับให้เหมาะสมยากมาก และให้ความหมายกับคำสำคัญที่ระเหยได้

  • การตรวจสอบดัชนีอาร์เรย์ การปรับให้เหมาะสมที่สำคัญเมื่อทำงานกับอาร์เรย์ (คลาสคอลเลกชัน. NET ทั้งหมดใช้อาร์เรย์ภายใน) เมื่อคอมไพเลอร์ JIT สามารถตรวจสอบว่าลูปไม่เคยทำดัชนีอาร์เรย์นอกขอบเขตมันจะกำจัดการตรวจสอบดัชนี ตัวใหญ่

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

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

  • การชักรหัส โค้ดภายในลูปที่ไม่ได้รับผลกระทบจากลูปสามารถย้ายออกจากลูปได้ เครื่องมือเพิ่มประสิทธิภาพของคอมไพเลอร์ C จะใช้เวลามากขึ้นในการหาโอกาสในการยก อย่างไรก็ตามมันเป็นการเพิ่มประสิทธิภาพที่มีราคาแพงเนื่องจากการวิเคราะห์การไหลของข้อมูลที่ต้องการและกระวนกระวายใจไม่สามารถมีเวลาดังนั้นยกเฉพาะกรณีที่ชัดเจน บังคับให้โปรแกรมเมอร์. NET เขียนซอร์สโค้ดที่ดีขึ้นและยกตัวเองขึ้นมา

  • กำจัดการแสดงออกย่อยทั่วไป x = y + 4; z = y + 4; กลายเป็น z = x; ค่อนข้างธรรมดาในงบเช่น dest [ix + 1] = src [ix + 1]; เขียนเพื่อความสะดวกในการอ่านโดยไม่แนะนำตัวแปรตัวช่วย ไม่จำเป็นต้องประนีประนอมการอ่าน

  • พับอย่างต่อเนื่อง x = 1 + 2; กลายเป็น x = 3; ตัวอย่างง่ายๆนี้รวบรวมโดยผู้รวบรวมแต่ทว่าเกิดขึ้นที่ JIT เมื่อการเพิ่มประสิทธิภาพอื่นทำให้เป็นไปได้

  • คัดลอกการขยายพันธุ์ x = a; y = x; กลายเป็น y = a; สิ่งนี้ช่วยให้ผู้จัดสรรตัวลงทะเบียนทำการตัดสินใจได้ดีขึ้น มันเป็นเรื่องใหญ่ในกระวนกระวายใจ x86 เพราะมีการลงทะเบียนน้อยมากที่จะทำงานกับ การเลือกสิ่งที่ถูกต้องเป็นสิ่งสำคัญยิ่ง

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

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

เครื่องมือเพิ่มประสิทธิภาพ JIT เป็นรหัสที่น่าเชื่อถือส่วนใหญ่เป็นเพราะได้รับการทดสอบนับล้านครั้ง มันเป็นเรื่องยากมากที่จะมีปัญหาในรุ่นที่วางจำหน่ายของโปรแกรมของคุณ มันเกิดขึ้นได้อย่างไร ทั้ง x64 และ x86 jitters มีปัญหากับ structs x86 jitter มีปัญหากับความสอดคล้องจุดลอยตัวทำให้เกิดผลลัพธ์ที่แตกต่างอย่างละเอียดเมื่อตัวกลางของการคำนวณจุดลอยตัวถูกเก็บไว้ในการลงทะเบียน FPU ที่ความแม่นยำ 80 บิตแทนที่จะถูกตัดเมื่อล้างหน่วยความจำ


23
ฉันไม่คิดว่าคอลเล็กชันทั้งหมดใช้อาร์เรย์: LinkedList<T>ไม่แม้ว่าจะไม่ได้ใช้บ่อยนัก
svick

ฉันคิดว่า CLR กำหนดค่าความแม่นยำ FPU ถึง 53 บิต (จับคู่แบบกว้าง 64- บิต) ดังนั้นจึงไม่ควรมีการคำนวณสองครั้งแบบขยายขนาด 80 บิตสำหรับค่า Float64 อย่างไรก็ตามการคำนวณ Float32 อาจคำนวณได้ที่ความแม่นยำ 53 บิตและจะถูกตัดเมื่อเก็บไว้ในหน่วยความจำเท่านั้น
Govert

2
volatileคำหลักที่ใช้ไม่ได้กับตัวแปรท้องถิ่นที่จัดเก็บไว้ในกรอบสแต็ค จากเอกสารที่msdn.microsoft.com/en-us/library/x13ttww7.aspx : "คำสำคัญระเหยสามารถใช้ได้กับฟิลด์ของคลาสหรือโครงสร้างเท่านั้นตัวแปรท้องถิ่นไม่สามารถประกาศได้"
Kris Vandermotten

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

3
อาจจะน่ากล่าวถึงว่าไม่มีวิธีการใด ๆ ใน System.Diagnostics.Debug ทำสิ่งใดในการสร้างการดีบัก นอกจากนี้ตัวแปรยังไม่ได้รับการสรุปให้เห็นอย่างรวดเร็ว ( stackoverflow.com/a/7165380/20553 )
Martin Brown

23
  1. ใช่มีความแตกต่างด้านประสิทธิภาพมากมายและสิ่งเหล่านี้ใช้กับรหัสของคุณจริงๆ การดีบักทำการปรับประสิทธิภาพให้น้อยที่สุดและโหมดการเปิดใช้งานเป็นอย่างมาก

  2. เฉพาะรหัสที่ขึ้นอยู่กับDEBUGค่าคงที่อาจทำงานแตกต่างกันกับรุ่นที่วางจำหน่าย นอกจากนั้นคุณไม่ควรเห็นปัญหาใด ๆ

ตัวอย่างของโค้ดเฟรมเวิร์กที่ขึ้นอยู่กับDEBUGค่าคงที่คือDebug.Assert()เมธอดซึ่งมีการ[Conditional("DEBUG)"]กำหนดแอ็ตทริบิวต์ ซึ่งหมายความว่ามันยังขึ้นอยู่กับDEBUGค่าคงที่และสิ่งนี้ไม่รวมอยู่ใน build build


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

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

2
@Testalino - ดีวันนี้มันยาก โปรเซสเซอร์ได้รับความรวดเร็วที่ผู้ใช้แทบจะไม่รอให้กระบวนการเรียกใช้โค้ดเพราะการกระทำของผู้ใช้ดังนั้นนี่เป็นความสัมพันธ์ทั้งหมด อย่างไรก็ตามหากคุณกำลังทำกระบวนการที่ยืดเยื้อจริง ๆ แล้วคุณจะสังเกตเห็น เช่นรหัสต่อไปนี้จะทำงานช้าลง 40% ภายใต้:DEBUG AppDomain.CurrentDomain.GetAssemblies().Sum(p => p.GetTypes().Sum(p1 => p1.GetProperties().Length))
Pieter van Ginkel

2
นอกจากนี้หากคุณเปิดasp.netใช้งานและใช้ดีบักแทนการปล่อยสคริปต์บางรายการอาจถูกเพิ่มในหน้าของคุณเช่น: MicrosoftAjax.debug.jsที่มีประมาณ 7k บรรทัด
BrunoLM

13

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

อย่างไรก็ตามหากคุณมีการคำนวณที่หนักมากคุณจะสังเกตเห็นความแตกต่าง (อาจสูงถึง 40% ตามที่ @Pieter กล่าวถึงแม้ว่ามันจะขึ้นอยู่กับลักษณะของการคำนวณ)

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


11
  • ประสบการณ์ของฉันคือแอพพลิเคชั่นขนาดกลางหรือใหญ่นั้นตอบสนองได้ดีกว่าใน Build build ลองกับแอปพลิเคชันของคุณและดูว่ามันรู้สึกอย่างไร

  • สิ่งหนึ่งที่สามารถกัดคุณด้วย Release builds ได้คือ Debug build code บางครั้งสามารถระงับสภาวะการแข่งขันและข้อบกพร่องอื่น ๆ ที่เกี่ยวข้องกับเธรด รหัสที่ได้รับการปรับให้เหมาะสมอาจส่งผลให้มีการเรียงลำดับคำสั่งใหม่


9

คุณไม่ควรปล่อย. NET Debug ในการผลิต มันอาจมีรหัสที่น่าเกลียดเพื่อรองรับการแก้ไขและดำเนินการต่อหรือใครรู้อะไรอีกบ้าง เท่าที่ฉันรู้สิ่งนี้จะเกิดขึ้นเฉพาะใน VB ไม่ใช่ C # (หมายเหตุ: โพสต์ดั้งเดิมถูกติดแท็ก C #)แต่ก็ควรให้เหตุผลที่จะหยุดชั่วคราวตามสิ่งที่ Microsoft คิดว่าพวกเขาได้รับอนุญาตให้สร้างด้วย Debug ในความเป็นจริงก่อนที่จะ. NET 4.0 รหัส VB ​​รั่วไหลหน่วยความจำตามจำนวนอินสแตนซ์ของวัตถุกับเหตุการณ์ที่คุณสร้างในการสนับสนุนการแก้ไขและดำเนินการต่อ (แม้ว่าสิ่งนี้จะได้รับการแก้ไขตามhttps://connect.microsoft.com/VisualStudio/feedback/details/481671/vb-classes-with-events-are-not-garbage-collected-when-debugging ) รหัสที่สร้างขึ้น ดูน่ารังเกียจสร้างWeakReferenceวัตถุและเพิ่มเข้าไปในรายการคงที่ในขณะที่ถือล็อค) ฉันไม่ต้องการการสนับสนุนการดีบักแบบใด ๆ ในสภาพแวดล้อมการผลิตอย่างแน่นอน!


ฉันได้เปิดตัว Debug สร้างหลายครั้งและไม่เคยเห็นปัญหา ข้อแตกต่างอย่างเดียวคือแอปพลิเคชันฝั่งเซิร์ฟเวอร์ของเราไม่ใช่เว็บแอปที่สนับสนุนผู้ใช้จำนวนมาก แต่มันเป็นแอพพลิเคชั่นฝั่งเซิร์ฟเวอร์ที่มีโหลดการประมวลผลสูงมาก จากประสบการณ์ของฉันความแตกต่างระหว่าง Debug และ Release ดูเหมือนว่าเป็นทฤษฎีอย่างสมบูรณ์ ฉันไม่เคยเห็นความแตกต่างที่เป็นประโยชน์กับแอพของเรา
Sam Goldberg

5

จากประสบการณ์ของฉันสิ่งที่แย่ที่สุดที่ออกมาจากโหมดการเปิดตัวคือ "การปล่อยบั๊ก" ที่คลุมเครือ เนื่องจาก IL (ภาษากลาง) ได้รับการปรับให้เหมาะสมที่สุดในโหมด Release จึงมีความเป็นไปได้ของข้อบกพร่องที่จะไม่ปรากฏในโหมดแก้ไขข้อบกพร่อง มีคำถาม SO อื่น ๆ ที่ครอบคลุมปัญหานี้: สาเหตุที่พบบ่อยสำหรับข้อบกพร่องในรุ่นที่วางจำหน่ายไม่มีอยู่ในโหมดแก้ไขข้อบกพร่อง

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


ติดตามได้ที่นี่เป็นบทความที่ให้ตัวอย่างของ Bug ที่วางจำหน่าย: codeproject.com/KB/trace/ReleaseBug.aspx
Roly

ยังคงเป็นปัญหาหากแอปพลิเคชันได้รับการทดสอบและอนุมัติด้วยการตั้งค่าการดีบักแม้ว่าจะระงับข้อผิดพลาดหากสิ่งนั้นทำให้การสร้างการปล่อยล้มเหลวในระหว่างการปรับใช้
ØyvindBråthen

4

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


1
สำหรับรหัสที่ถูกผูกไว้กับ IO สร้างรุ่นวางจำหน่ายอาจไม่เร็วกว่าการตรวจแก้จุดบกพร่องได้อย่างง่ายดาย
ริชาร์ด

0
    **Debug Mode:**
    Developer use debug mode for debugging the web application on live/local server. Debug mode allow developers to break the execution of program using interrupt 3 and step through the code. Debug mode has below features:
   1) Less optimized code
   2) Some additional instructions are added to enable the developer to set a breakpoint on every source code line.
   3) More memory is used by the source code at runtime.
   4) Scripts & images downloaded by webresource.axd are not cached.
   5) It has big size, and runs slower.

    **Release Mode:**
    Developer use release mode for final deployment of source code on live server. Release mode dlls contain optimized code and it is for customers. Release mode has below features:
   1) More optimized code
   2) Some additional instructions are removed and developer cant set a breakpoint on every source code line.
   3) Less memory is used by the source code at runtime.
   4) Scripts & images downloaded by webresource.axd are cached.
   5) It has small size, and runs fast.

2
ดูเหมือนว่าในโหมดการเปิดตัวบางครั้งองค์ประกอบแรกของรายการจะไม่ได้หมายเลขอย่างถูกต้อง นอกจากนี้องค์ประกอบบางอย่างในรายการจะถูกทำซ้ำ :)
เกียนเปาโล
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.