Node.js เร็วขึ้นเพียงใดโดยที่ยังคงใช้ Threads ภายในอยู่


281

ฉันเพิ่งดูวิดีโอต่อไปนี้: รู้เบื้องต้นเกี่ยวกับ Node.jsและยังไม่เข้าใจว่าคุณจะได้รับประโยชน์จากความเร็วได้อย่างไร

โดยหลักแล้ว ณ จุดหนึ่ง Ryan Dahl (ผู้สร้างของ Node.js) กล่าวว่า Node.js เป็นแบบวนรอบเหตุการณ์แทนที่จะเป็นแบบเธรด หัวข้อมีราคาแพงและควรจะเหลือผู้เชี่ยวชาญของการเขียนโปรแกรมพร้อมกันที่จะใช้

หลังจากนั้นเขาจะแสดงสแต็กสถาปัตยกรรมของ Node.js ซึ่งมีการใช้งาน C พื้นฐานซึ่งมีกลุ่มเธรดของตัวเองภายใน ดังนั้นนักพัฒนา Node.js จะไม่เริ่มต้นเธรดของตนเองหรือใช้เธรดพูลโดยตรง ... พวกเขาใช้ async call-backs ฉันเข้าใจมาก

สิ่งที่ฉันไม่เข้าใจคือจุดที่ Node.js ยังคงใช้เธรด ... มันเพิ่งซ่อนการนำไปใช้งานดังนั้นวิธีนี้จะเร็วขึ้นหากมีคน 50 คนร้องขอไฟล์ 50 ไฟล์ (ไม่ใช่ในหน่วยความจำในปัจจุบัน) แล้วก็ไม่จำเป็นต้อง 50 เธรด ?

ความแตกต่างเพียงอย่างเดียวคือเนื่องจากมีการจัดการภายในนักพัฒนา Node.js ไม่จำเป็นต้องเขียนโค้ดรายละเอียดของเธรด แต่ภายใต้มันยังคงใช้เธรดเพื่อประมวลผลคำขอไฟล์ IO (การบล็อก)

ดังนั้นคุณไม่เพียงแค่นำปัญหาเดียว (เธรด) และซ่อนไว้ในขณะที่ปัญหานั้นยังคงมีอยู่: เธรดส่วนใหญ่หลาย ๆ การสลับบริบทการล็อกตาย ... ฯลฯ ?

ต้องมีรายละเอียดบางอย่างที่ฉันยังไม่เข้าใจที่นี่


14
ฉันอยากจะเห็นด้วยกับคุณว่าการอ้างสิทธิ์นั้นค่อนข้างง่าย ฉันเชื่อว่าข้อดีของประสิทธิภาพการทำงานของโหนดจะลดลงถึงสองสิ่ง: 1) เธรดที่แท้จริงนั้นมีอยู่ในระดับที่ค่อนข้างต่ำและยังคงมีข้อ จำกัด ด้านขนาดและจำนวนและการซิงโครไนซ์เธรดจึงง่ายขึ้น 2) "การสลับ" ระดับ OS ผ่านselect()เร็วกว่าการสลับบริบทของเธรด
Pointy

คำตอบ:


140

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

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

result = query( "select smurfs from some_mushroom" );
// twiddle fingers
go_do_something_with_result( result );

หากคำขอเข้ามาทำให้คุณสร้างเธรดใหม่ที่รันโค้ดด้านบนคุณจะมีเธรดอยู่ที่นั่นโดยไม่ต้องทำอะไรเลยขณะที่query()กำลังทำงาน (Apache ตามไรอันใช้เธรดเดี่ยวเพื่อตอบสนองคำขอต้นฉบับในขณะที่ nginx มีประสิทธิภาพเหนือกว่าในกรณีที่เขาพูดถึงเพราะไม่ใช่)

ตอนนี้ถ้าคุณฉลาดจริง ๆ คุณจะต้องแสดงรหัสด้านบนในลักษณะที่สภาพแวดล้อมสามารถดับและทำอย่างอื่นในขณะที่คุณกำลังเรียกใช้แบบสอบถาม:

