ภาษาแอสเซมบลีแบบอินไลน์ช้ากว่ารหัสภาษา C ++ หรือไม่


183

ฉันพยายามเปรียบเทียบประสิทธิภาพของภาษาแอสเซมบลีแบบอินไลน์และรหัส C ++ ดังนั้นฉันจึงเขียนฟังก์ชั่นที่เพิ่มขนาด 2,000 อาร์เรย์สองอาร์เรย์เป็น 100,000 ครั้ง นี่คือรหัส:

#define TIMES 100000
void calcuC(int *x,int *y,int length)
{
    for(int i = 0; i < TIMES; i++)
    {
        for(int j = 0; j < length; j++)
            x[j] += y[j];
    }
}


void calcuAsm(int *x,int *y,int lengthOfArray)
{
    __asm
    {
        mov edi,TIMES
        start:
        mov esi,0
        mov ecx,lengthOfArray
        label:
        mov edx,x
        push edx
        mov eax,DWORD PTR [edx + esi*4]
        mov edx,y
        mov ebx,DWORD PTR [edx + esi*4]
        add eax,ebx
        pop edx
        mov [edx + esi*4],eax
        inc esi
        loop label
        dec edi
        cmp edi,0
        jnz start
    };
}

ที่นี่main():

int main() {
    bool errorOccured = false;
    setbuf(stdout,NULL);
    int *xC,*xAsm,*yC,*yAsm;
    xC = new int[2000];
    xAsm = new int[2000];
    yC = new int[2000];
    yAsm = new int[2000];
    for(int i = 0; i < 2000; i++)
    {
        xC[i] = 0;
        xAsm[i] = 0;
        yC[i] = i;
        yAsm[i] = i;
    }
    time_t start = clock();
    calcuC(xC,yC,2000);

    //    calcuAsm(xAsm,yAsm,2000);
    //    for(int i = 0; i < 2000; i++)
    //    {
    //        if(xC[i] != xAsm[i])
    //        {
    //            cout<<"xC["<<i<<"]="<<xC[i]<<" "<<"xAsm["<<i<<"]="<<xAsm[i]<<endl;
    //            errorOccured = true;
    //            break;
    //        }
    //    }
    //    if(errorOccured)
    //        cout<<"Error occurs!"<<endl;
    //    else
    //        cout<<"Works fine!"<<endl;

    time_t end = clock();

    //    cout<<"time = "<<(float)(end - start) / CLOCKS_PER_SEC<<"\n";

    cout<<"time = "<<end - start<<endl;
    return 0;
}

จากนั้นฉันรันโปรแกรมห้าครั้งเพื่อรับรอบของตัวประมวลผลซึ่งอาจถูกมองว่าเป็นเวลา ทุกครั้งที่ฉันเรียกใช้ฟังก์ชันใดฟังก์ชันหนึ่งที่กล่าวถึงข้างต้นเท่านั้น

และนี่คือผลลัพธ์

ฟังก์ชั่นของรุ่นประกอบ:

Debug   Release
---------------
732        668
733        680
659        672
667        675
684        694
Average:   677

ฟังก์ชั่นของรุ่น C ++:

Debug     Release
-----------------
1068      168
 999      166
1072      231
1002      166
1114      183
Average:  182

โค้ด C ++ ในโหมดรีลีสนั้นเร็วกว่าโค้ดแอสเซมบลีเกือบ 3.7 เท่า ทำไม?

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


29
ค่อนข้างมาก การประกอบ Handcoded มีความเหมาะสมในบางสถานการณ์ แต่ต้องใช้ความระมัดระวังเพื่อให้แน่ใจว่าเวอร์ชั่นการประกอบเร็วกว่าสิ่งที่สามารถทำได้ด้วยภาษาระดับสูงกว่า
แมกนัสฮอฟฟ์

161
คุณอาจพบว่าเป็นการแนะนำให้ศึกษารหัสที่สร้างโดยคอมไพเลอร์และพยายามเข้าใจว่าทำไมมันเร็วกว่าแอสเซมบลีเวอร์ชันของคุณ
Paul R

34
ใช่ดูเหมือนว่าคอมไพเลอร์จะดีกว่าการเขียน asm กว่าคุณ คอมไพเลอร์สมัยใหม่นั้นค่อนข้างดีจริงๆ
David Heffernan

20
คุณดูที่ชุดประกอบ GCC ที่ผลิตหรือไม่? เป็นไปได้ GCC ใช้คำแนะนำ MMX ฟังก์ชั่นของคุณขนานกันมากคุณอาจใช้โปรเซสเซอร์ N เพื่อคำนวณผลรวมในเวลา 1 / N th ลองใช้ฟังก์ชั่นที่ไม่มีความหวังสำหรับการขนาน
Chris

11
หืมมมผมคาดว่าจะมีคอมไพเลอร์ที่ดีที่จะทำเช่นนี้ ~ 100000 ครั้งเร็ว ...
PlasmaHH

คำตอบ:


261

ใช่เวลาส่วนใหญ่

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

คุณสามารถเสมอผลิตตัวอย่างซึ่งเป็นรหัสการชุมนุมที่ทำด้วยมือดีกว่ารวบรวมรหัส แต่มักจะเป็นตัวอย่างเช่นสมมุติหรือกิจวัตรประจำวันเดียวไม่ได้เป็นจริงโปรแกรม 500.000+ บรรทัดของรหัส c ++) ผมคิดว่าคอมไพเลอร์จะผลิตที่ดีกว่าการชุมนุมรหัส 95 ครั้ง% และบางครั้งมีเพียงบางครั้งที่หายากที่คุณอาจจะต้องเขียนประกอบสำหรับไม่กี่สั้นใช้มาก , ที่สำคัญประสิทธิภาพการปฏิบัติหรือเมื่อคุณมีการเข้าถึงมีภาษาระดับสูงที่คุณชื่นชอบ ไม่เปิดเผย คุณต้องการสัมผัสความซับซ้อนนี้หรือไม่? อ่านคำตอบที่ยอดเยี่ยมที่นี่ใน SO

ทำไมจึงเป็นเช่นนี้

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

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

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

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

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

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

ทั้งหมดนี้กล่าวว่าแม้ในขณะที่คุณสามารถผลิต 5 ~ 10 ครั้งเร็วชุมนุมรหัสที่คุณควรถามลูกค้าของคุณหากพวกเขาชอบที่จะจ่ายหนึ่งสัปดาห์ของเวลาของคุณหรือเพื่อซื้อ $ 50 เร็วกว่า CPU การปรับให้เหมาะสมสูงสุดบ่อยครั้งกว่า (และโดยเฉพาะอย่างยิ่งในแอปพลิเคชัน LOB) นั้นไม่จำเป็นต้องใช้จากเราส่วนใหญ่


9
ไม่แน่นอน ฉันคิดว่ามันดีกว่า 95% ของผู้คนใน 99% เท่า บางครั้งก็เป็นเพราะค่าใช้จ่ายสูง (เพราะคณิตศาสตร์ที่ซับซ้อน ) หรือการใช้เวลา บางครั้งเพราะเราเพียงแค่ลืมเกี่ยวกับการปรับให้เหมาะสม ...
Adriano Repetti

