คณิตศาสตร์ที่รวดเร็วของ gcc ทำอะไรได้จริง?


144

ฉันเข้าใจว่าการ--ffast-mathตั้งค่าสถานะของ gcc สามารถเพิ่มความเร็วได้อย่างมากสำหรับ ops แบบลอยตัวและออกไปนอกมาตรฐาน IEEE แต่ฉันไม่สามารถหาข้อมูลเกี่ยวกับสิ่งที่เกิดขึ้นจริงได้เมื่อเปิดใช้งาน ใครช่วยกรุณาอธิบายรายละเอียดบางอย่างและอาจให้ตัวอย่างที่ชัดเจนว่าจะมีการเปลี่ยนแปลงอย่างไรถ้าธงเปิดหรือปิด?

ฉันพยายามขุดผ่าน SO สำหรับคำถามที่คล้ายกัน แต่ไม่สามารถหาอะไรอธิบายการทำงานของคณิตศาสตร์ได้อย่างรวดเร็ว

คำตอบ:


86

ตามที่คุณกล่าวถึงจะช่วยให้การปรับให้เหมาะสมที่ไม่ได้ปฏิบัติตามมาตรฐาน IEEE ที่เข้มงวด

ตัวอย่างคือ:

x = x*x*x*x*x*x*x*x;

ถึง

x *= x;
x *= x;
x *= x;

เนื่องจากเลขทศนิยมไม่สัมพันธ์กันการเรียงลำดับและการแยกตัวประกอบของการดำเนินการจะส่งผลต่อผลลัพธ์เนื่องจากการปัดเศษ ดังนั้นการเพิ่มประสิทธิภาพนี้ไม่ได้ทำภายใต้พฤติกรรม FP ที่เข้มงวด

ฉันยังไม่ได้ตรวจสอบเพื่อดูว่า GCC ทำสิ่งนี้ให้เหมาะสมหรือไม่ แต่ความคิดก็เหมือนกัน


25
@Andrey: สำหรับตัวอย่างนี้คุณไปจาก 7 คูณลงไปที่ 3
Mysticial

4
@Andrey: ในทางคณิตศาสตร์มันจะถูกต้อง แต่ผลลัพธ์อาจแตกต่างกันเล็กน้อยในบิตสุดท้ายเนื่องจากการปัดเศษที่แตกต่างกัน
Mysticial

1
ในกรณีส่วนใหญ่ความแตกต่างเล็กน้อยนี้จะไม่สำคัญ (ขึ้นอยู่กับลำดับของ 10 ^ -16 สำหรับdoubleแต่จะแตกต่างกันไปตามแอปพลิเคชัน) สิ่งหนึ่งที่ควรทราบคือการเพิ่มประสิทธิภาพ ffast-math ไม่จำเป็นต้องเพิ่มการปัดเศษ "เพิ่มเติม" เหตุผลเดียวที่ทำไมมันไม่เป็นไปตามมาตรฐาน IEEE เพราะคำตอบนั้นแตกต่างกัน (แม้ว่าเล็กน้อย) จากสิ่งที่เขียน
Mysticial

1
@user: ขนาดของข้อผิดพลาดขึ้นอยู่กับข้อมูลอินพุต มันควรจะมีขนาดเล็กเมื่อเทียบกับผล ตัวอย่างเช่นถ้าxน้อยกว่า 10 ข้อผิดพลาดในตัวอย่างของ Mystical จะลดลงประมาณ 10 ^ -10 แต่ถ้าx = 10e20ข้อผิดพลาดน่าจะมีหลายล้าน
Ben Voigt

3
@stefanct ก็จริงเกี่ยวกับ-fassociative-mathซึ่งรวมอยู่ใน-funsafe-math-optimizationsที่ในทางกลับกันการใช้งานด้วย-ffast-math ทำไมไม่ GCC เพิ่มประสิทธิภาพa*a*a*a*a*aเพื่อ(a*a*a)*(a*a*a)?
phuclv

255

-ffast-math ทำอะไรมากกว่าเพียงแค่ทำลายการปฏิบัติตามมาตรฐาน IEEE ที่เข้มงวด

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

ประการที่สองมันปิดใช้งานการตั้งค่าerrnoหลังจากฟังก์ชั่นทางคณิตศาสตร์คำสั่งเดียวซึ่งหมายถึงการหลีกเลี่ยงการเขียนตัวแปรเธรดท้องถิ่น (ซึ่งสามารถสร้างความแตกต่าง 100% สำหรับฟังก์ชั่นเหล่านั้นในสถาปัตยกรรมบางอย่าง)

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

ประการที่สี่จะช่วยให้การประมาณค่าซึ่งกันและกันสำหรับการหารและรากที่สองซึ่งกันและกัน

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

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


