คอมไพเลอร์ของ Fortran นั้นดีกว่าจริง ๆ


74

คำถามนี้เป็นส่วนขยายของการสนทนาสองครั้งที่เกิดขึ้นเมื่อเร็ว ๆ นี้ในการตอบกลับไปยัง " C ++ vs Fortran สำหรับ HPC " และมันเป็นความท้าทายมากกว่าคำถาม ...

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

ดังนั้นฉันจึงตัดสินใจลองทดสอบง่ายๆ: ฉันได้รับสำเนาของdaxpy.fและdaxpy.cและรวบรวมพวกเขาด้วย gfortran / gcc

ตอนนี้ daxpy.c เป็นเพียงการแปล f2c ของ daxpy.f (โค้ดที่สร้างขึ้นโดยอัตโนมัติน่าเกลียดเหมือน heck) ดังนั้นฉันจึงเอาโค้ดนั้นและทำความสะอาดมันเล็กน้อย (พบ daxpy_c) ซึ่งโดยทั่วไปหมายถึงการเขียนลูปด้านในสุดเป็น

for ( i = 0 ; i < n ; i++ )
    dy[i] += da * dx[i];

สุดท้ายฉันเขียนมันอีกครั้ง (ป้อน daxpy_cvec) โดยใช้ไวยากรณ์เวกเตอร์ของ gcc:

#define vector(elcount, type)  __attribute__((vector_size((elcount)*sizeof(type)))) type
vector(2,double) va = { da , da }, *vx, *vy;

vx = (void *)dx; vy = (void *)dy;
for ( i = 0 ; i < (n/2 & ~1) ; i += 2 ) {
    vy[i] += va * vx[i];
    vy[i+1] += va * vx[i+1];
    }
for ( i = n & ~3 ; i < n ; i++ )
    dy[i] += da * dx[i];

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

รหัสทั้งหมดได้รับการคอมไพล์โดยใช้ gfortran / gcc เวอร์ชั่น 4.5 พร้อมกับแฟล็ก "-O3 -Wall -msse2 -march = native -ffast-math -fomit-frame-pointer -malign-double -fstrict-aliasing" บนแล็ปท็อปของฉัน (Intel Core i5 CPU, M560, 2.67GHz) ฉันได้ผลลัพธ์ต่อไปนี้:

pedro@laika:~/work/fvsc$ ./test 1000000 10000
timing 1000000 runs with a vector of length 10000.
daxpy_f took 8156.7 ms.
daxpy_f2c took 10568.1 ms.
daxpy_c took 7912.8 ms.
daxpy_cvec took 5670.8 ms.

ดังนั้นรหัส Fortran ดั้งเดิมใช้เวลามากกว่า 8.1 วินาทีการแปลอัตโนมัติจะใช้เวลา 10.5 วินาทีการใช้งาน C แบบไร้เดียงสาทำใน 7.9 และรหัส vectorized อย่างชัดเจนทำใน 5.6 น้อยกว่าเล็กน้อย

นั่นคือ Fortran ช้ากว่าการใช้ C ไร้เดียงสาเล็กน้อยและช้ากว่าการปรับใช้ C เวกเตอร์ 50%

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

ทุกคนสามารถลองทดสอบนี้ด้วย icc / ifort ได้หรือไม่? ไวยากรณ์ของเวกเตอร์อาจไม่ทำงาน แต่ฉันอยากรู้ว่ารุ่น C ไร้เดียงสาทำงานอย่างไร กันไปสำหรับใครกับ xlc / xlf นอนอยู่รอบ ๆ

ฉันได้อัปโหลดแหล่งที่มาและ Makefile ที่นี่ หากต้องการกำหนดเวลาที่แม่นยำให้ตั้ง CPU_TPS ใน test.c เป็นจำนวน Hz บน CPU ของคุณ หากคุณพบว่าการปรับปรุงใด ๆ ของรุ่นใด ๆ โปรดโพสต์ไว้ที่นี่!