62
@ ja72 - ไม่มันไม่ดีกว่าในการเขียนโค้ด มันจะดีกว่าที่การเพิ่มประสิทธิภาพรหัส
Mike Baranczak

14
มันตอบโต้ได้ง่ายจนกว่าคุณจะพิจารณาอย่างถี่ถ้วน ในทำนองเดียวกันเครื่องที่ทำงานบน VM กำลังเริ่มทำการเพิ่มประสิทธิภาพรันไทม์ที่คอมไพเลอร์ก็ไม่มีข้อมูลที่จะทำ
Bill K

6
@ M28: คอมไพเลอร์สามารถใช้คำแนะนำเดียวกันได้ แน่นอนว่าพวกเขาจ่ายเงินในรูปของขนาดไบนารี่ (เพราะพวกเขาต้องระบุเส้นทางสำรองในกรณีที่ไม่สนับสนุนคำแนะนำเหล่านั้น) นอกจากนี้ส่วนใหญ่ "คำสั่งใหม่" ที่จะเพิ่มเป็นคำสั่ง SMID ต่อไปซึ่งทั้ง VMs และคอมไพเลอร์ค่อนข้างน่ากลัวในการใช้งาน VM ต้องจ่ายเงินสำหรับคุณสมบัตินี้โดยจะต้องรวบรวมรหัสเมื่อเริ่มต้น
Billy ONeal

9
@BILLK: PGO ทำสิ่งเดียวกันกับคอมไพเลอร์
Billy ONeal

194

รหัสการประกอบของคุณไม่ดีและอาจได้รับการปรับปรุง:

  • คุณกำลังผลักดันและเปิดใช้งานการลงทะเบียน ( EDX ) ในวงในของคุณ สิ่งนี้ควรถูกย้ายออกจากลูป
  • คุณโหลดตัวชี้อาร์เรย์ซ้ำในการวนซ้ำทุกครั้ง สิ่งนี้ควรย้ายออกจากลูป
  • คุณใช้การloopเรียนการสอนซึ่งเป็นที่รู้จักกันว่าตายช้าใน CPU ที่ทันสมัยที่สุด (อาจเป็นผลมาจากการใช้หนังสือประกอบโบราณ *)
  • คุณไม่ได้ประโยชน์จากการวนซ้ำแบบแมนนวล
  • คุณไม่ได้ใช้คำแนะนำSIMD

ดังนั้นหากคุณไม่ได้ปรับปรุงชุดทักษะของคุณอย่างมากมายเกี่ยวกับแอสเซมเบลอร์คุณไม่ควรเขียนโค้ดแอสเซมเบลอร์เพื่อประสิทธิภาพ

* แน่นอนฉันไม่รู้ว่าคุณได้loopรับคำแนะนำจากหนังสือประกอบโบราณจริงๆหรือไม่ แต่คุณแทบจะไม่เคยเห็นมันในรหัสโลกแห่งความจริงเพราะคอมไพเลอร์ทุกตัวนั้นฉลาดพอที่จะไม่เปล่งออกมาloopคุณเห็นมันในหนังสือที่ไม่ดีและล้าสมัยของ IMHO เท่านั้น


คอมไพเลอร์อาจจะยังคงปล่อยloop(และหลายคน "เลิก" คำแนะนำ) ถ้าคุณเพิ่มประสิทธิภาพสำหรับขนาด
phuclv

1
@phuclv ใช่ แต่คำถามเดิมเกี่ยวกับความเร็วไม่ใช่ขนาด
IGR94

60

ก่อนที่จะเจาะลึกเข้าไปในแอสเซมบลีมีการแปลงรหัสที่มีอยู่ในระดับที่สูงขึ้น

static int const TIMES = 100000;

void calcuC(int *x, int *y, int length) {
  for (int i = 0; i < TIMES; i++) {
    for (int j = 0; j < length; j++) {
      x[j] += y[j];
    }
  }
}

สามารถเปลี่ยนเป็นผ่านการหมุนวน :

static int const TIMES = 100000;

void calcuC(int *x, int *y, int length) {
    for (int j = 0; j < length; ++j) {
      for (int i = 0; i < TIMES; ++i) {
        x[j] += y[j];
      }
    }
}

ซึ่งดีกว่าเท่าที่หน่วยความจำท้องถิ่นไป

นี่อาจเป็นการปรับให้เหมาะสมยิ่งขึ้นการทำa += bX คูณเท่ากับทำa += X * bเพื่อเราจะได้รับ:

static int const TIMES = 100000;

void calcuC(int *x, int *y, int length) {
    for (int j = 0; j < length; ++j) {
      x[j] += TIMES * y[j];
    }
}

อย่างไรก็ตามดูเหมือนว่าเครื่องมือเพิ่มประสิทธิภาพที่ฉันชอบ (LLVM) จะไม่ทำการเปลี่ยนแปลงนี้

[แก้ไข]ฉันพบว่าการเปลี่ยนแปลงจะดำเนินการถ้าเรามีrestrictรอบคัดเลือกไปและx yแน่นอนโดยไม่มีข้อ จำกัด นี้x[j]และy[j]สามารถนามแฝงไปยังตำแหน่งเดียวกันซึ่งทำให้การเปลี่ยนแปลงนี้ผิดพลาด [แก้ไขท้าย]

อย่างไรก็ตามนี่คือฉันคิดว่ารุ่น C ที่ได้รับการปรับปรุง มันง่ายกว่ามากแล้ว ตามนี้นี่คือรอยแตกของฉันที่ ASM (ฉันปล่อยให้เสียงดังดังกังวานสร้างมันฉันไม่มีประโยชน์ที่มัน):

calcuAsm:                               # @calcuAsm
.Ltmp0:
    .cfi_startproc
# BB#0:
    testl   %edx, %edx
    jle .LBB0_2
    .align  16, 0x90
.LBB0_1:                                # %.lr.ph
                                        # =>This Inner Loop Header: Depth=1
    imull   $100000, (%rsi), %eax   # imm = 0x186A0
    addl    %eax, (%rdi)
    addq    $4, %rsi
    addq    $4, %rdi
    decl    %edx
    jne .LBB0_1
.LBB0_2:                                # %._crit_edge
    ret
.Ltmp1:
    .size   calcuAsm, .Ltmp1-calcuAsm
.Ltmp2:
    .cfi_endproc

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


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

2
@ user957121: เราสามารถเพิ่มประสิทธิภาพได้ดีขึ้นเมื่อเรามีข้อมูลเพิ่มเติม โดยเฉพาะที่นี่สิ่งที่เป็นอุปสรรคต่อการเรียบเรียงเป็นไปได้aliasingระหว่างและx yนั่นคือคอมไพเลอร์ไม่สามารถมั่นใจได้ว่าทั้งหมดi,jในที่เรามี[0, length) x + i != y + jหากมีการทับซ้อนกันแล้วการเพิ่มประสิทธิภาพเป็นไปไม่ได้ ภาษา C แนะนำrestrictคำสำคัญเพื่อบอกคอมไพเลอร์ว่าพอยน์เตอร์สองตัวไม่สามารถใช้นามแฝงได้ แต่มันไม่สามารถใช้งานกับอาร์เรย์ได้เพราะมันยังสามารถซ้อนทับได้แม้ว่าจะไม่ได้ใช้นามแฝงก็ตาม
Matthieu M.

