คอมไพเลอร์ C ++ เอาวงเล็บที่ไม่มีประโยชน์ออกหรือไม่


19

รหัสจะ

int a = ((1 + 2) + 3); // Easy to read

ทำงานช้ากว่า

int a = 1 + 2 + 3; // (Barely) Not quite so easy to read

หรือคอมไพเลอร์สมัยใหม่ฉลาดพอที่จะลบ / ปรับวงเล็บ "ไร้ประโยชน์"

ดูเหมือนว่าจะเป็นข้อกังวลในการเพิ่มประสิทธิภาพเล็ก ๆ น้อย ๆ แต่การเลือก C ++ มากกว่า C # / Java / ... นั้นเกี่ยวกับการปรับให้เหมาะสมที่สุด (IMHO)


9
ฉันคิดว่า C # และ Java จะปรับให้เหมาะสมเช่นกัน ฉันเชื่อว่าเมื่อพวกเขาแยกวิเคราะห์และสร้าง AST พวกเขาจะลบสิ่งที่ไร้ประโยชน์ออกไปอย่างชัดเจน
Farid Nouri Neshat

5
ทุกสิ่งที่ฉันได้อ่านชี้ไปที่การรวบรวม JIT ได้อย่างง่ายดายทำให้การรวบรวมเงินล่วงหน้าเป็นไปอย่างราบรื่นดังนั้นเงินเพียงอย่างเดียวจึงไม่ใช่ข้อโต้แย้งที่น่าสนใจมาก คุณนำการเขียนโปรแกรมเกมขึ้นมา - เหตุผลที่แท้จริงในการรวบรวมการรวบรวมล่วงหน้าคือมันสามารถคาดเดาได้ - ด้วยการรวบรวม JIT ที่คุณไม่เคยรู้เมื่อคอมไพเลอร์กำลังจะเตะและลองเริ่มการคอมไพล์โค้ด แต่ฉันจะทราบว่าการคอมไพล์ล่วงหน้าของรหัสพื้นเมืองไม่ได้เกิดขึ้นพร้อมกันกับการรวบรวมขยะดูเช่นมาตรฐาน ML และ D และฉันเคยเห็นข้อโต้แย้งที่น่าเชื่อถือว่าการรวบรวมขยะมีประสิทธิภาพมากขึ้น ...
Doval

7
... มากกว่า RAII และพอยน์เตอร์อัจฉริยะดังนั้นจึงเป็นคำถามที่ตามมาด้วยเส้นทางที่ได้รับความนิยม (C ++) เทียบกับเส้นทางที่ยังไม่ได้อ่านในการทำการเขียนโปรแกรมเกมในภาษาเหล่านั้น ฉันจะทราบด้วยว่าการกังวลเกี่ยวกับวงเล็บนั้นเป็นบ้า - ฉันเห็นว่าคุณมาจากไหน แต่นั่นเป็นการเพิ่มประสิทธิภาพแบบไร้สาระขนาดเล็ก การเลือกโครงสร้างข้อมูลและอัลกอริธึมในโปรแกรมของคุณจะมีประสิทธิภาพเหนือกว่าสิ่งอื่นใด
Doval

6
อืม ... คุณคาดหวังการเพิ่มประสิทธิภาพแบบไหนกันแน่? หากคุณกำลังพูดถึงการวิเคราะห์แบบสแตติกในภาษาส่วนใหญ่ที่ฉันรู้จักสิ่งนี้จะถูกแทนที่ด้วยผลลัพธ์ที่รู้จักแบบคงที่ (การใช้งานแบบ LLVM ที่ใช้แม้จะบังคับใช้เรื่องนี้ AFAIK) หากคุณกำลังพูดถึงลำดับการดำเนินการมันไม่สำคัญเนื่องจากเป็นการดำเนินการเดียวกันและไม่มีผลข้างเคียง นอกจากนี้ยังต้องการตัวถูกดำเนินการสองตัวต่อไป และถ้าคุณใช้สิ่งนี้เพื่อเปรียบเทียบ C ++, Java และ C # เกี่ยวกับประสิทธิภาพดูเหมือนว่าคุณไม่มีความคิดที่ชัดเจนว่าการเพิ่มประสิทธิภาพคืออะไรและทำงานอย่างไรดังนั้นคุณควรมุ่งเน้นที่การเรียนรู้แทน
Theodoros Chatzigiannakis

