คอมไพเลอร์ของ Fortran สร้างโค้ดได้เร็วกว่าคอมไพเลอร์ C จริงๆหรือ


17

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

เหตุผลสำคัญที่เป็นเช่นนี้: คอมไพเลอร์ Fortran ปล่อยออกมาโดยเฉลี่ย 1,1 หน่วยประมวลผลคำสั่งต่อบรรทัดของรหัสในขณะที่ C คอมไพเลอร์ส่งเฉลี่ย 1.6 คำสั่งโปรเซสเซอร์ต่อบรรทัดของรหัส - ฉันไม่จำตัวเลขที่แน่นอน แต่ แนวคิดก็คือคอมไพเลอร์ C ปล่อยรหัสเครื่องจักรออกมาอย่างเห็นได้ชัดมากขึ้นดังนั้นจึงผลิตโปรแกรมที่ช้ากว่า

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


19
นั่นอาจหมายถึงว่าโปรแกรมของ Fortran นั้นละเอียดกว่า C ... การเปรียบเทียบที่มีความหมายสามารถทำได้โดยการใช้ฟังก์ชั่นเดียวกันในทั้งสองภาษาและเปรียบเทียบรหัสเครื่องที่เกิดขึ้น (ขนาดและความเร็ว)
PéterTörök

รหัสที่สร้างขึ้นนั้นรองรับการประมวลผลแบบขนานหรือไม่

@ PéterTörökมันก็หมายความว่าพูด BLAS และ LAPACK ใน Fortran ที่ใช้ในการทำงานได้ดีขึ้นแล้วพอร์ต C / C ++ ใด ๆ ของพวกเขา ตอนนี้ช่องว่างกำลังหดตัวอย่างรวดเร็ว
SK-logic

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

อดีต Fortran ไม่สนับสนุนการเรียกซ้ำดังนั้นจึงไม่จำเป็นต้องกดอาร์กิวเมนต์การเรียกฟังก์ชันลงบนสแต็กเนื่องจากจะมีพื้นที่ที่จัดสรรแบบคงที่สำหรับการขัดแย้งของแต่ละ funcion นี่คือหนึ่งในเหตุผลที่อาจเร็วกว่านี้ ฉันเดาคุณอาจพบคำตอบที่สมบูรณ์มากขึ้นที่นี่: amazon.com/Programming-Language-Pragmatics-Third-Edition/dp/…
Pedro Rolo

คำตอบ:


36

IIRC หนึ่งในเหตุผลหลักที่ว่ากันว่า Fortran เร็วกว่านั้นคือการไม่มีaliasing ตัวชี้ดังนั้นพวกเขาจึงสามารถใช้การปรับแต่งที่คอมไพเลอร์ C ไม่สามารถใช้ได้:

ใน FORTRAN ฟังก์ชันอาร์กิวเมนต์อาจไม่ใช้นามแฝงซึ่งกันและกันและคอมไพเลอร์จะถือว่ามันไม่ สิ่งนี้ทำให้การเพิ่มประสิทธิภาพที่ยอดเยี่ยมและเป็นหนึ่งในเหตุผลสำคัญที่ทำให้ชื่อเสียงของ FORTRAN เป็นภาษาที่รวดเร็ว (โปรดทราบว่าการใช้นามแฝงอาจยังคงเกิดขึ้นภายในฟังก์ชัน FORTRAN ตัวอย่างเช่นถ้า A คืออาร์เรย์และ i และ j เป็นดัชนีที่เกิดขึ้นมีค่าเดียวกันดังนั้น A [i] และ A [j] เป็นสองชื่อที่แตกต่างกันสำหรับ ตำแหน่งหน่วยความจำเดียวกันโชคดีเนื่องจากอาร์เรย์ฐานต้องมีชื่อเดียวกันจึงสามารถทำการวิเคราะห์ดัชนีเพื่อกำหนดกรณีที่ A [i] และ A [j] ไม่สามารถใช้นามแฝงได้)

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


เหตุผลอีกประการสำหรับการเพิ่มประสิทธิภาพที่ดีขึ้นก็คือการสนับสนุนพื้นเมืองสำหรับจำนวนเชิงซ้อน
SK-logic

