ราคาแพงในที่สุดก็ลอง


14

ในกรณีของรหัสที่คุณต้องทำการล้างทรัพยากรก่อนออกจากฟังก์ชันมีความแตกต่างที่สำคัญระหว่าง 2 วิธีในการทำมัน

  1. การทำความสะอาดทรัพยากรก่อนทุกคำสั่งส่งคืน

    void func()
    {
        login();
    
        bool ret = dosomething();
        if(ret == false)
        {
            logout();
            return;
        }
    
        ret = dosomethingelse();
        if(ret == false)
        {
            logout();
            return;
        }
    
        dootherstuff();
    
        logout();
    }
  2. การทำความสะอาดทรัพยากรในบล็อกสุดท้าย

    void func()
    {
        login();
    
        try
        {
            bool ret = dosomething();
            if(ret == false)
                return;
    
            ret = dosomethingelse();
            if(ret == false)
                return;
    
            dootherstuff();
    
        }
        finally
        {
            logout();
        }
    }

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


3
มันไม่คุ้มที่จะโพสต์เป็นคำตอบ แต่ไม่ใช่ หากคุณกำลังใช้ Java ซึ่งคาดว่าข้อยกเว้นจะเป็นกลยุทธ์การจัดการข้อผิดพลาดเริ่มต้นคุณควรใช้ลอง / จับ / ในที่สุด - ภาษาได้รับการปรับให้เหมาะสมกับรูปแบบที่คุณใช้
Jonathan Rich

1
@ JonathanRich: เป็นเช่นนั้น?
haylem

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

@mikeTheLiar - ถ้าคุณหมายความว่าฉันควรจะเขียนif(!cond)มันก็คือจาวาที่ทำให้ฉันทำอย่างนี้ ใน C ++, นั่นเป็นวิธีที่ผมเขียนรหัสทั้งสอง booleans และประเภทอื่น ๆ - int x; if(!x)คือ เนื่องจาก java อนุญาตให้ฉันใช้สิ่งนี้ได้เฉพาะbooleansฉันจึงหยุดใช้if(cond)& if(!cond)ใน java โดยสิ้นเชิง
user93353

1
@ user93353 ที่ทำให้ฉันเศร้า ฉันอยากจะดู(someIntValue != 0)มากกว่าการเปรียบเทียบมากกว่าการประเมินบูลีน นั่นทำให้ฉันมีกลิ่นและฉันจะทำให้มันสดชื่นทันทีที่เห็นในป่า
MikeTheLiar

คำตอบ:


23

ตามที่ระบุไว้ในข้อยกเว้น Java ช้าแค่ไหน? จะเห็นได้ว่าความtry {} catch {}เชื่องช้าของนั้นอยู่ภายในการสร้างสัญชาตญาณของข้อยกเว้นนั้นเอง

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

ในตัวอย่างที่ให้ไว้ในคำถามนี้ไม่มีข้อยกเว้นใครจะไม่คาดหวังว่าจะเกิดการชะลอตัว - พวกเขาไม่ได้ถูกสร้างขึ้น สิ่งที่อยู่ที่นี่คือการtry {} finally {}จัดการการจัดสรรทรัพยากรในบล็อกในที่สุด

ดังนั้นเพื่อตอบคำถามไม่ไม่มีค่าใช้จ่ายรันไทม์ที่แท้จริงภายในtry {} finally {}โครงสร้างที่ไม่ได้ใช้ข้อยกเว้น (มันไม่เคยได้ยินมาก่อน) อะไรคืออาจจะมีราคาแพงเวลาการบำรุงรักษาเมื่อใครอ่านรหัสและเห็นรูปแบบรหัสนี้ไม่ปกติและ coder มีเพื่อให้ได้ใจของพวกเขาไปรอบ ๆ สิ่งที่อื่นที่เกิดขึ้นในวิธีการนี้หลังจากที่returnก่อนจะกลับไปสายก่อน


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

พิจารณาเวลาการบำรุงรักษาของการสอนโครงสร้างภาษาใหม่ให้กับบางคน การเห็นtry {} finally {}ไม่ใช่สิ่งที่คนมักเห็นในโค้ด Java และอาจทำให้ผู้คนสับสน มีเวลาการบำรุงรักษาในระดับหนึ่งสำหรับการเรียนรู้โครงสร้างขั้นสูงใน Java มากกว่าสิ่งที่ผู้คนคุ้นเคย

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