ปรับปรุง:

ฉันได้เพิ่มรหัสทดสอบของ stali ลงในไฟล์ออนไลน์และเสริมด้วยเวอร์ชัน C ฉันปรับเปลี่ยนโปรแกรมให้ทำ 1'000'000 ลูปบนเวกเตอร์ที่มีความยาว 10'000 ให้สอดคล้องกับการทดสอบก่อนหน้า (และเนื่องจากเครื่องของฉันไม่สามารถจัดสรรเวกเตอร์ที่มีความยาว 1'000'000'000 เหมือนในต้นฉบับของ stali รหัส). เนื่องจากตัวเลขมีขนาดเล็กลงเล็กน้อยฉันจึงใช้ตัวเลือก-par-threshold:50เพื่อทำให้คอมไพเลอร์มีแนวโน้มที่จะขนานกันมากขึ้น เวอร์ชัน icc / ifort ที่ใช้คือ 12.1.2 20111128 และผลลัพธ์มีดังนี้

pedro@laika:~/work/fvsc$ OMP_NUM_THREADS=1 time ./icctest_c
3.27user 0.00system 0:03.27elapsed 99%CPU

pedro@laika:~/work/fvsc$ OMP_NUM_THREADS=1 time ./icctest_f
3.29user 0.00system 0:03.29elapsed 99%CPU

pedro@laika:~/work/fvsc$ OMP_NUM_THREADS=2 time ./icctest_c
4.89user 0.00system 0:02.60elapsed 188%CPU

pedro@laika:~/work/fvsc$ OMP_NUM_THREADS=2 time ./icctest_f
4.91user 0.00system 0:02.60elapsed 188%CPU

โดยสรุปแล้วผลลัพธ์นั้นสำหรับวัตถุประสงค์ในทางปฏิบัติทั้งหมดเหมือนกันสำหรับทั้งรุ่น C และ Fortran และรหัสทั้งคู่ขนานกันโดยอัตโนมัติ โปรดทราบว่าเวลาที่รวดเร็วเมื่อเทียบกับการทดสอบก่อนหน้านี้เกิดจากการใช้เลขทศนิยมที่มีความแม่นยำเดียว!

ปรับปรุง:

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

pedro@laika:~/work/fvsc$ OMP_NUM_THREADS=1 time ./mm_test_f 2500
 triple do time   3.46421700000000     
3.63user 0.06system 0:03.70elapsed 99%CPU

pedro@laika:~/work/fvsc$ OMP_NUM_THREADS=1 time ./mm_test_c 2500
triple do time 3.431997791385768
3.58user 0.10system 0:03.69elapsed 99%CPU

pedro@laika:~/work/fvsc$ OMP_NUM_THREADS=2 time ./mm_test_f 2500
 triple do time   5.09631900000000     
5.26user 0.06system 0:02.81elapsed 189%CPU

pedro@laika:~/work/fvsc$ OMP_NUM_THREADS=2 time ./mm_test_c 2500
triple do time 2.298916975280899
4.78user 0.08system 0:02.62elapsed 184%CPU

โปรดทราบว่าcpu_timeใน Fortran วัดเวลา CPU และไม่ใช่เวลานาฬิกาแขวนดังนั้นฉันจึงปิดการโทรtimeเพื่อเปรียบเทียบพวกเขาสำหรับ 2 CPU ไม่มีความแตกต่างที่แท้จริงระหว่างผลลัพธ์ยกเว้นว่ารุ่น C ทำงานได้ดีกว่าในสองคอร์เล็กน้อย

ตอนนี้สำหรับmatmulคำสั่งแน่นอนเฉพาะใน Fortran เนื่องจากไม่สามารถใช้งานได้ใน C:

pedro@laika:~/work/fvsc$ OMP_NUM_THREADS=1 time ./mm_test_f 2500
 matmul    time   23.6494780000000     
23.80user 0.08system 0:23.91elapsed 99%CPU