query( statement: "select smurfs from some_mushroom", callback: go_do_something_with_result() );

นี่คือสิ่งที่ node.js กำลังทำอยู่ คุณกำลังตกแต่งโดยทั่วไป - ในวิธีที่สะดวกเพราะภาษาและสภาพแวดล้อมดังนั้นประเด็นเกี่ยวกับการปิด - รหัสของคุณในแบบที่สภาพแวดล้อมสามารถฉลาดเกี่ยวกับสิ่งที่ทำงานและเมื่อ ด้วยวิธีดังกล่าว node.js ไม่ใช่สิ่งใหม่ในแง่ที่ว่ามันคิดค้น I / O แบบอะซิงโครนัส (ไม่ใช่ทุกคนที่อ้างสิทธิ์ในสิ่งนี้) แต่เป็นสิ่งใหม่ในแบบที่มันแสดงออกมานั้นแตกต่างกันเล็กน้อย

หมายเหตุ: เมื่อฉันบอกว่าสภาพแวดล้อมสามารถฉลาดเกี่ยวกับสิ่งที่ทำงานและเมื่อโดยเฉพาะสิ่งที่ฉันหมายถึงคือเธรดที่ใช้ในการเริ่มต้น I / O บางอย่างตอนนี้สามารถใช้เพื่อจัดการคำขออื่น ๆ หรือการคำนวณบางอย่างที่สามารถทำได้ ในแบบขนานหรือเริ่มต้น I / O แบบขนานอื่น ๆ (ฉันไม่แน่ใจว่าโหนดมีความซับซ้อนพอที่จะเริ่มทำงานได้มากขึ้นสำหรับคำขอเดียวกัน แต่คุณได้รับแนวคิด)


6
โอเคฉันสามารถเห็นได้อย่างชัดเจนว่าสิ่งนี้สามารถเพิ่มประสิทธิภาพได้อย่างไรเพราะฟังดูเหมือนฉันจะสามารถเพิ่มประสิทธิภาพ CPU ของคุณได้สูงสุดเนื่องจากไม่มีเธรดหรือสแต็คการประมวลผลรอให้ IO กลับมาดังนั้นสิ่งที่ Ryan ทำเสร็จแล้ว วิธีปิดช่องว่างทั้งหมด
Ralph Caraveo

34
ใช่สิ่งหนึ่งที่ฉันพูดคือมันไม่ใช่ว่าเขาพบวิธีที่จะปิดช่องว่าง: มันไม่ใช่รูปแบบใหม่ สิ่งที่แตกต่างคือเขาใช้ Javascript เพื่อให้โปรแกรมเมอร์แสดงโปรแกรมของพวกเขาในแบบที่สะดวกกว่าสำหรับอะซิงโครนัสประเภทนี้ อาจเป็นรายละเอียด nitpicky แต่ก็ยัง ...
jrtipton

16
นอกจากนี้ยังควรค่าแก่การชี้ให้เห็นว่าสำหรับงาน I / O จำนวนมากโหนดใช้สิ่งที่เป็น async I / O api ระดับเคอร์เนลที่พร้อมใช้งาน (epoll, kqueue, / dev / polls)
Paul

7
ฉันยังไม่แน่ใจว่าฉันเข้าใจอย่างถ่องแท้ หากเราพิจารณาว่าภายในการดำเนินการตามคำขอของเว็บ IO นั้นเป็นสิ่งที่ใช้เวลาส่วนใหญ่ในการดำเนินการตามคำขอและหากการดำเนินการ IO แต่ละครั้งมีการสร้างเธรดใหม่แล้วสำหรับ 50 คำขอที่เข้ามาทดแทนอย่างรวดเร็วเราจะ อาจมี 50 เธรดที่ทำงานแบบขนานและเรียกใช้งานส่วนของ IO ความแตกต่างจากเว็บเซิร์ฟเวอร์มาตรฐานคือมีคำขอทั้งหมดถูกดำเนินการบนเธรดในขณะที่ node.js เป็นเพียงส่วน IO แต่นั่นเป็นส่วนที่ใช้เวลาส่วนใหญ่และทำให้เธรดรออยู่
Florin Dumitrescu

