เหตุใด SSE สเกลาร์ sqrt (x) จึงช้ากว่า rsqrt (x) * x


106

ฉันได้จัดทำโปรไฟล์หลักคณิตศาสตร์ของเราใน Intel Core Duo และในขณะที่ดูวิธีการต่างๆของสแควร์รูทฉันสังเกตเห็นว่ามีอะไรแปลก ๆ : การใช้การดำเนินการสเกลาร์ SSE การใช้สแควร์รูทซึ่งกันและกันเร็วกว่าและคูณ เพื่อรับ sqrt มากกว่าที่จะใช้ opcode sqrt ดั้งเดิม!

ฉันกำลังทดสอบด้วยการวนซ้ำเช่น:

inline float TestSqrtFunction( float in );

void TestFunc()
{
  #define ARRAYSIZE 4096
  #define NUMITERS 16386
  float flIn[ ARRAYSIZE ]; // filled with random numbers ( 0 .. 2^22 )
  float flOut [ ARRAYSIZE ]; // filled with 0 to force fetch into L1 cache

  cyclecounter.Start();
  for ( int i = 0 ; i < NUMITERS ; ++i )
    for ( int j = 0 ; j < ARRAYSIZE ; ++j )
    {
       flOut[j] = TestSqrtFunction( flIn[j] );
       // unrolling this loop makes no difference -- I tested it.
    }
  cyclecounter.Stop();
  printf( "%d loops over %d floats took %.3f milliseconds",
          NUMITERS, ARRAYSIZE, cyclecounter.Milliseconds() );
}

ฉันได้ลองสิ่งนี้กับร่างกายที่แตกต่างกันสองสามอย่างสำหรับ TestSqrtFunction และฉันมีเวลาบางอย่างที่ทำให้หัวของฉันเกา สิ่งที่เลวร้ายที่สุดคือการใช้ฟังก์ชัน sqrt () แบบเนทีฟและปล่อยให้คอมไพเลอร์ "สมาร์ท" เพิ่มประสิทธิภาพ " ที่ 24ns / float โดยใช้ x87 FPU สิ่งนี้ไม่ดีอย่างน่าสมเพช:

inline float TestSqrtFunction( float in )
{  return sqrt(in); }

สิ่งต่อไปที่ฉันลองใช้คือการใช้ภายในเพื่อบังคับให้คอมไพเลอร์ใช้สเกลาร์ sqrt opcode ของ SSE:

inline void SSESqrt( float * restrict pOut, float * restrict pIn )
{
   _mm_store_ss( pOut, _mm_sqrt_ss( _mm_load_ss( pIn ) ) );
   // compiles to movss, sqrtss, movss
}

ดีกว่านี้ที่ 11.9ns / float ฉันยังลองใช้เทคนิคการประมาณค่า Newton-Raphson ที่แปลกประหลาดของ Carmackซึ่งทำงานได้ดีกว่าฮาร์ดแวร์ที่ 4.3ns / float แม้ว่าจะมีข้อผิดพลาด 1 ใน 2 10 (ซึ่งมากเกินไปสำหรับวัตถุประสงค์ของฉัน)

doozy คือตอนที่ฉันลอง SSE op สำหรับสแควร์รูทซึ่งกันและกันจากนั้นใช้การคูณเพื่อรับสแควร์รูท (x * 1 / √x = √x) แม้ว่าจะใช้เวลาดำเนินการสองอย่าง แต่ก็เป็นวิธีแก้ปัญหาที่เร็วที่สุดที่ 1.24ns / float และแม่นยำถึง 2 -14 :

inline void SSESqrt_Recip_Times_X( float * restrict pOut, float * restrict pIn )
{
   __m128 in = _mm_load_ss( pIn );
   _mm_store_ss( pOut, _mm_mul_ss( in, _mm_rsqrt_ss( in ) ) );
   // compiles to movss, movaps, rsqrtss, mulss, movss
}

คำถามของฉันคืออะไรให้ ? เหตุใด opcode สแควร์รูทในตัวของ SSE จึงช้ากว่าการสังเคราะห์จากการคำนวณทางคณิตศาสตร์อื่น ๆ อีกสองรายการ

ฉันแน่ใจว่านี่เป็นต้นทุนของ op จริงๆเพราะฉันได้ตรวจสอบแล้ว:

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

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