pedro@laika:~/work/fvsc$ OMP_NUM_THREADS=2 time ./mm_test_f 2500
 matmul    time   26.6176640000000     
26.75user 0.10system 0:13.62elapsed 197%CPU

ว้าว. มันแย่มากจริงๆ ใครสามารถค้นพบสิ่งที่ฉันทำผิดหรืออธิบายได้ว่าทำไมสิ่งที่แท้จริงนี้ยังคงเป็นสิ่งที่ดี?

ฉันไม่ได้เพิ่มการdgemmเรียกไปยังเกณฑ์มาตรฐานเนื่องจากเป็นการเรียกไลบรารีไปยังฟังก์ชันเดียวกันใน Intel MKL

สำหรับการทดสอบในอนาคตใครสามารถแนะนำตัวอย่างที่ทราบว่าช้ากว่าใน C มากกว่าใน Fortran หรือไม่?

ปรับปรุง

เพื่อยืนยันการเรียกร้องของ stali ว่าmatmulintrinsic คือ "คำสั่งของ magnitue" เร็วกว่าผลิตภัณฑ์เมทริกซ์ที่ชัดเจนในเมทริกซ์ที่มีขนาดเล็กฉันปรับเปลี่ยนรหัสของตัวเองเพื่อคูณเมทริกซ์ขนาด 100x100 โดยใช้ทั้งสองวิธีละ 10,000 ครั้ง ผลลัพธ์บน CPU หนึ่งและสองมีดังนี้:

pedro@laika:~/work/fvsc$ OMP_NUM_THREADS=1 time ./mm_test_f 10000 100
 matmul    time   3.61222500000000     
 triple do time   3.54022200000000     
7.15user 0.00system 0:07.16elapsed 99%CPU

pedro@laika:~/work/fvsc$ OMP_NUM_THREADS=2 time ./mm_test_f 10000 100
 matmul    time   4.54428400000000     
 triple do time   4.31626900000000     
8.86user 0.00system 0:04.60elapsed 192%CPU

ปรับปรุง

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

คอมไพเลอร์ C จะสร้างโค๊ดรหัสเดียวกันหาก-fcx-limited-rangeตั้งค่าตัวเลือกไว้เช่นคอมไพเลอร์ได้รับคำสั่งให้ละเว้นค่าศักย์ไฟฟ้าสูง / ต่ำ - ต่ำในค่ากลาง ตัวเลือกนี้มีการตั้งค่าอย่างใดโดยค่าเริ่มต้นใน gfortran และอาจนำไปสู่ผลลัพธ์ที่ไม่ถูกต้อง การบังคับใช้-fno-cx-limited-rangeใน gfortran ไม่ได้เปลี่ยนแปลงอะไรเลย

ดังนั้นนี่จึงเป็นข้อโต้แย้งที่ต่อต้านการใช้ gfortran สำหรับการคำนวณเชิงตัวเลข: การดำเนินการกับค่าที่ซับซ้อนอาจเกิน / ต่ำกว่าการไหลแม้ว่าผลลัพธ์ที่ถูกต้องจะอยู่ในช่วงจุดลอยตัวก็ตาม นี่เป็นมาตรฐานของ Fortran ใน gcc หรือโดยทั่วไปแล้ว C99 ค่าเริ่มต้นคือการทำสิ่งต่าง ๆ อย่างเคร่งครัด (อ่านตามมาตรฐาน IEEE-754) เว้นแต่จะระบุไว้เป็นอย่างอื่น

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


ปัญหาเวลาหนึ่งก็คือถ้าโปรเซสเซอร์ของคุณทำโหมดความถี่ / เทอร์โบผลลัพธ์เหล่านี้อาจอยู่ทั่วแผนที่
Bill Barth

1
daxpy_c.cของคุณกำลังอัพเดท x ด้วย x หลายรายการและไม่แตะ y เลย คุณอาจต้องการที่จะแก้ไขปัญหาที่จะทำให้มันเป็นธรรม ...
แจ็ค Poulson