5
ผมสงสัยว่าทำไมก)คุณพิจารณาอ่านการแสดงออก parenthesised มากขึ้น (กับผมมันก็ดูน่าเกลียดทำให้เข้าใจผิด (ทำไมพวกเขาเน้นคำสั่งนี้โดยเฉพาะ? ไม่ควรจะเป็น assiciative นี่?) และ clunky) ข)เหตุผลที่คุณต้องการโดยไม่ต้องคิด วงเล็บมันอาจทำงานได้ดีกว่า (การแยกวิเคราะห์ parens ง่ายกว่าสำหรับเครื่องจักรมากกว่าการให้เหตุผลเกี่ยวกับฟิกซ์เจอร์ของผู้ปฏิบัติงานตามที่ Marc van Leuwen กล่าวถึงแม้ว่าสิ่งนี้จะไม่มีผลกับรันไทม์)
leftaroundabout

คำตอบ:


87

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

หากคุณไม่เขียนวงเล็บใด ๆ ตัวแยกวิเคราะห์จะต้องยังคงทำงานในการสร้างทรีการแยกวิเคราะห์และกฎสำหรับลำดับความสำคัญของโอเปอเรเตอร์และการเชื่อมโยงจะบอกได้อย่างชัดเจนว่าต้องแยกวิเคราะห์ทรีใด คุณอาจพิจารณากฎเหล่านั้นว่าเป็นการบอกคอมไพเลอร์ซึ่งวงเล็บ (โดยนัย) ควรแทรกลงในรหัสของคุณถึงแม้ว่า parser จะไม่เคยจัดการกับวงเล็บในกรณีนี้จริง ๆ : มันเพิ่งถูกสร้างขึ้นเพื่อสร้างต้นไม้แยกแบบเดียวกับถ้าวงเล็บ มีอยู่ในบางสถานที่ หากคุณใส่วงเล็บไว้ในสถานที่เหล่านั้นเช่นเดียวกับในint a = (1+2)+3;(ความสัมพันธ์ของ+คือไปทางซ้าย) parser จะมาถึงผลลัพธ์เดียวกันโดยเส้นทางที่แตกต่างกันเล็กน้อย หากคุณใส่วงเล็บต่างกันเช่นเดียวกับในint a = 1+(2+3);จากนั้นคุณกำลังบังคับให้ต้นไม้แยกวิเคราะห์ที่แตกต่างกันซึ่งอาจจะทำให้รหัสที่แตกต่างกันจะถูกสร้างขึ้น (แม้ว่าอาจจะไม่ได้เป็นเพราะคอมไพเลอร์อาจใช้การเปลี่ยนแปลงหลังจากการสร้างต้นไม้แยกวิเคราะห์ตราบเท่าที่ผลของการใช้งานรหัสผลลัพธ์จะไม่แตกต่างกัน มัน). หากว่ามีความแตกต่างในรหัส resuting ไม่มีอะไรสามารถพูดได้โดยทั่วไปว่ามีประสิทธิภาพมากขึ้น; จุดที่สำคัญที่สุดคือแน่นอนว่าเวลาส่วนใหญ่ของต้นไม้ในการแยกวิเคราะห์ไม่ให้การแสดงออกทางคณิตศาสตร์ที่เท่ากันดังนั้นการเปรียบเทียบความเร็วในการดำเนินการของพวกเขานั้นอยู่ข้างประเด็น: เราควรเขียนนิพจน์ที่ให้ผลลัพธ์ที่เหมาะสม

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

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


S / oldes / / ที่เก่าแก่ที่สุด คำตอบที่ดี +1
David Conrad

23
คำเตือน nitpick ทั้งหมด: "คำถามนี้ไม่ได้รับการตอบรับอย่างดี" - ฉันไม่เห็นด้วยที่จะ "ใส่คำว่าดี" ให้หมายถึง "แนะนำอย่างชัดเจนว่าช่องว่างความรู้ที่ต้องการเติมเต็ม" ในสาระสำคัญคำถามคือ "การเพิ่มประสิทธิภาพสำหรับ X เลือก A หรือ B และทำไมเกิดอะไรขึ้นภายใต้ใต้" ซึ่งอย่างน้อยสำหรับฉันแนะนำอย่างชัดเจนว่าช่องว่างความรู้คืออะไร ข้อบกพร่องในคำถามที่คุณชี้ไปยังที่อยู่อย่างถูกต้องและดีคือมันสร้างขึ้นในแบบจำลองทางจิตที่ผิดพลาด
Jonas Kölker