14
x / sqrt (x) = sqrt (x) หรือใส่วิธีอื่น: x ^ 1 * x ^ (- 1/2) = x ^ (1 - 1/2) = x ^ (1/2) = sqrt (x)
Crashworks

6
แน่นอนinline float SSESqrt( float restrict fIn ) { float fOut; _mm_store_ss( &fOut, _mm_sqrt_ss( _mm_load_ss( &fIn ) ) ); return fOut; }. แต่นี่เป็นความคิดที่ไม่ดีเพราะมันสามารถกระตุ้นให้เกิด load-hit-store ได้อย่างง่ายดายหาก CPU เขียนลอยไปที่สแต็กแล้วอ่านกลับทันที - การเล่นกลจาก vector register ไปยัง float register สำหรับค่าส่งคืนโดยเฉพาะ เป็นข่าวร้าย นอกจากนี้ opcodes เครื่องต้นแบบที่ SSE intrinsics เป็นตัวแทนจะใช้ที่อยู่ถูกดำเนินการต่อไป
Crashworks

4
ความสำคัญของ LHS ขึ้นอยู่กับ Gen เฉพาะและการก้าวของ x86 ที่กำหนด: ประสบการณ์ของฉันคือในทุกสิ่งที่สูงถึง i7 การย้ายข้อมูลระหว่างชุดลงทะเบียน (เช่น FPU ไปยัง SSE ถึงeax) นั้นแย่มากในขณะที่การเดินทางไปกลับระหว่าง xmm0 และ stack และกลับไม่ใช่เพราะการส่งต่อร้านค้าของ Intel คุณสามารถจับเวลาด้วยตัวคุณเองเพื่อดูได้อย่างแน่นอน โดยทั่วไปวิธีที่ง่ายที่สุดในการดู LHS ที่เป็นไปได้คือดูที่ชุดประกอบที่ปล่อยออกมาและดูว่าข้อมูลถูกเล่นกลระหว่างชุดทะเบียน คอมไพเลอร์ของคุณอาจทำสิ่งที่ชาญฉลาดหรืออาจไม่ เกี่ยวกับการทำให้เวกเตอร์เป็นมาตรฐานฉันเขียนผลลัพธ์ไว้ที่นี่: bit.ly/9W5zoU
Crashworks

2
สำหรับ PowerPC ใช่: IBM มีตัวจำลอง CPU ที่สามารถทำนาย LHS และฟองไปป์ไลน์อื่น ๆ อีกมากมายผ่านการวิเคราะห์แบบคงที่ PPC บางตัวยังมีตัวนับฮาร์ดแวร์สำหรับ LHS ที่คุณสามารถสำรวจได้ มันยากกว่าสำหรับ x86; เครื่องมือทำโปรไฟล์ที่ดีเป็นสิ่งที่หายาก (VTune ค่อนข้างเสียในปัจจุบัน) และท่อที่เรียงลำดับใหม่มีความละเอียดน้อยกว่า คุณสามารถลองวัดผลเชิงประจักษ์โดยการวัดคำแนะนำต่อรอบซึ่งสามารถทำได้อย่างแม่นยำด้วยตัวนับประสิทธิภาพของฮาร์ดแวร์ คุณสามารถอ่านการลงทะเบียน "คำแนะนำที่เลิกใช้" และ "รอบทั้งหมด" ได้เช่น PAPI หรือ PerfSuite ( bit.ly/an6cMt )
Crashworks

2
นอกจากนี้คุณยังสามารถเขียนเรียงสับเปลี่ยนบางส่วนบนฟังก์ชันและตั้งเวลาเพื่อดูว่ามีผู้ประสบปัญหาใดโดยเฉพาะจากคอกม้า Intel ไม่ได้เผยแพร่รายละเอียดมากมายเกี่ยวกับวิธีการทำงานของไปป์ไลน์ของพวกเขา (นั่นคือ LHS เลยเป็นความลับที่สกปรก) ดังนั้นสิ่งที่ฉันได้เรียนรู้มากมายคือการดูสถานการณ์ที่ทำให้เกิดการหยุดชะงักในส่วนโค้งอื่น ๆ (เช่น PPC ) แล้วสร้างการทดลองควบคุมเพื่อดูว่า x86 มีด้วยหรือไม่
Crashworks

คำตอบ:


216