ปัจจุบัน GCC และ Clang auto-vectorize ปัจจุบัน (หลังจากตรวจสอบแล้วว่าไม่ทับซ้อนกันหากคุณข้ามไป__restrict) SSE2 เป็นพื้นฐานสำหรับ x86-64 และด้วยการสับเปลี่ยน SSE2 สามารถคูณทวีคูณ 32- บิตได้ 2 เท่าพร้อมกัน (ผลิตผลิตภัณฑ์ 64- บิตดังนั้นการสับเพื่อนำผลลัพธ์กลับมารวมกัน) godbolt.org/z/r7F_uo (ต้องใช้ SSE4.1 สำหรับpmulld: บรรจุ 32x32 => ทวีคูณแบบ 32 บิต) GCC มีเคล็ดลับอย่างเรียบร้อยในการเปลี่ยนตัวคูณจำนวนเต็มคงที่ให้เป็นกะ / เพิ่ม (และ / หรือลบ) ซึ่งดีสำหรับตัวคูณที่ตั้งค่าไม่กี่บิต รหัสการสลับเพลงที่หนักของ Clang กำลังเกิดขึ้นบนคอขวดในการสับเปลี่ยนปริมาณงานบน CPU ของ Intel
Peter Cordes

41

คำตอบสั้น ๆ :ใช่

คำตอบยาว:ใช่เว้นแต่คุณจะรู้ว่าคุณกำลังทำอะไรอยู่และมีเหตุผลที่จะทำเช่นนั้น


3
จากนั้นเฉพาะเมื่อคุณเรียกใช้เครื่องมือการทำโปรไฟล์การประกอบระดับเช่น vtune สำหรับชิป Intel เพื่อดูว่าคุณสามารถปรับปรุงสิ่งต่าง ๆ ได้อย่างไร
Mark Mullin

1
เทคนิคนี้ตอบคำถาม แต่ก็ไร้ประโยชน์อย่างสมบูรณ์ A -1 จากฉัน
Navin

2
คำตอบยาวมาก: "ใช่เว้นแต่คุณจะรู้สึกเหมือนเปลี่ยนรหัสของคุณทั้งหมดเมื่อใดก็ตามที่ใหม่ (ER) CPU จะใช้ขั้นตอนวิธีการเลือกที่ดีที่สุด แต่ให้คอมไพเลอร์ทำเพิ่มประสิทธิภาพ"
Tommylee2k

35

ฉันได้แก้ไขรหัส asm ของฉัน:

  __asm
{   
    mov ebx,TIMES
 start:
    mov ecx,lengthOfArray
    mov esi,x
    shr ecx,1
    mov edi,y
label:
    movq mm0,QWORD PTR[esi]
    paddd mm0,QWORD PTR[edi]
    add edi,8
    movq QWORD PTR[esi],mm0
    add esi,8
    dec ecx 
    jnz label
    dec ebx
    jnz start
};

ผลลัพธ์สำหรับรุ่นที่วางจำหน่าย:

 Function of assembly version: 81
 Function of C++ version: 161

รหัสการประกอบในโหมดการเปิดตัวนั้นเร็วกว่า C ++ เกือบ 2 เท่า


18
ตอนนี้ถ้าคุณเริ่มใช้ SSE แทน MMX (ชื่อการลงทะเบียนxmm0แทนmm0) คุณจะได้รับความเร็วเพิ่มอีกสองเท่า ;-)
Gunther Piez

8
ฉันเปลี่ยนได้ 41 สำหรับรุ่นแอสเซมบลี มันมีอยู่ใน 4 ครั้งเร็ว :)
sasha

3
นอกจากนี้ยังสามารถรับได้มากขึ้นอีก 5% หากใช้รีจิสเตอร์ xmm ทั้งหมด
sasha

7
ตอนนี้ถ้าคุณคิดถึงเวลาที่คุณใช้จริง: การชุมนุมประมาณ 10 ชั่วโมงหรือมากกว่านั้น? C ++, ฉันเดาไม่กี่นาทีเหรอ? มีผู้ชนะที่ชัดเจนที่นี่เว้นแต่จะเป็นรหัสที่มีประสิทธิภาพที่สำคัญ
Calimo

1
คอมไพเลอร์ที่ดีจะทำการ vectorize อัตโนมัติด้วยpaddd xmm(หลังจากตรวจสอบการทับซ้อนระหว่างxและyเนื่องจากคุณไม่ได้ใช้int *__restrict x) ยกตัวอย่างเช่น gcc ไม่ว่า: godbolt.org/z/c2JG0- หรือหลังจากใส่เข้าไปmainแล้วก็ไม่จำเป็นต้องตรวจสอบการทับซ้อนเพราะสามารถดูการจัดสรรและพิสูจน์ว่ามันไม่ทับซ้อนกัน (และจะได้รับการจัดตำแหน่ง 16- ไบต์ในการใช้งาน x86-64 บางอย่างเช่นกันซึ่งไม่ใช่กรณีสำหรับคำจำกัดความของสแตนอะโลน) และถ้าคุณคอมไพล์ด้วยgcc -O3 -march=nativeคุณสามารถรับ 256 บิตหรือ 512 บิต vectorization
Peter Cordes

24

หมายความว่าฉันไม่ควรเชื่อใจในประสิทธิภาพของภาษาแอสเซมบลีที่เขียนด้วยมือของฉัน

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

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


2
ฉันคิดว่ามันเป็นการเขียนโดยเฉพาะอย่างยิ่งสำหรับโปรเซสเซอร์ x86 ที่ทันสมัยเป็นเรื่องยากเป็นพิเศษในการเขียนรหัสแอสเซมบลีที่มีประสิทธิภาพเนื่องจากการมีอยู่ของท่อหน่วยปฏิบัติการหลายหน่วยและลูกเล่นอื่น ๆ ในทุกแกน การเขียนโค้ดที่ทำให้การใช้งานทรัพยากรเหล่านี้มีความสมดุลเพื่อให้ได้ความเร็วสูงสุดในการดำเนินการมักจะส่งผลให้เกิดรหัสด้วยตรรกะที่ไม่ตรงไปตรงมาซึ่ง "ไม่ควร" จะรวดเร็วตามภูมิปัญญาการชุมนุม "ธรรมดา" แต่สำหรับซีพียูที่ซับซ้อนน้อยกว่าเป็นประสบการณ์ของฉันที่การสร้างโค้ดคอมไพเลอร์ C สามารถดีขึ้นอย่างมีนัยสำคัญ
Olof Forshell

4
รหัสคอมไพเลอร์ C สามารถปรับปรุงได้แม้กระทั่งบนซีพียู x86 ที่ทันสมัย แต่คุณต้องเข้าใจ CPU เป็นอย่างดีซึ่งเป็นเรื่องยากที่จะทำกับซีพียู x86 ที่ทันสมัย นั่นคือจุดของฉัน หากคุณไม่เข้าใจฮาร์ดแวร์ที่คุณกำหนดเป้าหมายคุณจะไม่สามารถปรับให้เหมาะสมได้ และแล้วคอมไพเลอร์มีแนวโน้มที่จะทำผลงานที่ดีกว่า
jalf