1
@JackPoulson: จับได้ดีแก้ไขและปรับปรุงผลลัพธ์
Pedro

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

1
@permeakra: ที่จริงแล้วมาตรฐาน C99 ระบุrestrictคำหลักที่บอกคอมไพเลอร์ว่า: เพื่อสมมติว่าอาร์เรย์ไม่ทับซ้อนกับโครงสร้างข้อมูลอื่นใด
เปโดร

คำตอบ:


37

ความแตกต่างในการกำหนดเวลาของคุณน่าจะเกิดจากการเปิดตัว Fortran daxpyด้วยตนเอง เวลาต่อไปนี้อยู่บน 2.67 GHz Xeon X5650 โดยใช้คำสั่ง

./test 1000000 10000

คอมไพเลอร์ Intel 11.1

Fortran ที่มีการเปิดใช้งานแบบแมนนวล: 8.7 วินาที
Fortran โดยไม่มีการเปิดออกด้วยตนเอง: 5.8 วินาที
C โดยที่ไม่มีการเปิดออกด้วยตนเอง: 5.8 วินาที

คอมไพเลอร์ GNU 4.1.2

Fortran ที่มีการเปิดใช้งานแบบแมนนวล: 8.3 วินาที
Fortran ที่ไม่มีการเปิดใช้งานแบบแมนนวล: 13.5 วินาที
C โดยไม่มีการเปิดออกแบบแมนนวล: 13.6 วินาที
C ด้วยคุณลักษณะเวกเตอร์: 5.8 วินาที

คอมไพเลอร์ GNU 4.4.5

Fortran ที่มีการคลายออกด้วยตนเอง: 8.1 วินาที
Fortran ที่ไม่มีการเปิดใช้งานด้วยตนเอง: 7.4 วินาที
C โดยไม่มีการเปิดออกด้วยตนเอง: 8.5 วินาที
C ด้วยเวกเตอร์ที่มีการแสดงผล: 5.8 วินาที

สรุปผลการวิจัย

  • การคลายออกด้วยตนเองช่วย GNU 4.1.2 คอมไพเลอร์ Fortran บนสถาปัตยกรรมนี้ แต่เจ็บเวอร์ชันใหม่กว่า (4.4.5) และคอมไพเลอร์ Intel Fortran
  • คอมไพเลอร์ GNU 4.4.5 C นั้นสามารถแข่งขันกับ Fortran ได้มากกว่าเวอร์ชั่น 4.2.1
  • Vector ภายในนั้นอนุญาตให้ประสิทธิภาพของ GCC ตรงกับคอมไพเลอร์ของ Intel

ใช้เวลาในการทดสอบกิจวัตรที่ซับซ้อนมากขึ้นเช่น dgemv และ dgemm หรือไม่


ขอบคุณสำหรับผลลัพธ์! คุณใช้ gcc รุ่นใดและคุณมีความเฉพาะเจาะจงมากขึ้นเกี่ยวกับ CPU หรือไม่
Pedro

2
คอมไพเลอร์ของคุณเก่ากว่าซีพียูของคุณ ... คุณลองกับ gcc-4.5 ได้ไหม
Pedro

1
ฉันแค่ลองมัน เวอร์ชัน vectorized ที่มี GCC 4.4.5 นั้นตรงกับผลลัพธ์ของ Intel 11.1 อย่างแน่นอน
Jack Poulson

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

4
เราสามารถพูดแบบนี้ในการอภิปรายอายุ "เราใช้ Fortran เพราะมันมีประสิทธิภาพมากกว่า" ดังนั้นเราจึงสามารถโยนมันทิ้งในถังขยะได้หรือไม่?
Stefano Borini

16

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

ทำงานทั้งบนแล็ปท็อปของฉัน (Macbook Pro, Intel Core i7, 2.66 GHz), ประสิทธิภาพสัมพัทธ์ของรุ่น C-vectorized มือของคุณและรุ่น vectorized Fortran ที่ไม่ใช่มือขึ้นอยู่กับคอมไพเลอร์ที่ใช้ (ด้วยตัวเลือกของคุณเอง):