sqrtssให้ผลลัพธ์ที่ปัดเศษอย่างถูกต้อง rsqrtssให้การประมาณค่าซึ่งกันและกันแม่นยำประมาณ 11 บิต

sqrtssกำลังสร้างผลลัพธ์ที่แม่นยำกว่ามากเมื่อต้องการความแม่นยำ rsqrtssมีอยู่สำหรับกรณีที่ค่าประมาณพอเพียง แต่ต้องใช้ความเร็ว หากคุณอ่านเอกสารของ Intel คุณจะพบลำดับคำสั่ง (การประมาณค่ารากที่สองซึ่งกันและกันตามด้วยขั้นตอน Newton-Raphson ขั้นตอนเดียว) ที่ให้ความแม่นยำเกือบเต็ม (ประมาณ 23 บิตถ้าฉันจำได้ถูกต้อง) และยังค่อนข้างน้อย เร็วกว่าsqrtss.

แก้ไข:หากความเร็วเป็นสิ่งสำคัญและคุณกำลังเรียกสิ่งนี้แบบวนซ้ำสำหรับหลาย ๆ ค่าคุณควรใช้คำแนะนำเหล่านี้ในเวอร์ชัน vectorized rsqrtpsหรือsqrtpsทั้งสองกระบวนการสี่ลอยต่อคำสั่ง


3
ขั้นตอน n / r ให้ความแม่นยำ 22 บิต (เพิ่มเป็นสองเท่า) 23 บิตจะมีความแม่นยำเต็มรูปแบบ
Jasper Bekkers

7
@Jasper Bekkers: ไม่มันจะไม่ ขั้นแรก float มีความแม่นยำ 24 บิต ประการที่สองsqrtssคือการปัดเศษอย่างถูกต้องซึ่งต้องใช้ ~ 50 บิตก่อนการปัดเศษและไม่สามารถทำได้โดยใช้การวนซ้ำ N / R แบบธรรมดาในความแม่นยำเดียว
Stephen Canon

1
นี่คือเหตุผลแน่นอน เพื่อขยายผลลัพธ์นี้: โครงการ Embree ของ Intel ( software.intel.com/en-us/articles/… ) ใช้ vectorization สำหรับคณิตศาสตร์ คุณสามารถดาวน์โหลดแหล่งที่มาได้ที่ลิงค์นั้นและดูว่าพวกเขาทำเวกเตอร์ 3/4 D ของพวกเขาอย่างไร การทำให้เป็นมาตรฐานเวกเตอร์ของพวกเขาใช้ rsqrt ตามด้วยการวนซ้ำของ newton-raphson ซึ่งมีความแม่นยำมากและยังเร็วกว่า 1 / ssqrt!
Brandon Pelfrey

7
ข้อแม้เล็ก ๆ : x rsqrt (x) ส่งผลให้ NaN ถ้า x เป็นศูนย์หรือไม่มีที่สิ้นสุด 0 * rsqrt (0) = 0 * INF = NaN INF rsqrt (INF) = INF * 0 = NaN ด้วยเหตุนี้ CUDA บน NVIDIA GPU จึงคำนวณค่ารากที่สองที่มีความแม่นยำเดียวโดยประมาณเป็นผู้รับ (rsqrt (x)) โดยฮาร์ดแวร์จะให้ทั้งการประมาณอย่างรวดเร็วไปยังสแควร์รูทซึ่งกันและกันและสแควร์รูทซึ่งกันและกัน เห็นได้ชัดว่าการตรวจสอบอย่างชัดเจนเกี่ยวกับกรณีพิเศษทั้งสองก็เป็นไปได้เช่นกัน (แต่จะช้ากว่าใน GPU)
njuffa

@BrandonPelfrey คุณพบขั้นตอนของ Newton Rhapson ในไฟล์ใด
fredoverflow

7

นอกจากนี้ยังเป็นจริงสำหรับการแบ่ง MULSS (a, RCPSS (b)) เร็วกว่า DIVSS (a, b) ในความเป็นจริงมันยังเร็วกว่าแม้ว่าคุณจะเพิ่มความแม่นยำด้วยการทำซ้ำแบบ Newton-Raphson