1
และถ้าคุณต้องการรวบรวมคอมไพเลอร์จริงๆคุณต้องมีความคิดสร้างสรรค์และปรับให้เหมาะสมในวิธีที่คอมไพเลอร์ไม่สามารถทำได้ มันเป็นการแลกเปลี่ยนเวลา / รางวัลนั่นคือสาเหตุที่ C เป็นภาษาสคริปต์สำหรับบางรหัสและระดับกลางสำหรับภาษาระดับที่สูงขึ้นสำหรับผู้อื่น สำหรับฉันแล้วการชุมนุมนั้นสนุกกว่า :) เหมือนgrc.com/smgassembly.htm
Hawken

22

เหตุผลเดียวที่จะใช้ภาษาแอสเซมบลีในปัจจุบันคือการใช้คุณสมบัติบางอย่างที่ภาษาไม่สามารถเข้าถึงได้

สิ่งนี้ใช้กับ:

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

แต่คอมไพเลอร์ปัจจุบันค่อนข้างฉลาดพวกเขายังสามารถแทนที่งบสองคำที่แยกกันเช่นเดียว d = a / b; r = a % b;กับคำสั่งเดียวที่คำนวณการหารและส่วนที่เหลือในคราวเดียวถ้ามันพร้อมใช้งานแม้ว่า C ไม่มีตัวดำเนินการดังกล่าว


10
มีสถานที่อื่นสำหรับ ASM นอกเหนือจากสองแห่งนี้ กล่าวคือไลบรารี bignum มักจะเร็วกว่า ASM มากกว่า C อย่างเห็นได้ชัดเนื่องจากมีการเข้าถึงการพกพาธงและส่วนบนของการคูณและเช่นนั้น คุณสามารถทำสิ่งเหล่านี้ในพกพา C เช่นกัน แต่มันช้ามาก
Mooing Duck

@MooingDuck นั่นอาจถือได้ว่าเป็นการเข้าใช้งานคุณสมบัติฮาร์ดแวร์ฮาร์ดแวร์ที่ไม่สามารถใช้งานได้โดยตรงในภาษา ... แต่ตราบใดที่คุณเพิ่งแปลรหัสระดับสูงของคุณเป็นชุดประกอบด้วยมือคอมไพเลอร์จะเอาชนะคุณ
fortran

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

1
@fortran คุณเพียงแค่บอกว่าถ้าคุณไม่ปรับรหัสของคุณมันจะไม่เร็วเท่ากับรหัสที่คอมไพเลอร์ปรับให้เหมาะสม การเพิ่มประสิทธิภาพเป็นเหตุผลหนึ่งที่จะเขียนประกอบในสถานที่แรก หากคุณหมายถึงการแปลแล้วปรับให้เหมาะสมไม่มีเหตุผลที่คอมไพเลอร์จะเอาชนะคุณเว้นแต่คุณจะไม่เก่งในการเพิ่มประสิทธิภาพการประกอบ ดังนั้นในการเอาชนะคอมไพเลอร์คุณต้องปรับให้เหมาะสมในวิธีที่คอมไพเลอร์ไม่สามารถทำได้ มันอธิบายได้ด้วยตนเอง เหตุผลเดียวที่จะเขียนประกอบคือถ้าคุณจะดีกว่าคอมไพเลอร์ / ล่าม นั่นเป็นเหตุผลที่ปฏิบัติได้จริงในการเขียนชุดประกอบ
Hawken

1
เพียงแค่พูดว่า: เสียงดังกราวมีการเข้าถึงธงพก 128 บิตคูณและอื่น ๆ ผ่านฟังก์ชั่นในตัว และสามารถรวมสิ่งเหล่านี้เข้ากับอัลกอริธึมการปรับให้เหมาะสมตามปกติ
gnasher729

19

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

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

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

สำหรับแรงบันดาลใจฉันขอแนะนำให้คุณค้นหาบทความของMichael Abrash (หากคุณไม่เคยได้ยินจากเขาเขาเป็นกูรูด้านการปรับให้เหมาะสมเขายังได้ร่วมมือกับ John Carmack ในการเพิ่มประสิทธิภาพของตัวแสดงซอฟต์แวร์ Quake!)

"ไม่มีสิ่งใดที่เป็นรหัสที่เร็วที่สุด" - Michael Abrash


2
ฉันเชื่อว่าหนึ่งในหนังสือของ Michael Abrash คือหนังสือกราฟิคโปรแกรมดำ แต่เขาไม่ใช่คนเดียวที่จะใช้ชุดประกอบคริสซอว์เยอร์เขียนเกมสองเกมแรกที่ประกอบด้วยตัวเอง
Hawken

14

ฉันได้เปลี่ยนรหัส asm:

 __asm
{ 
    mov ebx,TIMES
 start:
    mov ecx,lengthOfArray
    mov esi,x
    shr ecx,2
    mov edi,y
label:
    mov eax,DWORD PTR [esi]
    add eax,DWORD PTR [edi]
    add edi,4   
    dec ecx 
    mov DWORD PTR [esi],eax
    add esi,4
    test ecx,ecx
    jnz label
    dec ebx
    test ebx,ebx
    jnz start
};

ผลลัพธ์สำหรับรุ่นที่วางจำหน่าย:

 Function of assembly version: 41
 Function of C++ version: 161

รหัสการประกอบในโหมดการเปิดตัวนั้นเร็วกว่า C ++ เกือบ 4 เท่า IMHo ความเร็วของรหัสการประกอบขึ้นอยู่กับโปรแกรมเมอร์


ใช่รหัสของฉันต้องได้รับการปรับปรุงให้ดีที่สุดทำงานได้ดีสำหรับคุณและขอบคุณ!
user957121

5
มันเร็วกว่าสี่เท่าเพราะคุณทำงานเพียงหนึ่งในสี่ :-) shr ecx,2ฟุ่มเฟือยเพราะความยาวของอาเรย์นั้นถูกกำหนดไว้แล้วintและไม่ได้อยู่ในไบต์ ดังนั้นคุณจะได้ความเร็วเท่าเดิม คุณสามารถลอง padddจากคำตอบแฮโรลด์นี้จะเร็วขึ้นจริงๆ
กุนเธอร์ Piez

13

มันเป็นหัวข้อที่น่าสนใจมาก!
ฉันได้เปลี่ยน MMX โดย SSE ในรหัสของ Sasha
นี่คือผลลัพธ์ของฉัน:

Function of C++ version:      315
Function of assembly(simply): 312
Function of assembly  (MMX):  136
Function of assembly  (SSE):  62

รหัสการประกอบที่มี SSE เร็วกว่า C ++ 5 เท่า


12

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

ตัวอย่างเช่นแม้ฉันไม่แน่ใจว่ามันถูกต้องอีกต่อไป :):

การทำ:

mov eax,0

ค่าใช้จ่ายรอบมากกว่า

xor eax,eax

ซึ่งทำสิ่งเดียวกัน

คอมไพเลอร์รู้เทคนิคเหล่านี้ทั้งหมดและใช้


4
ยังคงเป็นจริงดูstackoverflow.com/questions/1396527/... ไม่ใช่เพราะวงรอบที่ใช้ แต่เป็นเพราะรอยเท้าหน่วยความจำลดลง
กุนเธอร์เพียซ

10