ถูกต้องแน่นอนสำหรับ Fortran IV หรือมากกว่านั้น ไม่แน่ใจว่า FORTRAN ที่ทันสมัยยังไม่มีพอยน์เตอร์, ไดนามิก meory เป็นต้น
Ingo

2
นั่นเป็นเหตุผลเดียวกันกับที่เรามักจะพูดถึงการประกอบแบบอินไลน์เล็กน้อยเมื่อพัฒนาใน C และ C ++ ในอุตสาหกรรมเกม คนสามารถเรียกร้องได้บ่อยเท่าที่พวกเขาเช่นเดียวกับที่ "คอมไพเลอร์สามารถเพิ่มประสิทธิภาพได้ดีกว่ามนุษย์เขียนชุมนุม" ความจริงก็คือตัวชี้ aliasing หมายความว่าพวกเขามักจะไม่สามารถ รหัสที่เราสามารถเขียนด้วยมือจะผิดกฎหมายในทางเทคนิคสำหรับคอมไพเลอร์ที่จะปล่อยออกมารู้ว่ามันไม่ได้ทำอะไรเกี่ยวกับนามแฝงของตัวชี้
Carson63000

5
restrictคำสำคัญของ C อนุญาตให้ผู้สร้างฟังก์ชันระบุว่าตัวชี้ไม่มีนามแฝง สิ่งนี้เพียงพอที่จะแก้ไขความแตกต่างหรือมีมากกว่านั้นหรือไม่?
bk

@bk .: การโจมตี "จำกัด " ของ C "ครึ่งปัญหา"; มันทำให้เป็นไปได้ที่จะบอกว่าตัวชี้เฉพาะจะไม่นามแฝงสิ่งอื่นใดในช่วงชีวิตของมัน แต่ไม่มีวิธีที่จะบอกคอมไพเลอร์ว่าวัตถุที่ถูกส่งผ่านไปยังฟังก์ชั่นจะไม่ถูกใช้นามแฝง
supercat

8

การเปรียบเทียบไม่ถูกต้องโดยสิ้นเชิง

อันดับแรกเนื่องจาก @ PéterTörökชี้ให้เห็นคุณต้องเปรียบเทียบจำนวนบรรทัดในโปรแกรมที่เทียบเท่าจาก Fortran และ Cเพื่อเปรียบเทียบสิ่งนี้กับการเปรียบเทียบที่ถูกต้องกับจำนวนบรรทัดที่ผลิต

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

ยิ่งไปกว่านั้นการรันโค้ดแบบยาวสามารถทำได้เร็วกว่าเพราะมันจะส่งผลให้จำนวนบรรทัดที่ถูกเรียกใช้งานลดลง (เช่นLine Count! = Executioned Line Count )


5

Dan ถูกต้องโปรแกรมที่ยาวกว่าไม่ได้หมายถึงโปรแกรมที่ช้าลง ขึ้นอยู่กับสิ่งที่พวกเขาทำ

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

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


2
หากคุณใช้โครงสร้างข้อมูลที่ซับซ้อน FORTRAN อาจเป็นทางเลือกที่ผิด FORTRAN ได้รับการปรับแต่งให้ทำตัวเลขง่าย ๆ อย่างรวดเร็ว
Zachary K

4

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


4

คำสั่งอาจเป็นจริงในวันเก่า (ประมาณปลายปี 70) เมื่อ C อยู่ในวัยเด็กและ Fortran ได้รับการสนับสนุนจากผู้ผลิตรายใหญ่ทั้งหมดและได้รับการปรับให้เหมาะสมที่สุด Early Fortrans ขึ้นอยู่กับสถาปัตยกรรมของ IBM ดังนั้นสิ่งที่ง่ายอย่างเช่นเลขคณิตถ้าจะเป็นหนึ่งคำสั่งต่อชุดคำสั่ง สิ่งนี้เป็นจริงของเครื่องรุ่นเก่าอย่าง Data General และ Prime ซึ่งมีการข้าม 3 ทาง สิ่งนี้ใช้ไม่ได้กับชุดการเรียนการสอนที่ทันสมัยซึ่งไม่มีการกระโดด 3 ทาง