13
@SystemParadox ขอบคุณที่ชี้ให้เห็น ฉันได้ทำการวิจัยบางอย่างเกี่ยวกับหัวข้อเมื่อเร็ว ๆ นี้และแน่นอนจับเป็น Asynchronous I / O เมื่อดำเนินการอย่างถูกต้องในระดับเคอร์เนลไม่ได้ใช้เธรดในขณะที่ดำเนินการ async I / O แต่เธรดการโทรจะถูกปล่อยทันทีที่การดำเนินการ I / O เริ่มต้นขึ้นและการดำเนินการติดต่อกลับจะดำเนินการเมื่อการดำเนินการ I / O เสร็จสิ้นและมีเธรดพร้อมใช้งาน ดังนั้น node.js สามารถเรียกใช้ 50 คำร้องขอพร้อมกันที่มีการดำเนินการ 50 I / O ใน (เกือบ) ขนานโดยใช้เพียงหนึ่งเธรดหากการสนับสนุน async สำหรับการดำเนินการ I / O ถูกนำไปใช้อย่างเหมาะสม
Florin Dumitrescu

32

บันทึก! นี่คือคำตอบเก่า แม้ว่าจะยังคงเป็นจริงในโครงร่างคร่าวๆรายละเอียดบางอย่างอาจมีการเปลี่ยนแปลงเนื่องจากการพัฒนาอย่างรวดเร็วของโหนดในไม่กี่ปีที่ผ่านมา

มันใช้หัวข้อเพราะ:

  1. ตัวเลือก O_NONBLOCK เปิด () ไม่ทำงานบนไฟล์
  2. มีห้องสมุดของบุคคลที่สามที่ไม่เสนอการไม่ปิดกั้น IO

ในการปลอม IO ที่ไม่มีการบล็อกเธรดจะต้องทำดังนี้: ทำการบล็อก IO ในเธรดแยกต่างหาก มันเป็นทางออกที่น่าเกลียดและทำให้เกิดค่าใช้จ่ายมาก

มันยิ่งแย่ลงในระดับฮาร์ดแวร์:

  • ด้วยDMAซีพียูแบบอะซิงโครนัสจะลดการโหลด IO
  • ข้อมูลจะถูกถ่ายโอนโดยตรงระหว่างอุปกรณ์ IO และหน่วยความจำ
  • เคอร์เนลล้อมสิ่งนี้ในแบบซิงโครนัสบล็อกการเรียกระบบ
  • Node.js ล้อมรอบการเรียกของระบบบล็อกในเธรด

นี่เป็นเพียงความโง่และไร้ประสิทธิภาพ แต่มันก็ใช้งานได้อย่างน้อย! เราสามารถเพลิดเพลินกับ Node.js เพราะซ่อนรายละเอียดที่น่าเกลียดและยุ่งยากไว้เบื้องหลังสถาปัตยกรรมอะซิงโครนัสที่ขับเคลื่อนด้วยเหตุการณ์

อาจมีบางคนใช้ O_NONBLOCK เป็นไฟล์ในอนาคตหรือไม่ ...

แก้ไข:ฉันพูดถึงเรื่องนี้กับเพื่อนและเขาบอกฉันว่าทางเลือกอื่นสำหรับเธรดคือการสำรวจด้วยการเลือก : ระบุการหมดเวลาเป็น 0 และทำ IO บนตัวอธิบายไฟล์ที่ส่งคืน (ตอนนี้รับประกันว่าจะไม่บล็อก)


แล้ว Windows ล่ะ
Pacerier

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

28

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

1) องค์ประกอบที่แสดงความคิดเห็นในรหัสหลอกในหนึ่งในคำตอบที่ได้รับความนิยม

result = query( "select smurfs from some_mushroom" );
// twiddle fingers
go_do_something_with_result( result );

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

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

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

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