คอมไพเลอร์เอาชนะคุณ ฉันจะลองดู แต่ฉันจะไม่รับประกันอะไรเลย ฉันจะสมมติว่า "การคูณ" โดย TIMES นั้นหมายถึงการทำให้การทดสอบประสิทธิภาพมีความเกี่ยวข้องยิ่งขึ้นyและxมีการจัดแนว 16 และนั่นlengthก็คือการคูณที่ไม่เป็นศูนย์ของ 4 นั่นอาจเป็นจริงทั้งหมด

  mov ecx,length
  lea esi,[y+4*ecx]
  lea edi,[x+4*ecx]
  neg ecx
loop:
  movdqa xmm0,[esi+4*ecx]
  paddd xmm0,[edi+4*ecx]
  movdqa [edi+4*ecx],xmm0
  add ecx,4
  jnz loop

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


ฉันคิดว่าการกำหนดที่ซับซ้อนกำลังทำให้โค้ดของคุณช้าลงหากคุณเปลี่ยนรหัสเป็นmov ecx, length, lea ecx,[ecx*4], mov eax,16... add ecx,eaxและจากนั้นใช้ [esi + ecx] ทุกที่ที่คุณจะหลีกเลี่ยงแผงวงจร 1 รอบต่อการเรียนการสอนเพื่อเร่งจำนวนลูป (หากคุณมี Skylake ล่าสุดไม่สามารถใช้งานได้) การเพิ่มการลงทะเบียนทำให้การวนลูปแน่นขึ้นซึ่งอาจช่วยได้หรือไม่ก็ได้
Johan

@Johan ที่ไม่ควรเป็นแผงลอย แต่มีความล่าช้ารอบเพิ่มเติม แต่แน่ใจว่ามันจะไม่เป็นอันตรายหากไม่มี ... ฉันเขียนโค้ดนี้สำหรับ Core2 ซึ่งไม่มีปัญหานั้น ไม่ใช่ r + r เช่นกัน "ซับซ้อน" btw หรือไม่
แฮโรลด์

7

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

เป็นเพราะแม้แต่การเพิ่มประสิทธิภาพที่เล็กที่สุดที่คอมไพเลอร์ทำได้ดีกว่าโค้ดที่เข้มงวดของคุณโดยไม่มีการเพิ่มประสิทธิภาพเลย

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


3
ฉันคิดว่าสิ่งนี้ขึ้นอยู่กับภาษาและคอมไพเลอร์ ฉันสามารถจินตนาการคอมไพเลอร์ C ที่ไม่มีประสิทธิภาพอย่างมากซึ่งผลลัพธ์อาจถูกตีได้ง่ายด้วยการเขียนโดยมนุษย์ที่ตรงไปตรงมา GCC ไม่มาก
Casey Rodarmor

ด้วยคอมไพเลอร์ C / ++ ที่ทำหน้าที่ดังกล่าวและมีเพียง 3 วิชาหลัก ๆ พวกเขามักจะค่อนข้างดีในสิ่งที่พวกเขาทำ ยังคงเป็นไปได้ (มาก) ในบางสถานการณ์ที่การเขียนด้วยลายมือจะเร็วขึ้น ไลบรารีคณิตศาสตร์จำนวนมากเลื่อนไปที่ asm เพื่อจัดการค่าหลายค่า / กว้างได้ดียิ่งขึ้น ดังนั้นในขณะที่การันตีค่อนข้างแข็งแกร่งเกินไป แต่ก็มีโอกาส
ssube

@peachykeen: ฉันไม่ได้หมายความว่าชุดประกอบรับรองว่าจะช้ากว่า C ++ โดยทั่วไป ฉันหมายถึง "รับประกัน" ในกรณีที่คุณมีรหัส C ++ และแปลมันทีละบรรทัดเพื่อประกอบ อ่านย่อหน้าสุดท้ายของคำตอบของฉันมากเกินไป :)
vsz

5

ในฐานะคอมไพเลอร์ฉันจะแทนที่ลูปด้วยขนาดคงที่เป็นงานการประมวลผลจำนวนมาก

int a = 10;
for (int i = 0; i < 3; i += 1) {
    a = a + i;
}

จะผลิต

int a = 10;
a = a + 0;
a = a + 1;
a = a + 2;

และในที่สุดมันก็จะรู้ว่า "a = a + 0;" ไม่มีประโยชน์ดังนั้นจึงจะลบบรรทัดนี้ หวังว่าบางสิ่งในหัวของคุณตอนนี้ยินดีที่จะแนบตัวเลือกการเพิ่มประสิทธิภาพบางอย่างเป็นความคิดเห็น การเพิ่มประสิทธิภาพที่มีประสิทธิภาพมากเหล่านี้จะทำให้ภาษาที่รวบรวมได้เร็วขึ้น


4
และหากไม่มีaความผันผวนมีโอกาสที่ดีที่คอมไพเลอร์จะทำint a = 13;ตั้งแต่เริ่มต้น
vsz


4

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

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


3

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

inline void set_port_high(void)
{
  (*((volatile unsigned char*)0x40001204) = 0xFF);
}

คอมไพเลอร์สำหรับรหัส ARM แบบ 32 บิตตามที่กล่าวไว้ข้างต้นมีแนวโน้มว่าจะทำให้มันเป็นดังนี้:

ldr  r0,=0x40001204
mov  r1,#0
strb r1,[r0]
[a fourth word somewhere holding the constant 0x40001204]

หรือบางที

ldr  r0,=0x40001000  ; Some assemblers like to round pointer loads to multiples of 4096
mov  r1,#0
strb r1,[r0+0x204]
[a fourth word somewhere holding the constant 0x40001000]

ซึ่งสามารถปรับให้เหมาะสมในโค้ดที่ประกอบด้วยมือเล็กน้อยเช่น:

ldr  r0,=0x400011FF
strb r0,[r0+5]
[a third word somewhere holding the constant 0x400011FF]

หรือ

mvn  r0,#0xC0       ; Load with 0x3FFFFFFF
add  r0,r0,#0x1200  ; Add 0x1200, yielding 0x400011FF
strb r0,[r0+5]

ทั้งสองวิธีที่ประกอบด้วยมือนั้นต้องการพื้นที่โค้ด 12 ไบต์แทนที่จะเป็น 16 หลังจะแทนที่ "โหลด" ด้วย "เพิ่ม" ซึ่งจะอยู่ใน ARM7-TDMI รันสองรอบเร็วขึ้น หากรหัสจะถูกดำเนินการในบริบทที่ r0 ไม่ทราบ / ไม่สนใจเวอร์ชั่นภาษาแอสเซมบลีจะค่อนข้างดีกว่าเวอร์ชันที่รวบรวม ในทางตรงกันข้ามสมมติว่าคอมไพเลอร์รู้ว่าบาง register [เช่น r5] กำลังจะเก็บค่าที่อยู่ภายใน 2047 ไบต์ของที่อยู่ที่ต้องการ 0x40001204 [เช่น 0x40001000] และรู้เพิ่มเติมว่าบาง register [เช่น r7] เพื่อเก็บค่าที่บิตต่ำเป็น 0xFF ในกรณีนั้นคอมไพเลอร์สามารถปรับโค้ด C ให้เหมาะกับ:

