มีโปรเซสเซอร์ที่เคยใช้คำสั่งรากที่สองจำนวนเต็มหรือไม่?
(retrocomputing.stackexchange.com)- ประเด็นไม่ใช่
sqrtแบบเลขทศนิยมลอยตัวที่พบได้ทั่วไป แต่คือเคยมีกรณีที่ให้บริการ รากที่สองจำนวนเต็ม เป็นคำสั่ง CPU หรือฟังก์ชันฮาร์ดแวร์หรือไม่ โดย divider/square-rooter ของ Nintendo DS คล้ายกันแต่ไม่ใช่คำสั่งเนทีฟ - RTX 2000 Forth CPU ของ Harris และ RTX 2010 รุ่นเกรดทหาร ถูกยกเป็นกรณีที่มีคำสั่ง square root แบบหลายขั้นตอน โดย RTX 2000 มีโครงสร้างที่ได้ผลลัพธ์จาก setup 1 ครั้งและ step 15 ครั้ง
- กรณีที่เก่ากว่านั้นคือ ENIAC ซึ่งในปี 1946 ใช้ divider/square-rooter unit ควบคุม decimal integer accumulator เพื่อทำการหารได้สูงสุด 40 ครั้งต่อวินาที หรือคำนวณรากที่สองได้ 3 ครั้งต่อวินาที
- รากที่สองจำนวนเต็มต้องใช้ตัวคูณจำนวนเต็มที่เร็วและความแม่นยำเพียงพอ จึงเป็นภาระหนักสำหรับ CPU ในอดีต และยังมีแนวทางอย่าง
frsqrte/frsqrtsของ ARMv8 ที่แยกการประมาณค่าและการวนซ้ำเพื่อปรับสมดุลความแม่นยำ·ความเร็ว - inverse square root แบบ Quake ไม่ได้มีข้อได้เปรียบด้านประสิทธิภาพโดยทั่วไปอีกต่อไปบนฮาร์ดแวร์สมัยใหม่ ส่วนการค้นตาราง·การอินเตอร์โพเลต·การวนซ้ำตระกูล Halley·divide and conquer แบบ fixed-point ล้วนเป็นตัวเลือกที่ขึ้นกับสภาพแวดล้อมการใช้งาน
ขอบเขตของคำถามและกรณีของ Nintendo DS
- คำถามนี้ว่าด้วยว่าเคยมีโปรเซสเซอร์ที่ใช้งาน คำสั่งรากที่สองจำนวนเต็ม จริงหรือไม่
- คำสั่ง square root สำหรับเลขทศนิยมลอยตัวพบได้ทั่วไป แต่เริ่มจากสมมติฐานว่าผู้ถามไม่เคยเห็นคำสั่ง square root เฉพาะจำนวนเต็ม
- Nintendo DS มี integer divider/square rooter แบบ memory-mapped
- เนื่องจากโปรเซสเซอร์ ARM ไม่มี FPU หรือ hardware divider จึงช่วยในการคำนวณ 3D
- อย่างไรก็ตาม จุดสำคัญของคำถามคือสิ่งนี้ ไม่ใช่คำสั่งโปรเซสเซอร์เนทีฟ
Harris RTX 2000 และ RTX 2010
- RTX 2000 Forth CPU ของ Harris ถูกกล่าวถึงว่าเป็นกรณีที่มีคำสั่ง square root แบบหลายขั้นตอน
- RTX 2010 ซึ่งเป็นรุ่นพี่น้องเกรดทหาร ก็มีฟังก์ชันในตระกูลเดียวกัน
- มีการเชื่อมโยงเอกสารที่เกี่ยวข้องคือ Stack Computers: RTX 2000
- ตาม RTX2000 Family Programmer’s Reference Manual ฟังก์ชันนี้ใกล้เคียงกับ square root แบบวนซ้ำ โดยใช้วิธีรัน คำสั่ง setup 1 คำสั่ง และ คำสั่ง step 15 คำสั่ง เพื่อให้ได้ค่าสุดท้าย
- “A Fast Method for Finding an Integer Square Root” ของ Ken Lyons ก็ถูกกล่าวถึงว่าเป็นเอกสารที่ว่าด้วยการใช้งานฮาร์ดแวร์และตัวอย่างการเขียนโปรแกรมในตระกูล RTX2000
divider/square-rooter unit ของ ENIAC
- ENIAC ในปี 1946 ก็จัดเป็นกรณีของฮาร์ดแวร์รากที่สองจำนวนเต็มด้วย
- ตามคำอธิบายที่ยกมา ENIAC ใช้ accumulator 4 ตัวที่ถูกควบคุมโดย multiplier unit พิเศษ เพื่อทำการคูณได้สูงสุด 385 ครั้งต่อวินาที
- accumulator 5 ตัวถูกควบคุมโดย divider/square-rooter unit พิเศษ เพื่อประมวลผลการหารได้สูงสุด 40 ครั้งต่อวินาที หรือ การคำนวณรากที่สอง 3 ครั้ง ต่อวินาที
- accumulator ของ ENIAC ทำงานเป็น decimal integer
เหตุผลที่การทำรากที่สองจำนวนเต็มทำได้ยาก
- คำตอบหนึ่งอธิบายวิธีที่มีประสิทธิภาพในการคำนวณ square root ว่าให้หาค่า รากที่สองผกผัน ด้วยการวนซ้ำ Newton-Raphson แล้วคูณกลับกับค่าเดิม
- วิธีนี้เป็นที่รู้จักในชื่อ “Quake method” และใน CPU กับ GPU สมัยใหม่มีกรณีที่ถูกทำให้เป็นรูปแบบทั่วไปด้วยคำสั่งประมาณค่าเริ่มต้นและคำสั่งวนซ้ำ
- ข้อจำกัดสำคัญของแนวทางนี้คือจำเป็นต้องมี multiplier ที่เร็ว
- sqrt แบบ floating-point ต้องใช้ FP multiplier ที่เร็ว และ FPU ก็มีสิ่งนี้
- sqrt แบบจำนวนเต็มต้องใช้ integer multiplier ที่เร็ว แต่คำอธิบายคือในอดีต CPU ส่วนใหญ่ไม่มีฮาร์ดแวร์แบบนั้น
- ยังมีเงื่อนไขเพิ่มเติมว่าหากต้องการความแม่นยำเพียงพอ ต้องมี multiplier ที่เร็วซึ่งมีความกว้างเป็นสองเท่าของความกว้างอินพุต
- เนื่องจากข้อกำหนดด้านความแม่นยำไม่ได้เท่ากันเสมอไป การแยกการประมาณค่าและการวนซ้ำแบบ
frsqrteกับfrsqrtsจึงช่วยปรับจำนวนรอบวนซ้ำให้ตรงกับ จุดแลกเปลี่ยนระหว่างความเร็ว·ความแม่นยำ ที่ต้องการได้
เทคนิค Quake และข้อถกเถียงเรื่องการทำ sqrt สมัยใหม่
- อีกคำตอบหนึ่งโต้แย้งว่าข้ออ้างที่ว่า Quake trick มีประสิทธิภาพที่สุดนั้นไม่ถูกต้องมานานแล้ว และจะใช้ได้เฉพาะเมื่ออยากได้ผลลัพธ์ float คุณภาพต่ำบนฮาร์ดแวร์บางประเภทเท่านั้น
- บนชิปสมัยใหม่ คำสั่ง sqrt เนทีฟเร็วกว่ามาก และมักอยู่ในระดับ ไม่กี่ clock cycle
- วิธีที่เสนอว่าเร็วกว่า คือเก็บตารางค่าที่มีระยะห่างไม่สม่ำเสมอ ดึงสองค่าออกมาอย่างรวดเร็วแล้วอินเตอร์โพเลต จากนั้น shift exponent ฐาน 2 และหากจำเป็นก็ใช้การวนซ้ำหนึ่งครั้งที่ดีกว่า Newton-Raphson
- ตระกูล Halley และวิธีวนซ้ำหลายแบบสามารถลู่เข้าได้เร็วกว่า Newton-Raphson แต่ความเร็วจริงขึ้นกับต้นทุนของแต่ละการดำเนินการ
- สำหรับช่วงที่เป็นจำนวนเต็มล้วน เช่น
2^32สามารถใช้แนวคิดเดียวกันในรูปแบบ fixed-point ได้- มีการเสนอ divide and conquer เป็นวิธีง่าย ๆ สำหรับฮาร์ดแวร์
- สามารถแมปแต่ละ 8 บิตไปยังตารางค่า fixed-point 256 ค่าแล้วค้นหาแบบขนาน จากนั้นทำการคูณ 3 ครั้ง โดย 2 ครั้งทำแบบขนาน เพื่อให้ได้ค่า 32 บิตแล้ว truncate
- งานวิจัยด้านการปรับแต่ง sqrt ยังคงดำเนินต่อไป และมีการยก เอกสาร INRIA HAL เป็นตัวอย่าง
1 ความคิดเห็น
ความคิดเห็นจาก Hacker News
AArch64 NEON มีคำสั่ง URSQRTE จึงใกล้เคียงกับคำถามต้นทางกว่าที่คิด
ถ้ามองค่าขนาด 32 บิตเป็นจำนวนเต็มแบบ fixed-point ที่มีส่วนเศษ 32 บิต ช่วงที่แทนได้จะเป็นค่าที่เว้นระยะเท่ากันตั้งแต่ 0 ถึง 1-ε โดยที่ ε=2^-32
URSQRTE จะคำนวณค่า ส่วนกลับของรากที่สอง แบบประมาณ จากนั้นหารครึ่ง และ clamp ผลลัพธ์ให้อยู่ในช่วง 0 ถึง 1-ε
จำนวนเต็มแบบ fixed-point ไม่ใช่จำนวนเต็มอย่างเคร่งครัด และส่วนกลับของรากที่สองแบบประมาณก็ไม่ใช่รากที่สอง แต่ก็ไปได้ใกล้พอสมควร
FRSQRTE ที่เกี่ยวข้องกันเป็นคำสั่งที่ใช้งานทั่วไปกว่ามาก โดยให้ค่า reciprocal square root แบบประมาณสำหรับ floating-point 32 บิต
ถ้าถามว่าทำได้ใน clock cycle เดียวไหม ถ้ามี lookup table ขนาดใหญ่มากก็ทำได้
ขนาดอาจลดลงได้บ้าง ขึ้นอยู่กับว่าในหนึ่ง clock cycle สามารถรัน logic gate แบบอนุกรมได้มากแค่ไหน
เช่น รากที่สองแบบไบนารีของ 10000 ค่อนข้างคล้ายกับรากที่สองของ 100 แค่ต่างกันที่จำนวนศูนย์
frsqrte) มักถูก implement ด้วยการ lookup table แบบนั้น โดย index ด้วยบิตบางส่วนของ mantissa และบิตล่างสุดของ exponentความแม่นยำโดยรวมใกล้เคียงกับ bf16 (ARM, RISC-V) หรือ fp16 (x86) ดังนั้นถ้าต้องการความแม่นยำสูงขึ้นก็มักทำ Newton-Raphson iteration เพิ่มอีกสองสามรอบ
ในแต่ละขั้นจะคำนวณว่าควรตั้งบิตใหม่ให้ผลลัพธ์
n_oldหรือไม่ ด้วยn2_new = (n_old + (1 << bit))^2 = n2_old + (n_old << (bit + 1)) + (1 << (bit*2))จากนั้นเทียบกับ operand เดิม ถ้ามากกว่าหรือเท่ากับ ก็ 1) ตั้งบิตในผลลัพธ์ และ 2) อัปเดต
n2_oldเป็นn2_newถ้ามีชุดคำสั่ง microcode และ ALU ที่เหมาะสม ก็ทำได้ใน n/2 หรืออาจเป็น n clock cycles และถ้าปรับให้ดีขึ้นอีกก็ลด n ลงเหลือถึง index ของบิตที่ติดอยู่ซ้ายสุดใน operand ได้
ถ้าเป็น lookup table ขนาดใหญ่ก็น่าจะต้องดึงจากหน่วยความจำ ซึ่งจะมี latency จาก cache และ memory hierarchy ไม่ใช่หรือ
คือเก็บ N^2 สำหรับคำตอบ N ทุกตัว
สำหรับจำนวนเต็ม 16 บิตทำได้จริง, 32 บิตก็อาจเป็นไปได้ แต่ 64 บิตคงไม่ไหว
ถ้าขยายคำจำกัดความของ “processor” ไปถึงอุปกรณ์ไฟฟ้ากลไก Friden SRQ สามารถคำนวณรากที่สองด้วยการบวกและ shift เพียงอย่างเดียว โดยไม่มีชิ้นส่วนอิเล็กทรอนิกส์เลยนอกจากมอเตอร์
ต้องตั้งตำแหน่งจุดทศนิยมเองด้วยมือ ดังนั้นในเชิงเทคนิคจะเรียกว่าเป็นการคำนวณจำนวนเต็มก็ได้
วิดีโอ: https://youtu.be/o44a1ao5h8w
ใช้ลำดับ
1 + 3 + 5 + ... + 2k + 1ก็หาค่า รากที่สองแบบจำนวนเต็ม ของจำนวนเต็มใด ๆ ได้ไม่ใช่หรือ?โดยพื้นฐานคือหาค่า k ของพจน์ที่ใกล้ที่สุดในลำดับนั้นซึ่งน้อยกว่าหรือเท่ากับจำนวนของเรา
ตามนิยามก็เป็น algorithm จริง แต่ถ้า implement แบบตรงไปตรงมา แม้กับเลข 32 บิตก็ช้ามาก
ระดับนี้ทำ binary search ไปเลยจะเร็วกว่ามาก
(x+y)^2=x^2+2xy+y^2ร่วมกับข้อสังเกตว่า ไม่ว่าจะอยู่ในฐานใด รากที่สองของเลข 2n หลักจะมีได้มากสุด n หลักเหมือนวิธีคำนวณรากที่สองด้วยมือบนกระดาษแบบทั่วไป
ถ้าประมวลผลทีละ 8 บิต ก็ต้องมีแค่ lookup table สำหรับ รากที่สองของเลข 8 บิต เท่านั้น
ชอบตรงส่วนนี้ในคำตอบด้านล่าง:
ต้องเลื่อนลงไปอ่านสักหน่อย แต่ขำจริงที่คำตอบคือ ENIAC
แค่อ่านเพิ่มนิดเดียวก็จะเห็นว่าตรงกันข้ามเลย
ไอเดียฉลาด ๆ ส่วนใหญ่ในปัจจุบันถูกใช้ในคอมพิวเตอร์ยุค 1940–60 มาแล้ว และกำลังถูกนำกลับมาใช้ในชิปเซมิคอนดักเตอร์รุ่นใหม่
อย่าง pipelining, out-of-order execution, multi-core เป็นต้น
ฮาร์ดแวร์ยุคเก่าอาจจะดู “หยาบ” ไปบ้าง แต่ในสถาปัตยกรรมมีเทคนิคที่เฉียบแหลมมาก ๆ ใช้อยู่
2 ^ (1/2 * Log2(X)) = sqrt(X)ถ้าเปลี่ยน
Log2(x)เป็น การนับจำนวน leading zeros ก็จะได้ค่าประมาณแบบหยาบมากถ้าประมาณ
Log(2)ให้ดีขึ้น ก็เข้าใกล้คำตอบได้มากขึ้นถ้าไม่ต้องการคำตอบที่ถูกต้องเป๊ะถึงจำนวนเต็มที่ใกล้ที่สุด แต่เอาแค่ค่าประมาณหยาบมาก ๆ ก็แค่ right shift ไปครึ่งหนึ่งของตำแหน่งบิต 1 ตัวแรก
แทบทุก processor มีคำสั่ง shift และคำสั่งอย่าง FLO (Find Leading One) หรือ FFS (Find First Set) ก็ดูเหมือนจะพบได้ทั่วไปจนไม่แน่ใจว่ามีที่ไหนไม่มีบ้าง
ในบางการใช้งาน ค่าประมาณหยาบมากแบบนี้อาจมีประโยชน์พอ ๆ กับคำตอบที่ถูกต้อง
เช่น เวลาต้องการแค่ค่าเริ่มต้นที่เหมาะสมสำหรับ Newton-Raphson iteration หลังจากนั้น
แน่นอนว่า trick การ right shift ก็ใช้เป็นค่าเริ่มต้นสำหรับคำนวณรากที่สองที่แม่นยำขึ้นได้ดีเหมือนกัน :P
ตอนนี้เป็นเรื่องเล่าบนอินเทอร์เน็ตที่ค่อนข้างดังแล้ว มี Carmack กับเลข 32 บิตมหัศจรรย์ปรากฏอยู่
hardware intrinsic ของ CUDA อีกตัวที่ผมชอบเป็นการส่วนตัวคือ
log2ถ้าจำไม่ผิด fixed-point DSP ส่วนใหญ่ หรืออาจจะทั้งหมด มีคำสั่งรากที่สองหรือคำสั่งช่วยสำหรับรากที่สอง
การวิเคราะห์แบบครบถ้วนของ algorithm รากที่สอง ที่กึ่งเกี่ยวข้องและน่าจะน่าสนใจสำหรับแฟน 6502: https://github.com/TobyLobster/sqrt_test