5) มีวิธีอื่นที่ฉันยอมรับตรรกะของการอ้างว่าวิธีของโหนด "เร็วกว่าด้วยการออกแบบ" และนั่นก็คือสิ่งนี้ โมเดลเธรดส่วนใหญ่ใช้โมเดลสวิตช์บริบทแบบแบ่งส่วนเวลาซึ่งอยู่ด้านบนของโมเดลที่เหมาะสมกว่า (การแจ้งเตือนการตัดสินใจค่า :) และมีประสิทธิภาพมากขึ้น สิ่งนี้เกิดขึ้นด้วยเหตุผลสองประการประการแรกโปรแกรมเมอร์ส่วนใหญ่ดูเหมือนจะไม่เข้าใจลำดับความสำคัญในการจัดลำดับความสำคัญและข้อที่สองถ้าคุณเรียนรู้การทำเกลียวในสภาพแวดล้อมของ Windows สะดุดตา Java เวอร์ชันแรกใช้ลำดับความสำคัญในการปรับใช้ Solaris และ timeslicing ใน Windows เนื่องจากโปรแกรมเมอร์ส่วนใหญ่ไม่เข้าใจและบ่นว่า "เธรดไม่ทำงานใน Solaris" พวกเขาเปลี่ยนแบบจำลองเป็นไทม์ซทุกที่) อย่างไรก็ตามสิ่งที่สำคัญที่สุดคือเวลาที่สร้างสวิทช์บริบทเพิ่มเติม (และอาจไม่จำเป็น) การสลับบริบททุกครั้งต้องใช้เวลาของ CPU และเวลานั้นจะถูกลบออกจากงานที่สามารถทำได้จริงในมือ อย่างไรก็ตามจำนวนเวลาที่ลงทุนในการสลับบริบทเนื่องจากเวลาที่เกิดขึ้นไม่ควรมากกว่าร้อยละขนาดเล็กมากของเวลาโดยรวมเว้นแต่จะมีบางสิ่งที่แปลก ๆ เกิดขึ้นและไม่มีเหตุผลที่ฉันสามารถคาดหวังได้ว่าจะเป็นเช่นนั้นใน เว็บเซิร์ฟเวอร์ที่เรียบง่าย) ดังนั้นใช่บริบทส่วนเกินที่เกี่ยวข้องกับการจับเวลาจะไม่มีประสิทธิภาพ (และสิ่งเหล่านี้จะไม่เกิดขึ้น และเวลานั้นจะถูกลบออกจากงานที่สามารถทำได้จริงในมือ อย่างไรก็ตามระยะเวลาที่ลงทุนในการสลับบริบทเนื่องจากเวลาที่เกิดขึ้นไม่ควรมากกว่าร้อยละขนาดเล็กมากของเวลาโดยรวมเว้นแต่จะมีบางสิ่งที่เกิดขึ้นนอกประเทศเกิดขึ้นและไม่มีเหตุผลที่ฉันจะเห็นว่าเป็นเช่นนั้น เว็บเซิร์ฟเวอร์ที่เรียบง่าย) ดังนั้นใช่บริบทส่วนเกินที่เกี่ยวข้องกับการจับเวลาจะไม่มีประสิทธิภาพ (และสิ่งเหล่านี้จะไม่เกิดขึ้น และเวลานั้นจะถูกลบออกจากงานที่สามารถทำได้จริงในมือ อย่างไรก็ตามระยะเวลาที่ลงทุนในการสลับบริบทเนื่องจากเวลาที่เกิดขึ้นไม่ควรมากกว่าร้อยละขนาดเล็กมากของเวลาโดยรวมเว้นแต่จะมีบางสิ่งที่เกิดขึ้นนอกประเทศเกิดขึ้นและไม่มีเหตุผลที่ฉันจะเห็นว่าเป็นเช่นนั้น เว็บเซิร์ฟเวอร์ที่เรียบง่าย) ดังนั้นใช่บริบทส่วนเกินที่เกี่ยวข้องกับการจับเวลาจะไม่มีประสิทธิภาพ (และสิ่งเหล่านี้จะไม่เกิดขึ้นเคอร์เนลเธรดตามกฎ, btw) แต่ความแตกต่างจะเป็นเปอร์เซ็นต์ของปริมาณงานไม่กี่ชนิดของปัจจัยตัวเลขทั้งหมดที่มีนัยในการอ้างประสิทธิภาพที่มักจะบอกเป็นนัยสำหรับโหนด

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