Compiler     Fortran time     C time
GCC 4.6.1    5408.5 ms        5424.0 ms
GCC 4.5.3    7889.2 ms        5532.3 ms
GCC 4.4.6    7735.2 ms        5468.7 ms

ดังนั้นดูเหมือนว่า GCC จะดีกว่าในการทำให้เวกเตอร์วนซ้ำในสาขา 4.6 มากกว่าที่เคยเป็นมา


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


ยินดีต้อนรับสู่ scicomp! ฉันยอมรับว่าเวอร์ชันคอมไพเลอร์มีความสำคัญเท่ากับภาษาในกรณีนี้ คุณหมายถึง 'of' แทนที่จะ 'off ในประโยคสุดท้ายของคุณหรือไม่?
Aron Ahmadia

9

วิธีที่ฉันจะเขียน AXPY ใน Fortran นั้นแตกต่างกันเล็กน้อย มันเป็นการแปลที่ถูกต้องของคณิตศาสตร์

m_blas.f90

 module blas

   interface axpy
     module procedure saxpy,daxpy
   end interface

 contains

   subroutine daxpy(x,y,a)
     implicit none
     real(8) :: x(:),y(:),a
     y=a*x+y
   end subroutine daxpy

   subroutine saxpy(x,y,a)
     implicit none
     real(4) :: x(:),y(:),a
     y=a*x+y
   end subroutine saxpy

 end module blas

ทีนี้ลองเรียกรูทีนด้านบนในโปรแกรม

test.f90

 program main

   use blas
   implicit none

   real(4), allocatable :: x(:),y(:)
   real(4) :: a
   integer :: n

   n=1000000000
   allocate(x(n),y(n))
   x=1.0
   y=2.0
   a=5.0
   call axpy(x,y,a)
   deallocate(x,y)

 end program main

ตอนนี้ให้รวบรวมและเรียกใช้ ...

login1$ ifort -fast -parallel m_blas.f90 test.f90
ipo: remark #11000: performing multi-file optimizations
ipo: remark #11005: generating object file /tmp/ipo_iforttdqZSA.o

login1$ export OMP_NUM_THREADS=1
login1$ time ./a.out 
real    0 m 4.697 s
user    0 m 1.972 s
sys     0 m 2.548 s

login1$ export OMP_NUM_THREADS=2
login1$ time ./a.out 
real    0 m 2.657 s
user    0 m 2.060 s
sys     0 m 2.744 s

ขอให้สังเกตว่าฉันไม่ได้ใช้ลูปหรือคำสั่งOpenMPใด ๆ ที่ชัดเจน สิ่งนี้จะเป็นไปได้ใน C (นั่นคือไม่ใช้ลูปและการเชื่อมต่ออัตโนมัติ) ฉันไม่ใช้ C ดังนั้นฉันไม่รู้


การขนานแบบอัตโนมัติเป็นคุณสมบัติของคอมไพเลอร์ Intel (ทั้ง Fortran และ C) และไม่ใช่ภาษา ดังนั้นความเท่าเทียมกันใน C ควรเทียบเคียง เพิ่งจะเกิดความอยากรู้อยากเห็นมันทำงานได้ดีเพียงใด n = 10,000
เปโดร

3
นั่นคือประเด็นทั้งหมด Autopar นั้นง่ายกว่าใน Fortran เนื่องจากข้อเท็จจริงที่ว่า Fortran (ไม่เหมือนกับ C) สนับสนุนการทำงานของอาเรย์ทั้งหมดเช่น matmult, transpose และอื่น ๆ ดังนั้นการปรับโค้ดให้ง่ายขึ้นสำหรับคอมไพเลอร์ของ Fortran GFortran (ซึ่งคุณใช้แล้ว) ไม่มีทรัพยากรสำหรับนักพัฒนาเพื่อปรับแต่งคอมไพเลอร์ของ Fortran ให้เหมาะสมเนื่องจากโฟกัสของพวกเขาในปัจจุบันคือการนำมาตรฐาน Fortran 2003 มาใช้แทนการปรับให้เหมาะสม
stali