สำหรับa = b + c * d;, a = b + (c * d);จะเป็น [ไม่เป็นอันตราย] ซ้ำซ้อนวงเล็บ หากพวกเขาช่วยให้คุณอ่านรหัสได้ดีขึ้น a = (b + c) * d;จะเป็นวงเล็บไม่ใช่แบบซ้ำซ้อน - จริง ๆ แล้วพวกเขาเปลี่ยนต้นไม้แยกวิเคราะห์ผลลัพธ์และให้ผลลัพธ์ที่แตกต่างกัน ถูกต้องตามกฎหมายอย่างสมบูรณ์แบบ (อันที่จริงจำเป็น) แต่ไม่เหมือนกับการจัดกลุ่มเริ่มต้นโดยนัย
Phil Perry

1
@OrangeDog จริงมันเป็นความคิดเห็นที่น่าอายของเบ็นทำให้มีคนไม่กี่คนที่ชอบเรียกร้อง VMs เร็วกว่าเจ้าของภาษา
gbjbaanb

1
@ JonasKölker: ประโยคเริ่มต้นของฉันอ้างถึงคำถามตามสูตรในชื่อจริง ๆ : หนึ่งไม่สามารถตอบคำถามเกี่ยวกับว่าคอมไพเลอร์แทรกหรือลบวงเล็บเนื่องจากที่อยู่บนพื้นฐานของความเข้าใจผิดว่าคอมไพเลอร์ทำงานอย่างไร แต่ฉันเห็นด้วยมันค่อนข้างชัดเจนว่าช่องว่างความรู้จำเป็นต้องได้รับการแก้ไข
Marc van Leeuwen

46

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

FYI คอมไพเลอร์ฉลาดพอที่จะปรับมันให้เหมาะสมโดยสิ้นเชิงถ้าทำได้ ในตัวอย่างของคุณสิ่งนี้จะกลายเป็นint a = 6;เมื่อรวบรวม


9
อย่าง - ติดเป็นจำนวนมากในวงเล็บตามที่คุณต้องการและปล่อยให้คอมไพเลอร์จะทำงานหนักของการหาสิ่งที่คุณต้องการ :)
gbjbaanb

23
@Serge การเขียนโปรแกรมที่แท้จริงนั้นเกี่ยวกับความสามารถในการอ่านรหัสของคุณมากกว่าเกี่ยวกับประสิทธิภาพ คุณจะเกลียดตัวเองในปีหน้าเมื่อคุณต้องการแก้ไขข้อผิดพลาดและคุณมีรหัส "ที่ดีที่สุด" ที่จะไปด้วย
วงล้อประหลาด

1
@ ratchetfreak คุณพูดถูกและฉันยังรู้วิธีที่จะแสดงความคิดเห็นรหัสของฉัน int a = 6; // = (1 + 2) + 3
Serge

20
@Serge ฉันรู้ว่าจะไม่ระงับหลังจากปีของการปรับแต่งในเวลาความคิดเห็นและรหัสจะหายไปจากนั้นคุณก็จบลงด้วยint a = 8;// = 2*3 + 5
วงล้อประหลาด

21
หรือจาก www.thedailywtf.com:int five = 7; //HR made us change this to six...
Mooing Duck

23

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

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

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

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

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

short int a = 30001, b = 30002, c = 30003;
int d = -a + b + c;    // ok
int d = (-a + b) + c;  // ok, same code
int d = (-a + b + c);  // ok, same code
int d = ((((-a + b)) + c));  // ok, same code
int d = -a + (b + c);  // undefined behaviour, different code

ดังนั้นเพิ่มวงเล็บถ้าคุณต้องการ แต่ให้แน่ใจว่าพวกเขาไม่จำเป็นจริงๆ!

ฉันไม่เคยทำ มีความเสี่ยงที่จะเกิดข้อผิดพลาดโดยไม่มีประโยชน์


เชิงอรรถ: พฤติกรรมที่ไม่ได้ลงนามเกิดขึ้นเมื่อนิพจน์จำนวนเต็มที่ลงนามแล้วประเมินเป็นค่าที่อยู่นอกช่วงที่สามารถแสดงออกได้ในกรณีนี้ -32767 ถึง +32767 นี่เป็นหัวข้อที่ซับซ้อนเกินขอบเขตสำหรับคำตอบนี้