a) คำอธิบายที่แท้จริงว่าทำไมโหนดควรดีกว่า (นอกเหนือจากสองสถานการณ์ที่ฉันได้อธิบายไว้ข้างต้นสิ่งแรกที่ฉันเชื่อว่าเป็นคำอธิบายที่แท้จริงสำหรับการทดสอบทั้งหมดที่ฉันได้เห็นมา ] ยิ่งฉันคิดถึงมันมากเท่าไหร่ฉันก็ยิ่งสงสัยว่าหน่วยความจำที่ใช้โดยสแต็คจำนวนมากอาจมีความสำคัญที่นี่ขนาดสแต็คเริ่มต้นสำหรับเธรดสมัยใหม่นั้นค่อนข้างใหญ่ แต่หน่วยความจำที่จัดสรรโดย ระบบเหตุการณ์ปิดจะเป็นสิ่งที่จำเป็นเท่านั้น)

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

ไชโยโทบี้


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

3
แต่โหนดไม่ได้ดำเนินการกับเธรด มันยังคงใช้ภายในสำหรับงาน IO ซึ่งเป็นสิ่งที่เว็บร้องขอส่วนใหญ่ต้องการ
levi

1
นอกจากนี้ยังเก็บโหนดปิดการเรียกกลับใน RAM ดังนั้นฉันไม่สามารถดูว่ามันชนะ
Oleksandr Papchenko

@levi แต่ nodejs ไม่ได้ใช้การเรียงลำดับของ "หนึ่งเธรดต่อคำขอ" ใช้เธรดพูล IO อาจหลีกเลี่ยงความยุ่งยากในการใช้ IO API แบบอะซิงโครนัส (และ POSIX open()อาจไม่สามารถทำการปิดกั้นไม่ได้) วิธีนี้จะทำให้ประสิทธิภาพการทำงานลดลงซึ่งรูปแบบดั้งเดิมfork()/ pthread_create()-on-request จะต้องสร้างและทำลายเธรด และตามที่กล่าวไว้ใน postscript a) สิ่งนี้จะตัดทอนปัญหาพื้นที่สแต็ก คุณอาจจะสามารถตอบสนองการร้องขอนับพันด้วยกล่าว 16 กระทู้ IO ดี
binki

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

14

สิ่งที่ฉันไม่เข้าใจคือจุดที่ Node.js ยังคงใช้เธรด

Ryan ใช้เธรดสำหรับส่วนนั้นที่กำลังบล็อก (ส่วนใหญ่ของ node.js ใช้ non-blocking IO) เนื่องจากบางส่วนมีความยากที่จะเขียนไม่ใช่การบล็อก แต่ฉันเชื่อว่า Ryan ปรารถนาที่จะมีทุกสิ่งที่ไม่มีการปิดกั้น บนสไลด์ 63 (การออกแบบภายใน)คุณเห็นไรอันใช้libev (ห้องสมุดที่บทคัดย่อการแจ้งเตือนเหตุการณ์ตรงกัน) สำหรับ non-blocking eventloop เนื่องจาก node-loop ของ event.js ต้องการเธรดที่น้อยลงซึ่งช่วยลดการสลับบริบทการใช้หน่วยความจำเป็นต้น


11

stat()หัวข้อจะใช้เฉพาะในการจัดการกับฟังก์ชั่นที่ไม่มีสิ่งอำนวยความสะดวกไม่ตรงกันเช่น