strb r7,[r5+0x204]

สั้นกว่าและเร็วกว่าโค้ดแอสเซมบลีที่ปรับให้เหมาะสมที่สุด เพิ่มเติมสมมติว่า set_port_high เกิดขึ้นในบริบท:

int temp = function1();
set_port_high();
function2(temp); // Assume temp is not used after this

ไม่น่าเป็นไปได้เลยเมื่อทำการเข้ารหัสสำหรับระบบฝังตัว หากset_port_highเขียนในชุดรหัสคอมไพเลอร์จะต้องย้าย r0 (ซึ่งเก็บค่าส่งคืนจากfunction1) ที่อื่นก่อนที่จะเรียกใช้รหัสชุดประกอบจากนั้นย้ายค่านั้นกลับไปที่ r0 หลังจากนั้น (เนื่องจากมีสี่คำสั่งเดียวที่เล็กกว่าและเร็วกว่า รหัสชุดประกอบ "ที่ปรับให้เหมาะกับมือ"function2คาดว่าพารามิเตอร์แรกใน r0) ดังนั้นรหัสชุดประกอบ "ที่ได้รับการเพิ่มประสิทธิภาพ" จะต้องมีห้าคำแนะนำ แม้ว่าคอมไพเลอร์ไม่ทราบว่ามีรีจิสเตอร์ใดเก็บที่อยู่หรือค่าที่จะเก็บไว้เวอร์ชันสี่คำสั่ง (ซึ่งสามารถปรับให้ใช้รีจิสเตอร์ที่มีอยู่ได้ - ไม่จำเป็นว่า r0 และ r1) จะชนะการชุมนุม - รุ่นภาษา หากคอมไพเลอร์มีที่อยู่และข้อมูลที่จำเป็นใน r5 และ r7 ตามที่อธิบายไว้ก่อนหน้านี้function1จะไม่แก้ไขการลงทะเบียนเหล่านั้นและดังนั้นจึงสามารถแทนที่set_port_highstrbคำสั่ง

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

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

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

ldrh  r0,[r1],#2! ; Fetch with post-increment
ldrb  r1,[r8,r0 asr #10]
sub   pc,r8,r1,asl #2

register r8 จะเก็บที่อยู่ของตารางการแจกจ่ายหลักเสมอ (ภายในลูปที่โค้ดใช้เวลา 98% ของเวลาไม่มีอะไรที่เคยใช้เพื่อจุดประสงค์อื่น); 64 รายการทั้งหมดที่อ้างถึงที่อยู่ใน 256 ไบต์ก่อนหน้า เนื่องจากลูปหลักมีในกรณีส่วนใหญ่การ จำกัด เวลาดำเนินการอย่างหนักประมาณ 60 รอบการดึงและส่งเก้ารอบนั้นมีประโยชน์มากต่อการบรรลุเป้าหมายนั้น การใช้ตารางที่อยู่ 256 32 บิตจะเร็วขึ้นหนึ่งรอบ แต่จะกลืนกิน RAM ที่มีค่ามากขึ้น 1KB [แฟลชน่าจะเพิ่มสถานะรอมากกว่าหนึ่งสถานะ] การใช้ที่อยู่ 64- บิต 64 จะต้องมีการเพิ่มคำสั่งเพื่อปกปิดบางส่วนจากคำที่ดึงมาและยังคงได้รับการ gobbled ขึ้น 192 bytes มากกว่าตารางที่ฉันใช้จริง การใช้ตารางออฟเซ็ต 8 บิตให้โค้ดที่เล็กและรวดเร็วมาก แต่ไม่ใช่สิ่งที่ฉันคาดหวังว่าคอมไพเลอร์จะเกิดขึ้นด้วย; ฉันยังไม่คาดหวังให้คอมไพเลอร์อุทิศการลงทะเบียน "เต็มเวลา" เพื่อเก็บที่อยู่ตาราง

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


2

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


2

C ++ เร็วกว่าเว้นแต่คุณจะใช้ภาษาแอสเซมบลีที่มีความรู้ลึกกว่าด้วยวิธีที่ถูกต้อง

เมื่อฉันใช้รหัสใน ASM ฉันจะจัดระเบียบคำแนะนำด้วยตนเองเพื่อให้ CPU สามารถประมวลผลคำสั่งเพิ่มเติมได้แบบขนานเมื่อเป็นไปได้อย่างมีเหตุผล ฉันเพิ่งจะใช้ RAM เมื่อฉันใช้รหัสใน ASM ตัวอย่าง: อาจมีรหัส 20,000 บรรทัดใน ASM และฉันไม่เคยใช้ push / pop มาก่อน

คุณสามารถกระโดดข้าม opcode เพื่อปรับเปลี่ยนรหัสและพฤติกรรมได้โดยไม่ต้องมีการปรับเปลี่ยนรหัสด้วยตนเอง การเข้าถึงรีจิสเตอร์นั้นใช้ 1 ขีด (บางครั้งใช้. 25 ขีด) ของซีพียูการเข้าถึงแรมอาจใช้เวลาหลายร้อย

สำหรับการผจญภัย ASM ครั้งสุดท้ายของฉันฉันไม่เคยใช้ RAM เพื่อจัดเก็บตัวแปร (สำหรับ ASM นับพันรายการ) ASM อาจเร็วกว่า C ++ อย่างไม่น่าเชื่อ แต่มันก็ขึ้นอยู่กับปัจจัยหลายอย่างเช่น:

1. I was writing my apps to run on the bare metal.
2. I was writing my own boot loader that was starting my programs in ASM so there was no OS management in the middle.

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

ตัวอย่างเช่นโปรแกรมสุดท้ายที่ฉันเขียนคือใช้ JS และ GLSL และฉันไม่เคยสังเกตเห็นปัญหาประสิทธิภาพใด ๆ แม้แต่พูดถึง JS ซึ่งช้า เนื่องจากแนวคิดของการเขียนโปรแกรม GPU สำหรับ 3D ทำให้ความเร็วของภาษาที่ส่งคำสั่งไปยัง GPU นั้นแทบไม่เกี่ยวข้อง

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

สภาส่วนบุคคลของฉันคืออย่าเขียนรหัสการประกอบหากคุณสามารถหลีกเลี่ยงได้แม้ว่าฉันจะรักการประชุมก็ตาม


1

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


เมื่อคุณต้องการเอาชนะคอมไพเลอร์ก็มักจะง่ายกว่าที่จะเอาท์พุท asm สำหรับฟังก์ชั่นของคุณและเปลี่ยนให้เป็นฟังก์ชั่น asm แบบสแตนด์อะโลนที่คุณปรับแต่ง การใช้inline asm เป็นงานพิเศษที่จะได้รับส่วนต่อประสานระหว่าง C ++ และ asm ที่ถูกต้องและตรวจสอบว่ามันรวบรวมรหัสที่เหมาะสม (แต่อย่างน้อยเมื่อทำเพื่อความสนุกคุณไม่ต้องกังวลเกี่ยวกับการเอาชนะการปรับให้เหมาะสมเช่นการแพร่กระจายอย่างต่อเนื่องเมื่อฟังก์ชั่นอินไลน์เป็นอย่างอื่น gcc.gnu.org/wiki/DontUseInlineAsm )
Peter Cordes

ดูเพิ่มเติมCollatz-conjecture C ++ เทียบกับ asm เขียนด้วยมือถาม - ตอบเพิ่มเติมเกี่ยวกับการรวบรวมคอมไพเลอร์เพื่อความสนุก :) และคำแนะนำเกี่ยวกับวิธีใช้สิ่งที่คุณเรียนรู้เพื่อปรับเปลี่ยน C ++ เพื่อช่วยให้คอมไพเลอร์สร้างรหัสได้ดีขึ้น
Peter Cordes