พฤติกรรมที่ไม่ได้กำหนดในบรรทัดสุดท้ายเป็นเพราะชอร์ตสั้นมีเพียง 15 บิตหลังเครื่องหมายดังนั้นขนาดสูงสุด 32767 ใช่ไหม? ในตัวอย่างเล็ก ๆ น้อย ๆ คอมไพเลอร์ควรเตือนเกี่ยวกับการล้น +1 สำหรับตัวอย่างตัวนับทั้งสองทาง หากเป็นพารามิเตอร์ของฟังก์ชันคุณจะไม่ได้รับคำเตือน นอกจากนี้หากaไม่สามารถลงนามได้อย่างแท้จริงนำการคำนวณของคุณกับ-a + bสามารถล้นได้ง่ายเช่นกันหากaเป็นลบและbบวก
Patrick M

@PatrickM: ดูการแก้ไข พฤติกรรมที่ไม่ได้กำหนดหมายถึงคอมไพเลอร์สามารถทำสิ่งที่มันชอบรวมถึงการออกคำเตือนหรือไม่ เลขคณิตที่ไม่ได้ลงนามไม่ได้สร้าง UB แต่จะถูกลดค่าโมดูโลลงด้วยกำลังที่สูงขึ้นถัดไปของทั้งสอง
david.pfx

การแสดงออก(b+c)ในบรรทัดสุดท้ายจะส่งเสริมการขัดแย้งของมันintดังนั้นถ้าคอมไพเลอร์กำหนดintเป็น 16 บิต (เพราะมันเป็นโบราณหรือมีเป้าหมายเป็นไมโครคอนโทรลเลอร์ขนาดเล็ก) บรรทัดสุดท้ายจะถูกต้องตามกฎหมายอย่างสมบูรณ์
supercat

@supercat: ฉันไม่คิดอย่างนั้น ประเภททั่วไปและประเภทของผลลัพธ์ควรสั้น int หากไม่ใช่สิ่งที่เคยถูกถามคุณอาจต้องการโพสต์คำถามหรือไม่
david.pfx

@ david.pfx: กฎของ C โปรโมชั่นทางคณิตศาสตร์ที่มีความสวยใส: ทุกอย่างที่มีขนาดเล็กกว่าintได้รับการเลื่อนตำแหน่งให้เว้นแต่ประเภทนั้นจะไม่สามารถที่จะเป็นตัวแทนของค่าทั้งหมดในกรณีที่มันได้รับการเลื่อนตำแหน่งให้int unsigned intคอมไพเลอร์อาจหลงลืมโปรโมชั่นถ้าพฤติกรรมที่กำหนดไว้ทั้งหมดจะเป็นเช่นเดียวกับถ้าโปรโมชั่นถูกรวม บนเครื่องที่ประเภท 16 บิตทำหน้าที่เป็นแหวนพีชคณิตนามธรรมห่อ (a + b) + c และ a + (b + c) จะเทียบเท่า ถ้าintเป็นชนิด 16 บิตที่ติดอยู่บนล้น แต่แล้วจะมีกรณีที่หนึ่งในการแสดงออก ...
SuperCat

7

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

ดังนั้นที่คุณและฉันอาจเห็น ...

  • "int a = ((1 + 2) + 3);"

... คอมไพเลอร์อาจปล่อยบางอย่างเช่นนี้:

  • ถ่าน [1] :: "A"
  • Int32 :: DeclareStackVariable ()
  • Int32 :: 0x00000001
  • Int32 :: 0x00000002
  • Int32 :: เพิ่ม ()
  • Int32 :: 0x00000003
  • Int32 :: เพิ่ม ()
  • Int32 :: AssignToVariable ()
  • เป็นโมฆะ :: DiscardResult ()

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

ตกลงมันไม่เหมือนสิ่งที่คุณและฉันจัดการกับ แต่แล้วเราไม่ได้ใช้มัน!


4
ไม่มีคอมไพเลอร์ C ++ เพียงตัวเดียวที่จะผลิตสิ่งใด ๆ จากระยะไกลเช่นนี้ โดยทั่วไปแล้วพวกเขาผลิตรหัส CPU ที่แท้จริงและการชุมนุมไม่ได้ดูอย่างนี้
MSalters

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