อืมม ... คอมไพเลอร์ Intel C / C ++ iccยังทำการขนานอัตโนมัติ ฉันได้เพิ่มไฟล์icctest.cไปยังแหล่งอื่น คุณสามารถรวบรวมมันด้วยตัวเลือกเดียวกับที่คุณใช้ข้างต้นเรียกใช้และรายงานการกำหนดเวลาได้หรือไม่? ฉันต้องเพิ่มคำสั่ง printf ลงในโค้ดของฉันเพื่อหลีกเลี่ยง gcc ในการเพิ่มประสิทธิภาพทุกอย่าง นี่เป็นเพียงแฮ็คอย่างรวดเร็วและฉันหวังว่ามันจะไม่มีข้อผิดพลาด!
เปโดร

ฉันได้ดาวน์โหลดคอมไพล์เลอร์ icc / ifort ล่าสุดและทำการทดสอบด้วยตัวเอง คำถามได้รับการปรับปรุงเพื่อรวมผลลัพธ์ใหม่เหล่านี้คือการทำงานอัตโนมัติของ Intel ในทั้ง Fortran และ C
Pedro

1
ขอบคุณ ใช่ฉันสังเกตว่าอาจมีความแตกต่างเล็กน้อยเนื่องจากลูปง่ายและการดำเนินการเป็นระดับ 1 BLAS แต่อย่างที่ฉันพูดไว้ก่อนหน้านี้เนื่องจากความสามารถของ Fortran ในการดำเนินการอาเรย์ทั้งหมดและการใช้คำหลักเช่น PURE / ELEMENTAL มีพื้นที่เพิ่มเติมสำหรับการเพิ่มประสิทธิภาพคอมไพเลอร์ คอมไพเลอร์ใช้ข้อมูลนี้อย่างไรและสิ่งใดที่จริง ๆ แล้วมันแตกต่างกัน คุณสามารถลอง matmul ถ้าคุณต้องการbpaste.net/show/23035
stali

6

ฉันคิดว่ามันไม่เพียง แต่น่าสนใจเท่านั้นที่คอมไพเลอร์ปรับรหัสให้เหมาะกับฮาร์ดแวร์ที่ทันสมัย โดยเฉพาะระหว่าง GNU C และ GNU Fortran การสร้างรหัสอาจแตกต่างกันมาก

ลองพิจารณาอีกตัวอย่างหนึ่งเพื่อแสดงความแตกต่างระหว่างพวกเขา

การใช้ตัวเลขที่ซับซ้อนคอมไพเลอร์ GNU C จะผลิตโอเวอร์เฮดขนาดใหญ่สำหรับการดำเนินการทางคณิตศาสตร์ขั้นพื้นฐานเกือบทั้งหมดในจำนวนที่ซับซ้อน คอมไพเลอร์ Fortran ให้รหัสที่ดีกว่ามาก ลองมาดูตัวอย่างเล็ก ๆ ต่อไปนี้ใน Fortran:

COMPLEX*16 A,B,C
C=A*B

ให้ (gfortran -g -o complex.fo -c complex.f95; objdump -d -S complex.fo):

C=A*B
  52:   dd 45 e0                fldl   -0x20(%ebp)
  55:   dd 45 e8                fldl   -0x18(%ebp)
  58:   dd 45 d0                fldl   -0x30(%ebp)
  5b:   dd 45 d8                fldl   -0x28(%ebp)
  5e:   d9 c3                   fld    %st(3)
  60:   d8 ca                   fmul   %st(2),%st
  62:   d9 c3                   fld    %st(3)
  64:   d8 ca                   fmul   %st(2),%st
  66:   d9 ca                   fxch   %st(2)
  68:   de cd                   fmulp  %st,%st(5)
  6a:   d9 ca                   fxch   %st(2)
  6c:   de cb                   fmulp  %st,%st(3)
  6e:   de e9                   fsubrp %st,%st(1)
  70:   d9 c9                   fxch   %st(1)
  72:   de c2                   faddp  %st,%st(2)
  74:   dd 5d c0                fstpl  -0x40(%ebp)
  77:   dd 5d c8                fstpl  -0x38(%ebp)