เมื่อชั่งน้ำหนักค่าใช้จ่ายทั้งสองนี้มันมีราคาแพงในเวลาการบำรุงรักษาที่จะไม่ใช้try {} finally {}วิธีการ ในขณะที่ผู้คนสามารถ dicker เกี่ยวกับจำนวนมิลลิวินาทีเศษส่วนหรือคำแนะนำ jvm เพิ่มเติมtry {} finally {}บล็อกเปรียบเทียบกับรุ่นอื่น ๆ หนึ่งต้องพิจารณาชั่วโมงที่ใช้ในการแก้จุดบกพร่องน้อยกว่าวิธีที่เหมาะในการจัดการการจัดสรรทรัพยากร

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


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

2
สิ่งที่อาจมีราคาแพงกว่าในการบำรุงรักษามากกว่าการทำความเข้าใจกับการทดลองใช้ในที่สุดนี้ก็คือการต้องดูแลรักษาบล็อกทำความสะอาดหลายอย่าง อย่างน้อยตอนนี้คุณก็รู้ว่าถ้าต้องการการทำความสะอาดเพิ่มเติมก็มีเพียงที่เดียวเท่านั้น
Bart van Ingen Schenau

@BartvanIngenSchenau แน่นอน การลองในที่สุดอาจเป็นวิธีที่ดีที่สุดในการจัดการมันไม่ใช่แค่โครงสร้างทั่วไปที่ฉันเคยเห็นในรหัส - คนมีปัญหาพอที่จะเข้าใจลองจับในที่สุดและจัดการข้อยกเว้นโดยทั่วไป .. แต่ลอง -finaly โดยไม่ต้องจับหรือไม่? อะไร? ผู้คนไม่เข้าใจจริงๆ ตามที่คุณแนะนำจะเป็นการดีกว่าที่จะเข้าใจโครงสร้างภาษามากกว่าพยายามหลีกเลี่ยงและทำสิ่งที่ไม่ดี

ฉันส่วนใหญ่ต้องการทำให้เป็นที่รู้จักกันว่าทางเลือกในการลองในที่สุดก็ไม่ฟรี ฉันหวังว่าฉันจะให้ upvote อีกครั้งสำหรับการเพิ่มของคุณเกี่ยวกับต้นทุน
Bart van Ingen Schenau

5

/programming/299068/how-slow-are-java-exceptions

คำตอบที่ได้รับการยอมรับสำหรับคำถามนี้แสดงให้เห็นถึงการตัดการเรียกใช้ฟังก์ชั่นในบล็อก catch catch block ที่มีค่าใช้จ่ายน้อยกว่า 5% จากการเรียกใช้ฟังก์ชันเปล่า การขว้างและจับข้อยกเว้นที่เกิดขึ้นจริงทำให้รันไทม์ไปบอลลูนมากกว่า 66x การเรียกฟังก์ชันเปล่า ดังนั้นหากคุณคาดหวังว่าการออกแบบข้อยกเว้นจะโยนเป็นประจำดังนั้นฉันจะพยายามหลีกเลี่ยงมัน (ในรหัสที่มีความสำคัญต่อประสิทธิภาพอย่างเหมาะสม) อย่างไรก็ตามหากสถานการณ์พิเศษนั้นหายากมันก็ไม่ใช่เรื่องใหญ่


3
"ถ้าสถานการณ์พิเศษนั้นหายาก" - ก็มีการพูดซ้ำซากนั่น เจ๋งหมายถึงหายากและเป็นวิธีการยกเว้นที่แม่นยำ ไม่ควรเป็นส่วนหนึ่งของการออกแบบหรือการควบคุมการไหล
Jesse C. Slicer

1
ข้อยกเว้นไม่จำเป็นต้องเป็นเรื่องยากในทางปฏิบัติมันเป็นเพียงผลข้างเคียงจากอุบัติเหตุ ตัวอย่างเช่นถ้าคุณมีคน UI ที่ไม่ดีอาจก่อให้เกิดกรณีการใช้งานข้อยกเว้นไหลบ่อยกว่าการไหลปกติ
Esailija

2
ไม่มีข้อยกเว้นใด ๆ ในรหัสของคำถาม