@Malters Nitpicking เสียงดังกราวจะเปล่ง 'บางอย่างเช่นนั้น' ถ้าคุณปฏิบัติต่อ LLVM ISA เป็น 'สิ่งที่คล้าย' (SSA ไม่ใช่สแต็กตาม) ระบุว่าเป็นไปได้ที่จะเขียนแบ็กเอนด์ JVM สำหรับ LLVM และ JVM ISA (AFAIK) เป็นเสียงสแต็คตามสแต็ก -> llvm-> JVM จะมีลักษณะคล้ายกันมาก
Maciej Piechotka

ฉันไม่คิดว่า LLVM ISA มีความสามารถในการกำหนดชื่อตัวแปรสแต็กโดยใช้ตัวอักษรสตริงรันไทม์ (เพียงสองคำสั่งแรก) นี่คือการผสมขึ้นอย่างจริงจังเวลาทำงานและรวบรวมเวลา ความแตกต่างสำคัญเพราะคำถามนี้เกี่ยวกับความสับสนนั้น
MSalters

6

ขึ้นอยู่กับว่าเป็นทศนิยมหรือไม่:

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

  • ในการดำเนินการจำนวนเต็มพวกเขาสามารถได้รับการจัดเรียงใหม่

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

อย่างไรก็ตามแม้จาวาและ C # จะสามารถเพิ่มประสิทธิภาพได้ แต่พวกเขาจะทำที่รันไทม์


+1 สำหรับการนำเสนอการดำเนินการจุดลอยตัวนั้นไม่ได้เชื่อมโยงกัน
Doval

ในตัวอย่างของคำถามวงเล็บไม่เปลี่ยนแปลงการเชื่อมโยงเริ่มต้น (ซ้าย) ดังนั้นจุดนี้จึงเป็นสิ่งที่สงสัย
Marc van Leeuwen

1
เกี่ยวกับประโยคสุดท้ายฉันไม่คิดอย่างนั้น ในทั้ง java a และ c # คอมไพเลอร์จะสร้าง bytecode / IL ให้ได้ประสิทธิภาพสูงสุด รันไทม์ไม่ได้รับผลกระทบ
Stefano Altieri

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

6

โดยทั่วไป C ++ คอมไพเลอร์แปลว่ารหัสเครื่องไม่ c ++ ตัวเอง มันลบ parens ที่ไร้ประโยชน์ใช่แล้วเพราะเมื่อถึงเวลามันก็ไม่มี parens เลย รหัสเครื่องไม่ทำงานอย่างนั้น


5

รหัสทั้งคู่จบลงด้วยรหัสอย่างหนักเป็น 6:

movl    $6, -4(%rbp)

ตรวจสอบด้วยตัวเองที่นี่


2
แอสเซมบลีนี้คืออะไร
Peter Mortensen

มันเป็นคอมไพเลอร์หลอกว่ารหัสแปลงซีใน x86 ชุมนุมออนไลน์
MonoThreaded

1

ไม่ แต่ใช่ แต่อาจจะ แต่อาจเป็นวิธีอื่น แต่ไม่ใช่

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

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

แต่มีคำถามของคุณมากกว่านี้

ในกรณีของคอมไพเลอร์ C, C ++, Java หรือ C # ใด ๆ ฉันคาดว่าผลลัพธ์ของคำสั่งทั้งสองที่คุณให้ไว้จะมีผลลัพธ์เหมือนกับ:

int a = 6;