รหัสเครื่อง 39 ไบต์คืออะไร เมื่อเราพิจารณาเหมือนกันใน C

 double complex a,b,c; 
 c=a*b; 

และดูผลลัพธ์ (ทำในลักษณะเดียวกับด้านบน) เราได้รับ:

  41:   8d 45 b8                lea    -0x48(%ebp),%eax
  44:   dd 5c 24 1c             fstpl  0x1c(%esp)
  48:   dd 5c 24 14             fstpl  0x14(%esp)
  4c:   dd 5c 24 0c             fstpl  0xc(%esp)
  50:   dd 5c 24 04             fstpl  0x4(%esp)
  54:   89 04 24                mov    %eax,(%esp)
  57:   e8 fc ff ff ff          call   58 <main+0x58>
  5c:   83 ec 04                sub    $0x4,%esp
  5f:   dd 45 b8                fldl   -0x48(%ebp)
  62:   dd 5d c8                fstpl  -0x38(%ebp)
  65:   dd 45 c0                fldl   -0x40(%ebp)
  68:   dd 5d d0                fstpl  -0x30(%ebp)

ซึ่งมีรหัสเครื่อง 39 ไบต์เช่นกัน แต่ขั้นตอนการทำงาน 57 อ้างถึงทำส่วนที่เหมาะสมของงานและดำเนินการตามที่ต้องการ ดังนั้นเราจึงมีรหัสเครื่อง 27 ไบต์เพื่อเรียกใช้การทำงานหลายอย่าง ฟังก์ชั่นด้านหลังเป็น muldc3 ที่จัดทำโดยlibgcc_s.soและมี footprint 1375 ไบต์ในรหัสเครื่อง สิ่งนี้จะทำให้โค้ดช้าลงอย่างมากและให้ผลลัพธ์ที่น่าสนใจเมื่อใช้ profiler

เมื่อเราใช้ตัวอย่าง BLAS ด้านบนเพื่อ zaxpyและทำการทดสอบเดียวกันคอมไพเลอร์ Fortran ควรให้ผลลัพธ์ที่ดีกว่าคอมไพเลอร์ C

(ฉันใช้ GCC 4.4.3 สำหรับการทดสอบนี้ แต่ฉันสังเกตเห็นว่าพฤติกรรมนี้เป็น GCC อื่นที่เผยแพร่ไป)

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


1
ฉันเพิ่งปรุงตัวอย่างตามบรรทัดรหัสของคุณcomplex.cและเพิ่มลงในรหัสออนไลน์ ฉันต้องเพิ่มอินพุต / เอาต์พุตทั้งหมดเพื่อให้แน่ใจว่าไม่มีสิ่งใดถูกปรับให้เหมาะสม ฉันเพียง แต่ได้รับการโทรไปถ้าฉันไม่ได้ใช้__muldc3 -ffast-mathเมื่อ-O2 -ffast-mathฉันได้แอสเซมเบลอร์ 9 บรรทัด คุณยืนยันเรื่องนี้ได้ไหม
Pedro

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

การใช้ -O2 นำไปสู่คอมไพเลอร์เพื่อคำนวณทุกสิ่งที่เป็นไปได้ที่ runtime นั่นเป็นสาเหตุที่ทำให้การสร้างเช่นนั้นหายไปในบางครั้ง ไม่ควรใช้ตัวเลือก -ffast-math ในการคำนวณทางวิทยาศาสตร์เมื่อคุณต้องการใช้ผลลัพธ์
MK aka Grisu