บรรทัดของรหัสไม่เท่ากันงบของรหัส Fortran เวอร์ชันก่อนหน้านี้อนุญาตเพียงหนึ่งคำสั่งต่อบรรทัด Fortran รุ่นที่ใหม่กว่าสามารถรับได้หลายใบต่อบรรทัด C สามารถมีหลายงบต่อบรรทัด ในคอมไพเลอร์ที่ผลิตเร็วกว่าเช่น IVF ของ Intel (เดิมคือ CVF, MS Powerstation) และ C ของ Intel ไม่มีความแตกต่างระหว่างทั้งสอง คอมไพเลอร์เหล่านี้ได้รับการปรับให้เหมาะสมที่สุด


4

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

พิจารณาฟังก์ชั่น:

void diff(float dest[], float src1[], float src2[], int n)
{
  for (int i=0; i<n; i++)
    dest[i] = src1[i] - src2[i];
}

ถ้าคอมไพเลอร์รู้ว่าพอยน์เตอร์แต่ละตัวจะระบุจุดเริ่มต้นของอาเรย์มันสามารถสร้างโค้ดที่จะทำหน้าที่ตามองค์ประกอบของอาเรย์ในแบบคู่ขนานหรือในลำดับใด ๆ เนื่องจากสำหรับ x! = y การดำเนินการบน dest [x ] จะไม่ส่งผลกระทบต่อ src1 [y] หรือ src2 [y] ตัวอย่างเช่นในบางระบบคอมไพเลอร์อาจได้รับประโยชน์จากการสร้างโค้ดที่เทียบเท่ากับ:

void dif(float dest[], float src1[], float src2[], int n)
{
  int i=0;
  float t1a,t1b,t2a,t2b,tsa,tsb;
  if (n > 2)
  {
    n-=4;
    t1a = src1[n+3]; t1b = src2[n+3]; t1b=src2[n+2]; t2b = src2[n+2];
    do
    {
      tsa = t1a-t2a;
      t1a = src1[n+1]; t2a = src2[n+1]; 
      tsb = t2b-t2b;
      dest[n+3] = tsa;
      t1b = src1[n]; t2b = src2[n]; 
      n-=2;
      dest[n+4] = tsb;
    } while(n >= 0);
    ... add some extra code to handle cleanup
  }
  else
    ... add some extra code to handle small values of n
}

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

ในขณะที่โปรแกรมเมอร์ C สามารถพยายามที่จะบรรลุผลการเปรียบเทียบโดยการเขียนโค้ดที่คลี่ลูปและทำการซ้อนทับการทำงานของการส่งผ่านที่อยู่ติดกันอย่างชัดเจนรหัสดังกล่าวสามารถลดประสิทธิภาพได้หากใช้ตัวแปรอัตโนมัติจำนวนมากที่คอมไพเลอร์ หน่วยความจำ เครื่องมือเพิ่มประสิทธิภาพของคอมไพเลอร์ FORTRAN น่าจะรู้มากกว่าโปรแกรมเมอร์เกี่ยวกับรูปแบบของการแทรกสอดที่จะให้ประสิทธิภาพที่ดีที่สุดในสถานการณ์ที่กำหนดและการตัดสินใจดังกล่าวมักจะดีที่สุดสำหรับคอมไพเลอร์ดังกล่าว ในขณะที่ C99 พยายามที่จะปรับปรุงสถานการณ์ของ C โดยการเพิ่มrestrictqualifier ซึ่งสามารถใช้ที่นี่ได้ถ้าdest[]เป็น array ที่แยกจากทั้งสองsrc1[]และsrc2[]หรือถ้าโปรแกรมเมอร์เพิ่ม loop แยกรุ่นของ loop เพื่อจัดการกับกรณีที่destถูกแยกจากกันsrc1และsrc2ที่ไหนsrc1[]และdestเท่ากันและsrc2เป็น disjoint ที่src2[]และdest[]เท่ากันและsrc1ถูก disjoint และที่ทั้งสามอาร์เรย์มีค่าเท่ากัน ในทางตรงกันข้าม FORTRAN สามารถจัดการกับทั้งสี่กรณีได้อย่างง่ายดายโดยใช้รหัสต้นฉบับเดียวกันและรหัสเครื่องเดียวกัน

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