@PeterCordes ดังนั้นสิ่งที่คุณพูดคือคุณเห็นด้วย
madoki

1
ใช่ asm คือความสนุกยกเว้นinline asm จะเป็นตัวเลือกที่ผิดแม้ในการเล่น นี่เป็นคำถามทางเทคนิคแบบอินไลน์ดังนั้นจึงเป็นการดีที่จะตอบคำถามของคุณในประเด็นนี้ นอกจากนี้นี่เป็นความคิดเห็นมากกว่าคำตอบ
Peter Cordes

ตกลงตกลง ฉันเคยเป็นผู้ชายคนหนึ่ง แต่เป็น 80
madoki

-2

คอมไพเลอร์ c ++ จะทำการเพิ่มประสิทธิภาพในระดับองค์กรหลังจากสร้างโค้ดที่จะใช้ประโยชน์จากฟังก์ชั่นของซีพียูเป้าหมาย HLL จะไม่วิ่งเร็วกว่าแอสเซมเบลอร์ด้วยเหตุผลหลายประการ; 1. ) HLL จะถูกคอมไพล์และส่งออกด้วยรหัส Accessor การตรวจสอบขอบเขตและอาจสร้างขึ้นในการรวบรวมขยะ (เดิมคือการกำหนดขอบเขตในลักษณะ OOP) ทุกรอบที่ต้องการ (การพลิกและ flops) HLL ทำงานได้อย่างยอดเยี่ยมในวันนี้ (รวมถึง C ++ ที่ใหม่กว่าและอื่น ๆ เช่น GO) แต่ถ้าพวกเขามีประสิทธิภาพสูงกว่าแอสเซมเบลอร์ (เช่นรหัสของคุณ) คุณต้องปรึกษา CPU Documentation - เปรียบเทียบกับรหัสเลอะเทอะ down to op-code HLL สรุปรายละเอียดและไม่กำจัดส่วนอื่น ๆ ที่แอพของคุณจะไม่ทำงานหากเป็นที่รู้จักโดยโฮสต์ระบบปฏิบัติการ

รหัสแอสเซมเบลอร์ส่วนใหญ่ (ส่วนใหญ่เป็นวัตถุ) จะถูกเอาท์พุทเป็น "หัวขาด" เพื่อรวมไว้ในรูปแบบที่สามารถใช้งานได้อื่นซึ่งต้องการการประมวลผลที่น้อยกว่าดังนั้นมันจะเร็วกว่ามาก แต่ก็ไม่ปลอดภัย หากไฟล์ประมวลผลนั้นส่งออกโดยแอสเซมเบลอร์ (NAsm, YAsm; ฯลฯ ) มันจะยังคงทำงานได้เร็วขึ้นจนกว่าจะตรงกับรหัส HLL ในการทำงานอย่างสมบูรณ์ผลลัพธ์อาจถูกชั่งน้ำหนักอย่างแม่นยำ

การเรียกใช้ออบเจ็กต์รหัสแอสเซมเบลอร์จาก HLL ในรูปแบบใด ๆ จะเพิ่มการประมวลผลเหนือศีรษะรวมถึงการเรียกใช้พื้นที่หน่วยความจำโดยใช้หน่วยความจำที่จัดสรรไว้ทั่วโลกสำหรับประเภทข้อมูลตัวแปร / ค่าคงที่ (ซึ่งใช้กับ LLL และ HLL) โปรดจำไว้ว่าผลลัพธ์สุดท้ายคือการใช้ CPU ในท้ายที่สุดเป็น api และ abi ที่เกี่ยวข้องกับฮาร์ดแวร์ (opcode) และทั้งสองแอสเซมเบลอร์และ "HLL คอมไพเลอร์" เป็นหลัก / พื้นฐานเหมือนกันกับข้อยกเว้นที่แท้จริงเท่านั้นที่สามารถอ่านได้

สวัสดีแอปพลิเคชันคอนโซลโลกในแอสเซมเบลอร์โดยใช้ FAsm คือ 1.5 KB (และนี่เป็น Windows ที่เล็กกว่าใน FreeBSD และ Linux) และมีประสิทธิภาพเหนือกว่าทุกอย่างที่ GCC สามารถใช้งานได้ในวันที่ดีที่สุด เหตุผลคือ padding โดยนัยกับ nops, การตรวจสอบการเข้าถึงและการตรวจสอบขอบเขตเพื่อตั้งชื่อไม่กี่ เป้าหมายที่แท้จริงคือ clean HLL libs และคอมไพเลอร์ที่ปรับให้เหมาะสมซึ่งกำหนดเป้าหมาย cpu ในลักษณะ "hardcore" และส่วนใหญ่ทำวันนี้ (ในที่สุด) GCC นั้นไม่ดีไปกว่า YAsm -it คือการเข้ารหัสและความเข้าใจของนักพัฒนาที่มีปัญหาและ "การเพิ่มประสิทธิภาพ" เกิดขึ้นหลังจากการสำรวจสามเณรและการฝึกอบรมและประสบการณ์ระหว่างกาล

คอมไพเลอร์ต้องเชื่อมโยงและประกอบสำหรับเอาต์พุตใน opcode เดียวกันกับแอสเซมเบลอร์เพราะรหัสเหล่านั้นเป็นสิ่งที่ CPU จะยกเว้น (CISC หรือ RISC [PIC ด้วย]) YAsm ปรับแต่งและทำความสะอาดเป็นอย่างมากสำหรับ NAsm ยุคแรกในท้ายที่สุดเร่งความเร็วเอาต์พุตทั้งหมดจากแอสเซมเบลอร์นั้น แต่ถึงอย่างนั้น YAsm ก็ยังคงเช่นเดียวกับ NAsm ผลิตไฟล์ปฏิบัติการด้วยการพึ่งพาภายนอกที่กำหนดเป้าหมายไลบรารีระบบปฏิบัติการในนามของนักพัฒนา ในการปิด C ++ นั้นเป็นจุดที่น่าเหลือเชื่อและปลอดภัยกว่าแอสเซมเบลอร์มากกว่า 80 เปอร์เซ็นต์โดยเฉพาะในภาคการค้า ...


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

1
คุณสามารถสร้าง executables แบบคงที่โดยใช้ NASM หรือ YASM (ไม่มีรหัสภายนอก) พวกมันสามารถแสดงผลในรูปแบบไบนารี่แบบแฟลตดังนั้นคุณสามารถรวบรวมเอลฟ์ส่วนหัวได้ด้วยตัวคุณเองถ้าคุณไม่ต้องการเรียกใช้ldแต่มันก็ไม่ต่างอะไรนอกจากคุณจะพยายามปรับขนาดไฟล์ให้เหมาะสม (ไม่ใช่แค่ขนาดของ ส่วนข้อความ) ดูลมกรดสอนในการสร้างจริงๆ teensy ELF Executables สำหรับลินุกซ์
ปีเตอร์กอร์เดส