Intel และ AMD ต่างแนะนำเทคนิคนี้ในคู่มือการเพิ่มประสิทธิภาพ ในแอปพลิเคชันที่ไม่ต้องการการปฏิบัติตาม IEEE-754 เหตุผลเดียวที่ต้องใช้ div / sqrt คือความสามารถในการอ่านโค้ด


1
Broadwell และใหม่กว่ามีประสิทธิภาพการแบ่ง FP ที่ดีกว่าดังนั้นคอมไพเลอร์อย่าง clang จึงเลือกที่จะไม่ใช้ซึ่งกันและกัน + Newton สำหรับสเกลาร์ในซีพียูล่าสุดเพราะโดยปกติแล้วจะไม่เร็วกว่า ในลูปส่วนใหญ่divไม่ใช่การดำเนินการเพียงอย่างเดียวดังนั้นปริมาณงาน uop ทั้งหมดจึงมักเป็นคอขวดแม้ว่าจะมีdivpsหรือdivss. ดูการหารจุดลอยตัวเทียบกับการคูณจุดลอยตัวซึ่งคำตอบของฉันมีส่วนว่าเหตุใดจึงrcppsไม่ชนะการรับส่งข้อมูลอีกต่อไป (หรือการชนะเวลาแฝง) และตัวเลขในการหารทรูพุต / เวลาแฝง
Peter Cordes

หากข้อกำหนดด้านความแม่นยำของคุณต่ำมากจนคุณสามารถข้ามการวนซ้ำแบบนิวตันได้ใช่a * rcpss(b)อาจเร็วกว่า แต่ก็ยังดีกว่าa/b!
Peter Cordes

5

แทนที่จะให้คำตอบ แต่จริงๆแล้วอาจจะไม่ถูกต้อง (ฉันจะไม่ตรวจสอบหรือโต้แย้งเกี่ยวกับแคชและสิ่งอื่น ๆ สมมติว่าเหมือนกัน) ฉันจะพยายามชี้ให้คุณทราบแหล่งที่มาที่สามารถตอบคำถามของคุณได้
ความแตกต่างอาจอยู่ที่วิธีคำนวณ sqrt และ rsqrt คุณสามารถอ่านเพิ่มเติมได้ที่นี่http://www.intel.com/products/processor/manuals/ ฉันขอแนะนำให้เริ่มต้นจากการอ่านเกี่ยวกับฟังก์ชั่นโปรเซสเซอร์ที่คุณใช้มีข้อมูลบางอย่างโดยเฉพาะเกี่ยวกับ rsqrt (cpu ใช้ตารางการค้นหาภายในที่มีค่าประมาณมากซึ่งทำให้ง่ายกว่ามากในการรับผลลัพธ์) อาจดูเหมือนว่า rsqrt เร็วกว่า sqrt มากการดำเนินการ mul เพิ่มเติม 1 รายการ (ซึ่งไม่ต้องเสียค่าใช้จ่ายสูง) อาจไม่เปลี่ยนสถานการณ์ที่นี่

แก้ไข: ข้อเท็จจริงบางประการที่ควรค่าแก่การกล่าวถึง:
1. เมื่อฉันทำการปรับขนาดเล็กให้เหมาะสมกับไลบรารีกราฟิกของฉันและฉันได้ใช้ rsqrt สำหรับการคำนวณความยาวของเวกเตอร์ (แทนที่จะเป็น sqrt ฉันได้คูณผลรวมของฉันกำลังสองด้วย rsqrt ซึ่งเป็นสิ่งที่คุณได้ทำในการทดสอบของคุณ) และมันก็ทำได้ดีขึ้น
2. การคำนวณ rsqrt โดยใช้ตารางการค้นหาอย่างง่ายอาจจะง่ายกว่าเช่นเดียวกับ rsqrt เมื่อ x ไปที่อินฟินิตี้ 1 / sqrt (x) จะไปที่ 0 ดังนั้นสำหรับ x ขนาดเล็กค่าฟังก์ชันจะไม่เปลี่ยนแปลง (มาก) ในขณะที่ sqrt - มันไปที่อินฟินิตี้ดังนั้นมันจึงเป็นกรณีง่ายๆ;)

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


4

Newton-Raphson มาบรรจบกันเป็นศูนย์ของการf(x)ใช้ส่วนเพิ่มเท่ากับ-f/f' ที่ที่f'เป็นอนุพันธ์