15
เดมอนขอบคุณ! คุณสามารถเพิ่มการอ้างอิงได้ไหม? เช่นgcc.gnu.org/onlinedocs/gcc/Optimize-Options.html " -ffast-math ชุด -fno-math-errno, -funsafe-math-optimization, -finite-math-only, -fno-rounding-math, -fno-signaling -nans และ -fcx-limited-range ตัวเลือกนี้ทำให้แมโคร preprocessor FAST_MATHถูกกำหนด "และบางสิ่งบางอย่างจาก glibc เช่น ( math.hใกล้ math_errhandling)" โดยค่าเริ่มต้นฟังก์ชันทั้งหมดสนับสนุนทั้ง errno และ exception handling ในโหมดคณิตศาสตร์เร็วของ gcc และ ถ้าฟังก์ชั่นอินไลน์ถูกกำหนดสิ่งนี้อาจไม่เป็นจริง "
osgx

4
@avapowered: ไม่ว่าจะเป็น "อันตราย" ขึ้นอยู่กับสิ่งที่รับประกันคุณต้องการ -ffast-mathอนุญาตให้คอมไพเลอร์ตัดมุมและทำลายสัญญา (ตามที่อธิบาย) ซึ่งโดยทั่วไปไม่เป็นอันตรายเช่นนี้และไม่ใช่ปัญหาสำหรับคนส่วนใหญ่ สำหรับคนส่วนใหญ่มันเหมือนกันเร็วขึ้นเท่านั้น อย่างไรก็ตามหากรหัสของคุณใช้และยึดตามสัญญาเหล่านี้รหัสของคุณอาจทำงานแตกต่างไปจากที่คุณคาดไว้ โดยปกติแล้วที่นี้หมายถึงว่าโปรแกรมจะดูเหมือนปรับการทำงานส่วนใหญ่ แต่ผลลัพธ์ที่บางคนอาจจะ "ไม่คาดคิด" (พูดในการจำลองฟิสิกส์วัตถุทั้งสองอาจจะไม่ชนกันอย่างถูกต้อง)
Damon

2
@Royi: ทั้งสองควรเป็นอิสระจากกัน -O2โดยทั่วไปจะเปิดใช้งานการเพิ่มประสิทธิภาพทางกฎหมาย "ทุกคน" ยกเว้นขนาดการแลกเปลี่ยนที่รวดเร็ว -O3ยังเปิดใช้งานการปรับให้เหมาะสมที่ขนาดการค้าสำหรับความเร็ว มันยังคงความถูกต้อง 100% -ffast-mathพยายามทำให้การดำเนินการทางคณิตศาสตร์เร็วขึ้นโดยอนุญาตให้มีพฤติกรรม "ไม่ถูกต้องเล็กน้อย" ซึ่งมักจะไม่เป็นอันตราย แต่จะถือว่าไม่ถูกต้องตามถ้อยคำของมาตรฐาน หากรหัสของคุณมีความเร็วแตกต่างกันมากในคอมไพเลอร์สองตัว (ไม่ใช่เพียง 1-2%) จากนั้นตรวจสอบว่าโค้ดของคุณตรงตามมาตรฐานอย่างเคร่งครัดและ ...
Damon

1
... สร้างคำเตือนเป็นศูนย์ นอกจากนี้ตรวจสอบให้แน่ใจว่าคุณไม่ได้รับการกำหนดนามแฝงของกฎและสิ่งต่าง ๆ เช่น auto-vectorization โดยหลักการแล้ว GCC ควรทำงานอย่างน้อยดี (โดยปกติแล้วจะดีกว่าในประสบการณ์ของฉัน) เป็น MSVC เมื่อไม่เป็นเช่นนั้นคุณอาจทำผิดพลาดเล็กน้อยซึ่ง MSVC ไม่สนใจ แต่ทำให้ GCC ปิดใช้งานการเพิ่มประสิทธิภาพ คุณควรให้ทั้งสองทางเลือกหากคุณต้องการทั้งสองอย่างใช่
เดมอน

1
@Royi: รหัสนั้นดูไม่เล็กและเรียบง่ายสำหรับฉันไม่ใช่สิ่งที่ใครสามารถวิเคราะห์ในเชิงลึกในไม่กี่นาที (หรือแม้กระทั่งชั่วโมง) เหนือสิ่งอื่นใดมันเกี่ยวข้องกับอันตรายดูเหมือน#pragma omp parallel forและภายในร่างกายวนคุณทั้งจากการอ่านและการเขียนไปยังที่อยู่ที่ชี้ไปตามอาร์กิวเมนต์ของฟังก์ชันและทำการแยกสาขาที่ไม่สำคัญ จากการคาดเดาที่ไม่ได้รับการศึกษาคุณอาจกำลังแคชแคชจากการเรียกใช้เธรดที่กำหนดโดยการนำไปปฏิบัติและ MSVC อาจหลีกเลี่ยงร้านค้าระดับกลางที่กฎการกำหนดนามแฝงจะไม่ถูกต้อง เป็นไปไม่ได้ที่จะบอก
เดมอน
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.