ทำไมรหัสผลลัพธ์เสียเวลาทำคณิตศาสตร์กับตัวอักษร? ไม่มีการเปลี่ยนแปลงสถานะของโปรแกรมที่จะหยุดผลของ1 + 2 + 3การเป็น6ดังนั้นนั่นคือสิ่งที่ควรไปในรหัสกำลังดำเนินการ ที่จริงแล้วอาจจะไม่ใช่เช่นนั้น (ขึ้นอยู่กับสิ่งที่คุณทำกับ 6 นั้นบางทีเราสามารถทิ้งทุกอย่างออกไปได้และแม้แต่ C # ด้วยปรัชญาของ "อย่าปรับให้เหมาะสมอย่างหนัก เทียบเท่าint a = 6หรือเพียงแค่ทิ้งทุกอย่างออกไปโดยไม่จำเป็น)

ถึงแม้ว่าสิ่งนี้จะทำให้เราสามารถขยายคำถามของคุณได้ พิจารณาสิ่งต่อไปนี้:

int a = (b - 2) / 2;
/* or */
int a = (b / 2)--;

และ

int c;
if(d < 100)
  c = 0;
else
  c = d * 31;
/* or */
int c = d < 100 ? 0 : d * 32 - d
/* or */
int c = d < 100 && d * 32 - d;
/* or */
int c = (d < 100) * (d * 32 - d);

(หมายเหตุตัวอย่างสองตัวอย่างสุดท้ายนี้ไม่ถูกต้อง C # ในขณะที่ทุกอย่างอื่นที่นี่คือและพวกเขาจะถูกต้องใน C, C ++ และ Java.)

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

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

ในแต่ละของพวกเขามีเหตุผลที่สงสัยว่าอาจจะเร็วกว่าคนอื่น decrements โสดอาจจะมีการเรียนการสอนเฉพาะดังนั้นแน่นอนอาจจะเร็วกว่า(b / 2)-- บางทีอาจจะสามารถผลิตได้เร็วขึ้นโดยการเปลี่ยนมันเป็นเช่นนั้นทำให้เร็วกว่า(b - 2) / 2d * 32d << 5d * 32 - dd * 31เร็วกว่าความแตกต่างระหว่างสองสิ่งสุดท้ายนั้นน่าสนใจเป็นพิเศษ อนุญาตให้มีการประมวลผลบางอย่างที่จะข้ามในบางกรณี แต่อื่น ๆ หลีกเลี่ยงความเป็นไปได้ของการคาดการณ์ผิดพลาดสาขา

ดังนั้นนี่ทำให้เรามีคำถามสองข้อ: 1. จริง ๆ แล้วเร็วกว่าอีกคำถามหนึ่ง? 2. คอมไพเลอร์จะแปลงช้ากว่าให้เร็วขึ้นหรือไม่

และคำตอบคือ 1 มันขึ้นอยู่กับ 2. อาจจะ

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

และอาจเนื่องจากมีหลาย optimisations ที่ compilers (และ jitters และ script-engine) จะทำและในขณะที่บางคนอาจได้รับคำสั่งในบางกรณีเราโดยทั่วไปจะสามารถหารหัสเทียบเท่าที่มีเหตุผลบางอย่างที่ แม้แต่คอมไพเลอร์ที่ไร้เดียงสาที่สุดก็มีผลลัพธ์เหมือนกันและบางส่วนของโค้ดที่มีเหตุผลเทียบเท่าซึ่งแม้แต่โค้ดที่ซับซ้อนที่สุดก็ยังสร้างโค้ดที่เร็วกว่าสำหรับอีกอันหนึ่งสำหรับอีกอันหนึ่ง (แม้ว่าเราจะต้องเขียนบางอย่างทางพยาธิวิทยาทั้งหมด

ดูเหมือนว่าจะเป็นข้อกังวลในการเพิ่มประสิทธิภาพขนาดเล็กมาก

ไม่แม้แต่กับความแตกต่างที่ซับซ้อนกว่าที่ฉันให้ไว้ที่นี่ดูเหมือนว่าจะเป็นข้อกังวลเล็กน้อยที่ไม่เกี่ยวข้องกับการปรับให้เหมาะสม ถ้ามีอะไรที่มันเป็นเรื่องของ pessimisation ตั้งแต่คุณสงสัยว่ายากที่จะอ่านอาจจะช้ากว่าให้อ่านง่ายขึ้น((1 + 2) + 31 + 2 + 3

แต่การเลือก C ++ มากกว่า C # / Java / ... นั้นเป็นเรื่องของการปรับให้เหมาะสมที่สุด (IMHO)

ถ้านั่นคือสิ่งที่เลือก C ++ มากกว่า C # หรือ Java คือ "all about" ฉันบอกว่าคนควรจะเขียนสำเนาของ Stroustrup และ ISO / IEC 14882 และเพิ่มพื้นที่ว่างของคอมไพเลอร์ C ++ เพื่อให้มีที่ว่างสำหรับ MP3 หรืออะไรมากกว่านั้น

ภาษาเหล่านี้มีข้อดีที่แตกต่างกัน

หนึ่งในนั้นคือ C ++ โดยทั่วไปจะยังเร็วกว่าและเบากว่าเมื่อใช้หน่วยความจำ ใช่มีตัวอย่างที่ C # และ / หรือ Java เร็วขึ้นและ / หรือมีการใช้หน่วยความจำตลอดอายุการใช้งานของแอปพลิเคชันที่ดีขึ้นและสิ่งเหล่านี้เป็นเรื่องธรรมดามากขึ้นเมื่อเทคโนโลยีที่เกี่ยวข้องปรับปรุง แต่เรายังคาดหวังว่า executable ขนาดเล็กที่ทำงานได้เร็วขึ้นและใช้หน่วยความจำน้อยกว่าเทียบเท่าในภาษาใดภาษาหนึ่ง

นี่ไม่ใช่การเพิ่มประสิทธิภาพ

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

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

การเพิ่มประสิทธิภาพคือเมื่อเราทำการปรับปรุงในแง่มุมหนึ่งและ / หรือกรณีเฉพาะ ตัวอย่างทั่วไปคือ:

  1. ตอนนี้มันเร็วกว่าสำหรับเคสที่ใช้เพียงเคสเดียว แต่จะช้ากว่าสำหรับเคสอื่น
  2. ตอนนี้เร็วขึ้น แต่ใช้หน่วยความจำมากขึ้น
  3. ตอนนี้มันเบาลงในหน่วยความจำ แต่ช้ากว่า
  4. ตอนนี้เร็วขึ้น แต่ยากที่จะรักษา
  5. ตอนนี้มันง่ายต่อการบำรุงรักษา แต่ช้าลง

กรณีดังกล่าวจะมีเหตุผลหากเช่น:

  1. กรณีการใช้งานที่เร็วขึ้นนั้นพบได้บ่อยหรือมีอุปสรรคมากขึ้นในการเริ่มต้น
  2. โปรแกรมช้าและไม่ได้รับการยอมรับและเรามี RAM จำนวนมากฟรี
  3. โปรแกรมกำลังหยุดทำงานเนื่องจากใช้ RAM จำนวนมากจึงใช้เวลาในการแลกเปลี่ยนมากกว่าการประมวลผลเร็วสุด
  4. โปรแกรมช้าอย่างไม่อาจยอมรับได้และรหัสที่ยากต่อการทำความเข้าใจนั้นเป็นเอกสารที่ดีและค่อนข้างเสถียร
  5. โปรแกรมยังคงยอมรับได้อย่างรวดเร็วและยิ่งรหัสฐานที่เข้าใจได้ถูกกว่าเพื่อรักษาและช่วยให้การปรับปรุงอื่น ๆ ทำได้ง่ายขึ้น

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

และการเลือกภาษามีผลกระทบที่นี่เพราะความเร็วการใช้หน่วยความจำและความสามารถในการอ่านสามารถได้รับผลกระทบจากมัน แต่สามารถเข้ากันได้กับระบบอื่น ๆ ความพร้อมใช้งานของไลบรารีความพร้อมใช้งานของรันไทม์ (เพราะบาปของฉันฉันลงเอยด้วยการใช้ Linux และ Android เป็นระบบปฏิบัติการที่ฉันโปรดปรานและ C # เป็นภาษาที่ฉันชอบและในขณะที่ Mono ยอดเยี่ยม แต่ฉันก็ยังเจอกับเรื่องนี้ไม่มาก)

การพูดว่า "การเลือก C ++ เหนือ C # / Java / ... เป็นเรื่องเกี่ยวกับการปรับให้เหมาะสมที่สุด" จะสมเหตุสมผลถ้าคุณคิดว่า C ++ แย่มากเพราะการเพิ่มประสิทธิภาพนั้นเกี่ยวกับ "ดีกว่าแม้จะ ... " ไม่ "ดีกว่า" หากคุณคิดว่า C ++ นั้นดีกว่าตัวมันเองสิ่งสุดท้ายที่คุณต้องทำคือต้องกังวลเกี่ยวกับ micro-opts ที่เป็นไปได้ แน่นอนคุณน่าจะดีกว่าที่จะละทิ้งมันไปเลย แฮ็กเกอร์ที่มีความสุขคือคุณภาพที่จะเพิ่มประสิทธิภาพด้วยเช่นกัน!

หากคุณมีแนวโน้มที่จะพูดว่า "ฉันรัก C ++ และสิ่งหนึ่งที่ฉันชอบเกี่ยวกับมันคือการบีบรอบพิเศษ" นั่นก็เป็นอีกเรื่องหนึ่ง มันยังคงเป็นกรณีที่ micro-opts นั้นคุ้มค่าหากพวกเขาสามารถเป็นนิสัยที่สะท้อนกลับได้ (นั่นคือวิธีที่คุณมักจะเขียนโค้ดตามธรรมชาติจะเร็วกว่าที่ช้ากว่า) มิฉะนั้นพวกเขาไม่ได้เพิ่มประสิทธิภาพการคลอดก่อนกำหนดพวกเขากำลังมองในแง่ร้ายก่อนกำหนดที่จะทำให้สิ่งเลวร้ายลง


0

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

int a = 1 + 2 + 3;

แทบทุกภาษาที่มีอยู่มีกฎที่ผลรวมจะถูกประเมินโดยการเพิ่ม 1 + 2 จากนั้นเพิ่มผลลัพธ์บวก 3 หากคุณเขียน

int a = 1 + (2 + 3);

จากนั้นวงเล็บจะบังคับให้มีคำสั่งต่างกัน: เพิ่ม 2 + 3 ก่อนจากนั้นเพิ่ม 1 บวกผลลัพธ์ ตัวอย่างของวงเล็บคือคำสั่งเดียวกับที่จะเกิดขึ้น ในตัวอย่างนี้ลำดับของการดำเนินการแตกต่างกันเล็กน้อย แต่วิธีการเพิ่มจำนวนเต็มทำงานได้ผลลัพธ์เหมือนกัน ใน

int a = 10 - (5 - 4);

วงเล็บมีความสำคัญ การออกจากพวกเขาจะเปลี่ยนผลลัพธ์จาก 9 เป็น 1

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


practically every language in existence; ยกเว้น APL: ลอง (ที่นี่) [tryapl.org] การป้อน(1-2)+3(2), 1-(2+3)(-4) และ1-2+3(ยัง -4)
tomsmeding

0

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

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

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


1
ฉันไม่แน่ใจว่าสิ่งนี้ตอบคำถาม ย่อหน้าสนับสนุนมีความสับสนมากกว่าที่เป็นประโยชน์ คอมไพเลอร์ Java เกี่ยวข้องกับคอมไพเลอร์ C ++ อย่างไร
Adam Zuckerman

OP ถามว่าเครื่องหมายวงเล็บหายไปหรือไม่…ฉันบอกว่ามันเป็นและอธิบายเพิ่มเติมว่ารหัสปฏิบัติการเป็นเพียงตัวเลขที่แสดงถึง opcodes Java ถูกนำขึ้นมาในคำตอบอื่น ฉันคิดว่ามันตอบคำถามได้ดี… แต่นั่นเป็นเพียงความคิดเห็นของฉัน ขอบคุณที่ตอบกลับ
jinzai

3
วงเล็บไม่บังคับ "คำสั่งของการดำเนินการ" พวกเขาเปลี่ยนลำดับความสำคัญ ดังนั้นในa = f() + (g() + h());คอมไพเลอร์เป็นอิสระในการโทรf, gและhอยู่ในลำดับที่ (หรือในลำดับใดประสงค์)
Alok

ฉันไม่เห็นด้วยกับข้อความนั้น…คุณสามารถบังคับลำดับการใช้งานได้ด้วยวงเล็บ
jinzai

0

คอมไพเลอร์โดยไม่คำนึงถึงภาษาแปลคณิตศาสตร์มัดเป็น postfix กล่าวอีกนัยหนึ่งเมื่อคอมไพเลอร์เห็นสิ่งที่ชอบ:

((a+b)+c)

มันแปลเป็นนี้:

 a b + c +

สิ่งนี้เกิดขึ้นเพราะในขณะที่คนอื่น ๆ อ่านได้ง่ายกว่าสัญกรณ์สัญกรณ์ postfix สัญกรณ์ใกล้กับขั้นตอนที่คอมพิวเตอร์ต้องใช้เพื่อให้งานเสร็จ (และเพราะมีอัลกอริธึม - พัฒนาแล้ว) โดย คำจำกัดความ postfix ได้ขจัดปัญหาทั้งหมดที่เกิดขึ้นกับ order-of-operation หรือวงเล็บซึ่งทำให้สิ่งต่าง ๆ ง่ายขึ้นเมื่อเขียนรหัสเครื่อง

ฉันแนะนำบทความวิกิพีเดียเกี่ยวกับ Reverse Polish Notationสำหรับข้อมูลเพิ่มเติมเกี่ยวกับเรื่องนี้


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