สำหรับx=sqrt(y)คุณสามารถพยายามที่จะแก้f(x) = 0สำหรับxใช้f(x) = x^2 - y;

จากนั้นส่วนเพิ่มคือ: dx = -f/f' = 1/2 (x - y/x) = 1/2 (x^2 - y) / x ซึ่งมีการหารช้าอยู่ในนั้น

คุณสามารถลองใช้ฟังก์ชันอื่น ๆ (เช่นf(x) = 1/y - 1/x^2) ได้ แต่จะมีความซับซ้อนไม่แพ้กัน

ลองดูที่1/sqrt(y)ตอนนี้ คุณสามารถลองf(x) = x^2 - 1/yได้ แต่มันจะซับซ้อนพอ ๆ กันdx = 2xy / (y*x^2 - 1)เช่น ทางเลือกอื่นที่ไม่ชัดเจนทางเลือกหนึ่งสำหรับf(x)คือ:f(x) = y - 1/x^2

จากนั้น: dx = -f/f' = (y - 1/x^2) / (2/x^3) = 1/2 * x * (1 - y * x^2)

อา! ไม่ใช่การแสดงออกที่ไม่สำคัญ แต่คุณมีเพียงตัวคูณเท่านั้นไม่มีการหาร => เร็วขึ้น!

และ: ขั้นตอนการอัปเดตฉบับเต็มnew_x = x + dxจะอ่าน:

x *= 3/2 - y/2 * x * x ซึ่งก็ง่ายเช่นกัน


2

มีคำตอบอื่น ๆ อีกมากมายจากไม่กี่ปีที่ผ่านมา นี่คือสิ่งที่ฉันทามติถูกต้อง:

  • คำสั่ง rsqrt * คำนวณการประมาณค่ากับรากที่สองซึ่งกันและกันได้ดีประมาณ 11-12 บิต
  • มันใช้กับตารางการค้นหา (เช่น ROM) ที่สร้างดัชนีโดย mantissa (อันที่จริงมันเป็นตารางการค้นหาแบบบีบอัดซึ่งคล้ายกับตารางทางคณิตศาสตร์ในสมัยก่อนโดยใช้การปรับบิตลำดับต่ำเพื่อบันทึกทรานซิสเตอร์)
  • สาเหตุที่ใช้งานได้คือค่าประมาณเริ่มต้นที่ใช้โดย FPU สำหรับอัลกอริทึมรากที่สอง "จริง"
  • นอกจากนี้ยังมีคำแนะนำซึ่งกันและกันโดยประมาณ rcp คำแนะนำทั้งสองนี้เป็นข้อบ่งชี้ว่า FPU ใช้สแควร์รูทและการหารอย่างไร

นี่คือสิ่งที่ฉันทามติผิดพลาด:

  • FPU ในยุค SSE ไม่ใช้ Newton-Raphson เพื่อคำนวณรากที่สอง เป็นวิธีการที่ยอดเยี่ยมในซอฟต์แวร์ แต่จะเป็นข้อผิดพลาดในการนำไปใช้ในฮาร์ดแวร์

อัลกอริทึม NR เพื่อคำนวณรากที่สองซึ่งกันและกันมีขั้นตอนการอัปเดตนี้ตามที่ผู้อื่นกล่าวไว้:

x' = 0.5 * x * (3 - n*x*x);

นั่นคือการคูณจำนวนมากที่ขึ้นอยู่กับข้อมูลและการลบหนึ่งครั้ง

สิ่งที่ตามมาคืออัลกอริทึมที่ FPU สมัยใหม่ใช้จริง

ได้รับb[0] = nสมมติว่าเราสามารถหาชุดของตัวเลขY[i]ดังกล่าวว่าb[n] = b[0] * Y[0]^2 * Y[1]^2 * ... * Y[n]^2แนวทาง 1. แล้วพิจารณา:

x[n] = b[0] * Y[0] * Y[1] * ... * Y[n]
y[n] = Y[0] * Y[1] * ... * Y[n]

เห็นได้ชัดว่าx[n]แนวทางsqrt(n)และวิธีการy[n]1/sqrt(n)

เราสามารถใช้ขั้นตอนการอัพเดต Newton-Raphson สำหรับสแควร์รูทซึ่งกันและกันเพื่อให้ได้สิ่งที่ดีY[i]:

b[i] = b[i-1] * Y[i-1]^2
Y[i] = 0.5 * (3 - b[i])