1
ถ้าอย่างนั้น-ffast-mathคุณก็ไม่ควรใช้ Fortran สำหรับการคำนวณที่ซับซ้อน ตามที่ฉันอธิบายในการอัปเดตคำถามของฉัน-ffast-mathหรือโดยทั่วไป-fcx-limited-rangeบังคับให้ gcc ใช้ non-IEEE แบบเดียวกันซึ่งเป็นการ จำกัด ช่วงการคำนวณเหมือนกับมาตรฐานใน Fortran ดังนั้นหากคุณต้องการค่าที่ซับซ้อนอย่างเต็มรูปแบบและแก้ไข Infs และ NaNs คุณไม่ควรใช้ Fortran ...
Pedro

2
@Pedro: ถ้าคุณต้องการให้ GCC ทำตัวเหมือน GFortran wrt การคูณและการหารที่ซับซ้อนคุณควรใช้กฎ -fcx-fortran-
janneb

4

folks,

ฉันพบว่าการสนทนานี้น่าสนใจมาก แต่ฉันประหลาดใจเมื่อเห็นว่าการสั่งซื้อลูปในตัวอย่าง Matmul เปลี่ยนภาพ ฉันไม่มีคอมไพเลอร์ intel ที่มีอยู่ในเครื่องปัจจุบันของฉันดังนั้นฉันจึงใช้ gfortran แต่มีการเขียนลูปใหม่ใน mm_test.f90 เป็น

call cpu_time(start)  
do r=1,runs  
  mat_c=0.0d0  
     do j=1,n  
        do k=1,n  
  do i=1,n  
           mat_c(i,j)=mat_c(i,j)+mat_a(i,k)*mat_b(k,j)  
        end do  
     end do  
  end do  
end do  
call cpu_time(finish)  

เปลี่ยนผลลัพธ์ทั้งหมดสำหรับเครื่องของฉัน

ผลลัพธ์การกำหนดเวลาเวอร์ชันก่อนหน้าคือ:

#time ./mm_test_f 10000 100
 matmul    time   6.3620000000000001     
 triple do time   21.420999999999999     

ในขณะที่มีการวนซ้ำสามครั้งจัดใหม่ดังที่แสดงไว้ด้านบน:

#time ./mm_test_f 10000 100
 matmul    time   6.3929999999999998     
 triple do time   3.9190000000000005    

นี่คือ gcc / gfortran 4.7.2 20121109 บน Intel (R) Core (TM) i7-2600K CPU @ 3.40GHz

ธงคอมไพเลอร์ที่ใช้นั้นมาจาก Makefile ที่ฉันได้รับที่นี่ ...


3
ไม่น่าแปลกใจเนื่องจากหน่วยความจำของเมทริกซ์ในหน่วยความจำชอบหนึ่งคำสั่งเช่นถ้าแถวถูกเก็บไว้อย่างต่อเนื่องจะดีกว่าที่จะวนซ้ำแถวด้านในสุดเนื่องจากคุณสามารถโหลดแต่ละแถวในหน่วยความจำท้องถิ่นที่รวดเร็วกว่าเมื่อเทียบกับการโหลดซ้ำ ๆ ) เพื่อเข้าถึงองค์ประกอบเดียว ดูstackoverflow.com/questions/7395556
Christian Clason

ฉันเดาว่าฉันประหลาดใจที่ "matmul ที่แท้จริง" จะไม่ถูกเข้ารหัสเพื่อทำสิ่งนี้ มันเร็วกว่ามากเมื่อใช้ทริปเปิ้ลสั่งในวิธีที่สอง ดูเหมือนว่าจะอยู่ในชุดคอมไพเลอร์นี้เนื่องจากรุ่น gfortran ก่อนหน้านี้ที่ฉันสามารถทำได้ "แบน" มากกว่าในช่วงเวลาของพวกเขา - มันไม่สำคัญว่าคุณจะใช้ mult ในแบบไหน - ใช้เวลาใกล้เคียงกัน
Schatzi

-2

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

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