stat()ฟังก์ชั่นการปิดกั้นอยู่เสมอดังนั้น Node.js ความต้องการที่จะใช้ด้ายที่จะดำเนินการเรียกร้องที่เกิดขึ้นจริงโดยไม่ปิดกั้นหัวข้อหลัก (event ห่วง) อาจเป็นไปได้ว่าจะไม่มีการใช้เธรดจากเธรดพูลหากคุณไม่จำเป็นต้องเรียกใช้ฟังก์ชันเหล่านั้น


7

ฉันไม่รู้อะไรเลยเกี่ยวกับการทำงานภายในของ node.js แต่ฉันเห็นได้ว่าการใช้ลูปเหตุการณ์สามารถเอาชนะการจัดการ I / O ที่มีเธรดได้ดีกว่า ลองนึกภาพคำขอดิสก์ให้ฉัน staticFile.x ทำให้มัน 100 คำขอสำหรับไฟล์นั้น ตามปกติคำขอแต่ละรายการจะรับเธรดที่เรียกคืนไฟล์นั้นนั่นคือ 100 เธรด

ตอนนี้ลองนึกถึงคำขอแรกที่สร้างหนึ่งเธรดที่กลายเป็นวัตถุผู้เผยแพร่การร้องขออื่น ๆ ทั้งหมด 99 ครั้งแรกดูว่ามีวัตถุผู้เผยแพร่สำหรับ staticFile.x ถ้าเป็นเช่นนั้นให้ฟังมันในขณะที่มันกำลังทำงานอยู่มิฉะนั้นจะเริ่มเธรดใหม่ วัตถุผู้เผยแพร่ใหม่

เมื่อเธรดเสร็จสิ้นแล้วจะส่ง staticFile.x ให้กับผู้ฟังทั้งหมด 100 รายและทำลายตัวเองดังนั้นคำขอถัดไปจะสร้างเธรดใหม่และวัตถุผู้เผยแพร่ใหม่

ดังนั้นจึงเป็น 100 เธรดเทียบกับ 1 เธรดในตัวอย่างด้านบน แต่ยังค้นหาดิสก์ 1 รายการแทนการค้นหาดิสก์ 100 รายการอัตราขยายอาจเป็นฟีนอมินัล ไรอันเป็นคนฉลาด!

อีกวิธีในการดูคือหนึ่งในตัวอย่างของเขาที่จุดเริ่มต้นของภาพยนตร์ แทน:

pseudo code:
result = query('select * from ...');

อีกครั้ง 100 แบบสอบถามแยกฐานข้อมูลกับ ... :

pseudo code:
query('select * from ...', function(result){
    // do stuff with result
});

หากการสืบค้นกำลังดำเนินอยู่การสืบค้นที่เท่าเทียมกันอื่น ๆ ก็จะเพิ่มขึ้นใน bandwagon ดังนั้นคุณสามารถมี 100 การสอบถามในการไปกลับฐานข้อมูลเดียว


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

มันเป็นเพียงตัวอย่างที่เป็นนามธรรมเพื่ออธิบายว่าลูปเหตุการณ์สามารถนำเสนอประสิทธิภาพมากขึ้นได้อย่างไร nodejs ไม่ทำอะไรกับ DB โดยไม่มีโมดูลพิเศษ;)
BGerrissen

1
ใช่ความคิดเห็นของฉันมีมากขึ้นต่อการค้นหา 100 ครั้งในการไปกลับฐานข้อมูลเดียว : p
Tor Valamo

2
สวัสดี BGerrissen: โพสต์ที่ดี ดังนั้นเมื่อแบบสอบถามกำลังดำเนินการแบบสอบถามที่คล้ายกันอื่น ๆ จะ "ฟัง" เช่น staticFile.X ตัวอย่างข้างต้น? ตัวอย่างเช่นผู้ใช้ 100 คนที่ดึงข้อความค้นหาเดียวกันจะมีการดำเนินการค้นหาเพียงหนึ่งแบบสอบถามเท่านั้นและอีก 99 รายการจะรับฟังคำถามแรก ขอบคุณมาก!
CHAPa

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