จากนั้น:

x[0] = n Y[0]
x[i] = x[i-1] * Y[i]

และ:

y[0] = Y[0]
y[i] = y[i-1] * Y[i]

b[i] = x[i-1] * y[i-1]สังเกตที่สำคัญต่อไปคือว่า ดังนั้น:

Y[i] = 0.5 * (3 - x[i-1] * y[i-1])
     = 1 + 0.5 * (1 - x[i-1] * y[i-1])

จากนั้น:

x[i] = x[i-1] * (1 + 0.5 * (1 - x[i-1] * y[i-1]))
     = x[i-1] + x[i-1] * 0.5 * (1 - x[i-1] * y[i-1]))
y[i] = y[i-1] * (1 + 0.5 * (1 - x[i-1] * y[i-1]))
     = y[i-1] + y[i-1] * 0.5 * (1 - x[i-1] * y[i-1]))

นั่นคือกำหนด x และ y เริ่มต้นเราสามารถใช้ขั้นตอนการอัพเดตต่อไปนี้:

r = 0.5 * (1 - x * y)
x' = x + x * r
y' = y + y * r

หรือแม้กระทั่งนักแสดงเราสามารถตั้งค่าh = 0.5 * yได้ นี่คือการเริ่มต้น:

Y = approx_rsqrt(n)
x = Y * n
h = Y * 0.5

และนี่คือขั้นตอนการอัปเดต:

r = 0.5 - x * h
x' = x + x * r
h' = h + h * r

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

ในปี 2542 FPU ต้องการวงจรเพิ่ม / ซับเทคเตอร์แบบไพพ์ไลน์และวงจรทวีคูณแบบไพพ์ไลน์มิฉะนั้น SSE จะไม่ "สตรีมมิ่ง" มากนัก ต้องใช้เพียงหนึ่งในแต่ละวงจรในปี 2542 เพื่อนำวงในนี้ไปใช้แบบเต็มท่อโดยไม่ต้องเสียฮาร์ดแวร์จำนวนมากเพียงแค่สแควร์รูท

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


1
ที่เกี่ยวข้อง: sqrt () ของ GCC ทำงานอย่างไรหลังจากคอมไพล์แล้ว? รูทใช้วิธีไหน นิวตัน - ราฟสัน? มีลิงค์ไปยังการออกแบบหน่วยประมวลผล div / sqrt ของฮาร์ดแวร์ rsqrt vector ที่รวดเร็วและซึ่งกันและกันด้วย SSE / AVX ขึ้นอยู่กับความแม่นยำ - การทำซ้ำหนึ่งนิวตันในซอฟต์แวร์มีหรือไม่มี FMA สำหรับใช้กับ_mm256_rsqrt_psการวิเคราะห์ Haswell perf โดยปกติแล้วจะเป็นเพียงความคิดที่ดีหากคุณไม่มีงานอื่นในลูปและจะทำให้คอขวดยากที่ปริมาณงานตัวแบ่ง HW sqrt เป็น uop เดียวดังนั้นจึงใช้ได้ผสมกับงานอื่น ๆ
Peter Cordes

-2

มันเร็วกว่าเนื่องจากคำสั่งเหล่านี้ละเว้นโหมดการปัดเศษและไม่จัดการกับข้อยกเว้นจุดลอยตัวหรือตัวเลขที่ผิดปกติ ด้วยเหตุผลเหล่านี้การวางท่อเก็งกำไรและดำเนินการคำสั่ง fp อื่น ๆ จึงง่ายกว่ามาก


ผิดอย่างเห็นได้ชัด FMA ขึ้นอยู่กับโหมดการปัดเศษปัจจุบัน แต่มีอัตราการส่งข้อมูลเป็นสองต่อนาฬิกาใน Haswell และใหม่กว่า ด้วยหน่วย FMA แบบเต็มท่อสองหน่วย Haswell สามารถมี FMA ได้ถึง 10 เครื่องพร้อมกัน คำตอบที่ถูกต้องคือrsqrt's มากความถูกต้องลดลงซึ่งหมายถึงการทำงานมากน้อยที่จะทำ (หรือไม่มีเลย?) หลังจากที่ตารางการค้นหาที่จะได้รับการคาดเดาเริ่มต้น
Peter Cordes
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.