2
@ JesseC.Slicer มีภาษาที่ไม่แปลกที่จะเห็นพวกเขาใช้เป็นตัวควบคุมการไหล เป็นตัวอย่างเมื่อทำการวนซ้ำสิ่งใดใน python ตัววนซ้ำจะส่งข้อยกเว้น stopiteration เมื่อเสร็จสิ้น นอกจากนี้ใน OCaml ห้องสมุดมาตรฐานของพวกเขาดูเหมือนจะ "มีความสุขมาก" มันจะโยนในสถานการณ์จำนวนมากที่ไม่ได้หายาก (ถ้าคุณมีแผนที่และพยายามค้นหาบางสิ่งที่ไม่มีอยู่ในแผนที่ที่มันพ่น )
stonemetal

@stonemetal เช่นเดียวกับ Pict Dict พร้อม KeyError
Izkata

4

แทนที่จะปรับให้เหมาะสม - ลองคิดดูว่ารหัสของคุณกำลังทำอะไรอยู่

การเพิ่มบล็อกสุดท้ายคือการใช้งานที่แตกต่างกัน - หมายความว่าคุณจะออกจากระบบทุกข้อยกเว้น

โดยเฉพาะ - หากการเข้าสู่ระบบเกิดข้อยกเว้นพฤติกรรมในฟังก์ชันที่สองของคุณจะแตกต่างจากครั้งแรกของคุณ

หากการเข้าสู่ระบบและออกจากระบบไม่ได้เป็นส่วนหนึ่งของพฤติกรรมการทำงานจริง ๆ แล้วฉันขอแนะนำให้วิธีการทำสิ่งหนึ่งและสิ่งหนึ่งที่ดี:

    void func()
    {
            bool ret = dosomething();
            if(ret == false)
                return;

            ret = dosomethingelse();
            if(ret == false)
                return;

            dootherstuff();

    }

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

โพสต์ของคุณถูกแท็กเป็น Java ซึ่งฉันไม่คุ้นเคยเป็นพิเศษ แต่ใน. net ฉันจะใช้บล็อกการใช้สำหรับสิ่งนี้:

เช่น:

    using(var loginContext = CreateLoginContext()) 
    {
            // Do all your stuff
    }

และบริบทการเข้าสู่ระบบมีวิธีการกำจัดซึ่งจะออกจากระบบและทำความสะอาดทรัพยากร

แก้ไข: ฉันไม่ได้ตอบคำถามจริงๆ!

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

ฉันจะออกจากคำตอบที่เหลือเพราะแม้ว่าจะไม่ตอบคำถามฉันคิดว่ามันมีประโยชน์สำหรับ OP


1
เพื่อให้คำตอบของคุณสมบูรณ์ Java 7 ได้นำเสนอคำสั่ง try-with-resources ซึ่งคล้ายกับการusingสร้าง. net ของคุณโดยสมมติว่าทรัพยากรที่เป็นปัญหานั้นใช้AutoCloseableอินเทอร์เฟซ
haylem

ฉันจะลบretตัวแปรนี้ซึ่งเพิ่งได้รับมอบหมายสองครั้งเพื่อตรวจสอบหนึ่งบรรทัดในภายหลัง if (dosomething() == false) return;ทำให้มัน
ott--

เห็นด้วย แต่ฉันต้องการเก็บรหัส OP ตามวิธีที่ให้ไว้
Michael

@ ott-- ฉันคิดว่ารหัสของ OP เป็นการทำให้การใช้งานง่ายขึ้นตัวอย่างเช่นพวกเขาอาจมีอะไรที่คล้ายกันมากกว่าret2 = doSomethingElse(ret1)แต่ก็ไม่เกี่ยวข้องกับคำถามดังนั้นจึงถูกลบออก
Izkata

if (dosomething() == false) ...เป็นรหัสที่ไม่ดี ใช้ intiutive มากขึ้นif (!dosomething()) ...
Steffen Heil

1

ข้อสันนิษฐาน: คุณกำลังพัฒนาในรหัส C #

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

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


11
คำถามคือแท็กจาวา
Joachim Sauer

เห็นดี! ;)
Michael Shaw

3
นอกจากนี้วิธีการที่ไม่ได้เป็นตัวพิมพ์ใหญ่ถึงแม้ว่ามันจะเป็นเพียงรสชาติที่ดี
Erik Reppen

ยังคงเป็นคำตอบที่ดีถ้าคำถามคือ C # :)
PhillyNJ

-2

ดังที่คนอื่น ๆ ระบุไว้แล้วรหัส Ttese จะมีผลลัพธ์ที่แตกต่างกันหากมีdosomethingelseหรือdootherstuffมีข้อยกเว้น

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


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