1
บางทีคุณอาจนึกถึง C # หรือstd::vectorเรียบเรียงในโหมดดีบัก อาร์เรย์ C ++ ไม่เหมือนนั้น คอมไพเลอร์สามารถตรวจสอบสิ่งต่าง ๆ ในเวลาคอมไพล์ แต่ถ้าคุณเปิดใช้งานตัวเลือกการชุบแข็งพิเศษจะไม่มีการตรวจสอบรันไทม์ ดูตัวอย่างฟังก์ชั่นที่เพิ่มขึ้น 1024 องค์ประกอบแรกของint array[]หาเรื่อง เอาต์พุต asm ไม่มีการตรวจสอบรันไทม์: godbolt.org/g/w1HF5t godbolt.org/g/w1HF5tทั้งหมดที่ได้รับคือตัวชี้rdiไม่มีข้อมูลขนาด มันขึ้นอยู่กับโปรแกรมเมอร์ที่จะหลีกเลี่ยงพฤติกรรมที่ไม่ได้กำหนดโดยไม่ต้องเรียกมันด้วยอาร์เรย์ที่เล็กกว่า 1024
Peter Cordes

1
สิ่งที่คุณกำลังพูดถึงไม่ใช่อาร์เรย์ C ++ ธรรมดา (จัดสรรด้วยnewลบด้วยตนเองโดยdeleteไม่มีการตรวจสอบขอบเขต) คุณสามารถใช้ C ++ เพื่อสร้าง asm / machine-code bloated (เช่นซอฟต์แวร์ส่วนใหญ่) แต่นั่นเป็นความผิดของโปรแกรมเมอร์ไม่ใช่ C ++ คุณสามารถใช้allocaเพื่อจัดสรรพื้นที่สแต็กเป็นอาร์เรย์
Peter Cordes

1
เชื่อมโยงตัวอย่างในgcc.godbolt.orgของg++ -O3การสร้างรหัสขอบเขตการตรวจสอบสำหรับอาร์เรย์ธรรมดาหรือทำสิ่งอื่นที่คุณกำลังพูดถึง C ++ ทำให้การสร้างไบนารีที่ป่องๆง่ายขึ้น (และอันที่จริงคุณต้องระวังไม่ให้คุณตั้งใจให้มีประสิทธิภาพ) แต่ก็ไม่สามารถหลีกเลี่ยงได้อย่างแท้จริง หากคุณเข้าใจว่า C ++ คอมไพล์กับ asm ได้อย่างไรคุณจะได้รับโค้ดที่ค่อนข้างแย่กว่าที่คุณเขียนด้วยมือ แต่ด้วยการอินไลน์และการแพร่กระจายคงที่ในระดับที่ใหญ่กว่าที่คุณสามารถจัดการด้วยมือ
Peter Cordes

-3

การประกอบอาจเร็วขึ้นหากคอมไพเลอร์ของคุณสร้างรหัสสนับสนุนOOจำนวนมาก

แก้ไข:

สำหรับ downvoters: OP เขียนว่า "ฉันควร ... เน้นที่ C ++ และลืมภาษาแอสเซมบลีหรือไม่" และฉันยืนตามคำตอบของฉัน คุณต้องคอยจับตาดูโค้ด OO ที่สร้างขึ้นโดยเฉพาะเมื่อใช้วิธีการต่างๆ การไม่ลืมภาษาแอสเซมบลีหมายความว่าคุณจะตรวจสอบแอสเซมบลีรหัส OO ของคุณเป็นระยะซึ่งฉันเชื่อว่าเป็นสิ่งจำเป็นสำหรับการเขียนซอฟต์แวร์ที่มีประสิทธิภาพดี

อันที่จริงเรื่องนี้เกี่ยวข้องกับรหัสที่คอมไพล์ได้ทั้งหมดไม่ใช่แค่ OO


2
-1: ฉันไม่เห็นคุณลักษณะ OO ใด ๆ ที่ใช้อยู่ อาร์กิวเมนต์ของคุณเหมือนกับ "แอสเซมบลีอาจเร็วขึ้นหากคอมไพเลอร์ของคุณเพิ่มล้าน NOPs"
Sjoerd

ฉันไม่ชัดเจนนี่เป็นคำถาม C จริง ๆ หากคุณเขียนรหัส C สำหรับคอมไพเลอร์ C ++ คุณไม่ได้เขียนรหัส C ++ และคุณจะไม่ได้รับ OO เมื่อคุณเริ่มเขียนด้วยภาษา C ++ จริงการใช้สิ่ง OO คุณจะต้องมีความรู้มากในการทำให้คอมไพเลอร์ไม่สร้างรหัสสนับสนุน OO
Olof Forshell

ดังนั้นคำตอบของคุณไม่เกี่ยวกับคำถาม? (นอกจากนี้การชี้แจงไปในคำตอบที่ไม่ได้แสดงความคิดเห็นสามารถลบได้ตลอดเวลาโดยไม่มีการแจ้งเตือนการแจ้งเตือนหรือประวัติศาสตร์
Mooing Duck

1
ไม่แน่ใจว่าคุณหมายถึงอะไรโดย OO "รหัสสนับสนุน" อย่างแน่นอน แน่นอนถ้าคุณใช้ RTTI จำนวนมากและเช่นนั้นคอมไพเลอร์จะต้องสร้างคำสั่งพิเศษมากมายเพื่อรองรับคุณสมบัติเหล่านั้น - แต่ปัญหาใด ๆ ที่ระดับสูงพอที่จะให้สัตยาบันการใช้ RTTI นั้นซับซ้อนเกินกว่าจะเขียนได้อย่างเป็นไปได้ . แน่นอนสิ่งที่คุณสามารถทำได้คือเขียนเฉพาะนามธรรมนอกอินเตอร์เฟสเป็น OO ส่งไปยังโค้ดโพรซีเดอร์บริสุทธิ์ที่ปรับให้เหมาะสมกับประสิทธิภาพซึ่งเป็นสิ่งสำคัญ แต่ขึ้นอยู่กับแอปพลิเคชัน C, Fortran, CUDA หรือเพียงแค่ C ++ ที่ไม่มีการสืบทอดเสมือนอาจจะดีกว่าการประกอบที่นี่
leftaroundabout

2
ไม่อย่างน้อยก็มีโอกาสมาก มีบางสิ่งใน C ++ ที่เรียกว่ากฎศูนย์ค่าใช้จ่ายซึ่งจะใช้เวลาส่วนใหญ่ เรียนรู้เพิ่มเติมเกี่ยวกับ OO - คุณจะพบว่าในท้ายที่สุดแล้วมันจะปรับปรุงความสามารถในการอ่านรหัสของคุณปรับปรุงคุณภาพของรหัสเพิ่มความเร็วในการเข้ารหัสเพิ่มความทนทาน นอกจากนี้สำหรับการฝังตัว - แต่ใช้ C ++ เพื่อให้คุณสามารถควบคุมได้มากขึ้นการฝัง + OO ในแบบของ Java จะทำให้คุณเสียค่าใช้จ่าย
Zane
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.