Từ 1 tới 5 trên tổng số 5 kết quả

Đề tài: Chạy đua với trình biên dịch

  1. #1
    Ngày gia nhập
    08 2011
    Bài viết
    98

    Mặc định Chạy đua với trình biên dịch

    Đây là lần đầu lttq viết bài ở box này.Vì biết ở đây có nhiều cao thủ nên việc chém gió rất dễ lộ.Vì thế những gì mình nắm được thì sẽ trình bày kỹ,cái nào còn lơ mơ thì không dám nói nhiều,chờ cao nhân.

    Có 1 thread nào đó,bác TQN có bảo không nên dùng inline asm,chỉ nên dùng khi cần optimize,không nên chạy đua với trình dịch.Bài này viết ra chỉ để giải trí là chính,vì những project to thì mấy chỗ lặt vặt này chắc cũng chẳng ai ngó.


    C Code:
    1. #include <iostream>
    2. using namespace std;
    3.  
    4. int Tong(int len,int A[]) {
    5.     int i,sum=0;
    6.     for(i=0;i<len;i++) sum+=A[i];
    7.     return sum;
    8. }
    9.  
    10. int main(int argc,char* argv[])
    11. {
    12.     int M[10];
    13.     M[0]=1;
    14.     M[1]=2;
    15.     cout << Tong(2,M);
    16.     system("PAUSE");
    17.     return 0;
    18. }

    Khi disasembly thì tại vị trí của "for(i=0;i<len;i++) sum+=A[i];" nó sẽ như sau :

    C Code:
    1. 00D713BE   MOV DWORD PTR SS:[EBP-14],0 // sum = 0
    2. 00D713C5   MOV DWORD PTR SS:[EBP-8],0  // i = 0
    3. 00D713CC   JMP SHORT loop-asm.00D713D7 // vào loop
    4. 00D713CE   MOV EAX,DWORD PTR SS:[EBP-8] //
    5. 00D713D1   ADD EAX,1                    //  i++
    6. 00D713D4   MOV DWORD PTR SS:[EBP-8],EAX
    7. 00D713D7   MOV EAX,DWORD PTR SS:[EBP-8]
    8. 00D713DA   CMP EAX,DWORD PTR SS:[EBP+8] // while(i < len)
    9. 00D713DD   JGE SHORT loop-asm.00D713F0
    10. 00D713DF   MOV EAX,DWORD PTR SS:[EBP-8]
    11. 00D713E2   MOV ECX,DWORD PTR SS:[EBP+C]
    12. 00D713E5   MOV EDX,DWORD PTR SS:[EBP-14]
    13. 00D713E8   ADD EDX,DWORD PTR DS:[ECX+EAX*4] // sum += A[i]
    14. 00D713EB   MOV DWORD PTR SS:[EBP-14],EDX // sum
    15. 00D713EE   JMP SHORT loop-asm.00D713CE
    16. 00D713F0   MOV EAX,DWORD PTR SS:[EBP-14] //return về eax

    Rõ ràng là ta có thể giảm đi 1 biến i và 1 biến sum cho chỡ chật stack.
    Như sau :

    C Code:
    1. int Tong(int len,int A[]) {
    2.     __asm {
    3.         xor eax, eax // coi eax là sum
    4.         xor edx, edx // coi như edx là i
    5.         mov ecx,dword ptr [A]
    6.         cmp len, 0 // if (len != 0)
    7.         je L2 //mảng trống thì nhảy qua loop
    8.     L1: add eax, dword ptr [ecx+edx*4] // Tong += A[i]
    9.         add edx, 1 // i++
    10.         cmp edx,len
    11.         jb L1 //nhảy khi i>=len
    12.     L2: //return eax
    13.     }
    14. }


    Tiết kiệm:

    Code:
    mov eax, [a]
    imul eax, 6
    mov [b], eax
    mov ebx, [c]
    add ebx, 2
    mov [d], ebx
    có thể bớt ebx đi:

    Code:
    mov eax, [a]
    imul eax, 6
    mov [b], eax
    mov eax, [c]
    add eax, 2
    mov [d], eax
    sử dụng triệt để các thanh ghi thấp hơn(16bit-8bit)

    Code:
    mov eax, [a] 
    imul eax, 6
    mov [b], eax
    mov ax, [c] 
    add ax, 2
    mov [d], ax
    giảm đi 1 thanh ghi :

    biểu thức (A *= 128) và (A <<= 7) đều được biên dịch thành:

    Code:
    mov eax, A 
    shl eax, 7
    mov A, eax
    tối ưu hơn :
    Code:
    __asm shl A, 7
    Giảm lệnh :
    C Code:
    1. int a, b, c, d, e;
    2. e = a*b + a*c;

    Trình biên dịch phát sinh mã hợp ngữ như sau:

    Code:
    mov eax, dword ptr [a] 
    imul eax, dword ptr [b] 
    mov ecx, dword ptr [a] 
    imul ecx, dword ptr [c]
    add eax, ecx  
    mov dword ptr [e], eax
    Tuy nhiên, ta có thể viết rút gọn giảm được 1 phép imul (nhân), 1 phép mov (di
    chuyển, sao chép):

    Code:
    __asm 
    { 
        mov eax, b; 
        add eax, c; 
        imul eax, a; 
        mov e, eax; 
    }
    Từ 1 câu lệnh :
    a = b > c ? d : e;
    VS2010 sinh mã như sau :

    Code:
    00173550  mov         eax,dword ptr [b (177144h)]  
    00173555  cmp         eax,dword ptr [c (177140h)]  
    0017355B  jle         wmain+6Bh (17356Bh)  
    0017355D  mov         ecx,dword ptr [d (17713Ch)]  
    00173563  mov         dword ptr [ebp-0C4h],ecx  
    00173569  jmp         wmain+77h (173577h)  
    0017356B  mov         edx,dword ptr [e (177138h)]  
    00173571  mov         dword ptr [ebp-0C4h],edx  
    00173577  mov         eax,dword ptr [ebp-0C4h]  
    0017357D  mov         dword ptr [a (177148h)],eax
    Ta có thể inline lại gọn hơn
    Code:
    __asm{
    	mov eax, [b]
    	cmp eax, [c]
    	jng L1
    	mov eax, [d]
    	jmp L2
    	L1: mov eax, [e]
    	L2: mov [a], eax
    }
    Optimize hơn:

    Code:
    mov eax, [b]
    cmp eax, [c]
    mov eax, [d]
    cmovng eax, [e]
    mov [a], eax
    Bài viết này lượm lặt ý tưởng từ nhiều nguồn,triển khai lại theo ý tưởng của mình ^^
    Đã được chỉnh sửa lần cuối bởi lttq : 28-08-2011 lúc 10:57 AM.

  2. #2
    Ngày gia nhập
    07 2008
    Nơi ở
    /media/Anime
    Bài viết
    2,288

    Mình thấy cần chỉ phải optimize = tay khổ sở như thế. VS đã hỗ trợ sẵn chế độ build optimize rồi mà. Đây là code của vòng for khi build chế độ optimize small code.

    Code gốc :
    C++ Code:
    1. int Tong(int len,int A[]) {
    2.     int i,sum=0;
    3.     for(i=0;i<len;i++) sum+=A[i];
    4.     return sum;
    5. }

    Code asm được vs optimize :
    C++ Code:
    1. _asm {
    2.    Label1:
    3.        xor  ecx, ecx ; // i = 0;
    4.        add eax,dword ptr A[ecx*4] ; // sum += A[i];
    5.        inc ecx ; // i++;
    6.        cmp ecx,edx ; // if (i < len) goto Label1;
    7.    jl Label1
    8. }

    Biến len sẽ được mov vào edx trước khi vào vòng for, kết quả trả ra thanh ghi eax thay cho biến sum. Như vậy bạn optimize vòng for từ 16 xuống còn 9 dòng. VS optimize từ 16 xuống còn 5 dòng. Tỉ số là 1-0 nghiên về VS.
    Đã được chỉnh sửa lần cuối bởi meoconlongvang : 28-08-2011 lúc 09:01 AM.
    Càng yêu mèo thì mèo càng mập. Mèo càng mập ta lại càng yêu.

  3. #3
    Ngày gia nhập
    08 2011
    Bài viết
    98

    Trích dẫn Nguyên bản được gửi bởi meoconlongvang Xem bài viết
    Mình thấy cần chỉ phải optimize = tay khổ sở như thế. VS đã hỗ trợ sẵn chế độ build optimize rồi mà. Đây là code của vòng for khi build chế độ optimize small code.

    Biến len sẽ được mov vào edx trước khi vào vòng for, kết quả trả ra thanh ghi eax thay cho biến sum. Như vậy bạn optimize vòng for từ 16 xuống còn 9 dòng. VS optimize từ 16 xuống còn 5 dòng. Tỉ số là 1-0 nghiên về VS.
    Ồ vâng.Đúng là code này tối ưu hơn,nhưng đâu phải mọi trình dịch đều làm được thế.Với lại nếu canh chỉnh từng dòng thì chưa chắc VS đã hơn đâu.


    Tiếp tục về vấn đề này :

    Khi khởi tạo 1 hàm,thì prolog của hàm thường bảo lưu khá nhiều,nhiều thanh ghi không dùng đến vẫn cứ bảo lưu làm cho khi dịch ra thêm rườm rà.Với những hàm chỉ dùng với ý đồ đơn giản như in ra... thì có thể viết lại prolog và epilog cho hàm trở nên gọn gàng.Ví dụ sau đây viết lai prolog và epilog của hàm mặc định là __cdecl

    Dùng declspec ( naked ) để lọc prolog và epilog :

    C Code:
    1. #include "stdio.h"
    2. __declspec ( naked ) int f() {
    3.    __asm {      /* prolog */
    4.       push   ebp
    5.       mov      ebp, esp
    6.       sub      esp, __LOCAL_SIZE
    7.       }
    8.    printf("test ok");  
    9.    __asm {   /* epilog */
    10.       mov      esp, ebp
    11.       pop      ebp
    12.       ret
    13.       }
    14. }
    15. int main(){
    16. f();
    17. return 0;
    18. }

    microsoft có cung cấp từ khóa __LOCAL_SIZE để trình dịch tự cấp phát vùng nhớ cho hàm.Ta có thể tự tính toán rồi cấp cho nó cho hợp với từng hàm.Vì nhiều khi compiler cầu toàn nên có thể chừa ra nhiều byte cho hàm quá,không tối ưu.Hơn nữa,đã chạy đua với trình dịch rồi thì cũng nên ... tự tính toán rồi cấp bộ nhớ cho nó luôn cho nó ... máu

    ======

    Tối ưu các instruction :
    Lựa chọn các instruction thông minh cũng góp phần giảm size của ứng dụng
    Code:
    	
                    mov eax, 0 // 5byte
    		sub eax, eax // 2 byte
    		xor eax,eax // 2 byte
    		//
    		mov eax, 3 // 5 byte
    		push 3    //
    		pop eax  // 3 byte
    
    		//
    		mov eax, -1 // 5  byte
    		or eax, -1 // 3 byte
    ... vâng vâng


    Các bạn có cao kiến gì thì cứ đóng góp cho vui.Chế độ build release của VS cũng khá tốt rồi,nên cứ optimize bằng tay từng bước thế này cũng hơi cực
    Đã được chỉnh sửa lần cuối bởi lttq : 30-08-2011 lúc 11:56 AM.
    Nghe Metal và dịch thơ
    lttqstudy.wordpress.com

  4. #4
    Ngày gia nhập
    03 2008
    Bài viết
    71

    He he, đúng là vui là chính, chứ một vòng for mà phải viết code ASM tùm lum vậy thì mất công quá

  5. #5
    Ngày gia nhập
    02 2009
    Bài viết
    23

    Có 3 lý do này bạn nên xem qua:
    1. Nếu bạn sử dụng __asm đồng nghĩa sẽ làm khó hiểu cho đồng nghiệp của bạn, làm giảm time lập trình của bạn, sẽ khó debug hơn.
    2. Tốc độ xử lý của máy tính đang tăng rất nhanh (theo cấp số nhân)
    3. Quan trọng nhất là trình biên dịch đang dần thông minh hơn ( trong tg lai có thể đoạn của bạn sẽ làm chậm chương trình đi cũng nên :P)

    Bạn nên cân nhắc giữa được và mất.

Các đề tài tương tự

  1. Công cụ lập trình C Dev C biên dịch chậm trên Win 7 là do đâu?
    Gửi bởi samchan.ict trong diễn đàn Tài liệu, ebooks và công cụ
    Trả lời: 8
    Bài viết cuối: 16-05-2013, 10:18 AM
  2. không chạy đc file jar sau khi biên dịch
    Gửi bởi allvalk trong diễn đàn Nhập môn lập trình Java
    Trả lời: 1
    Bài viết cuối: 29-10-2012, 01:51 PM
  3. Biên dịch chạy tốt, chạy file .exe trong Debug lỗi
    Gửi bởi conrongchautien trong diễn đàn Thắc mắc lập trình Visual C++
    Trả lời: 2
    Bài viết cuối: 19-04-2012, 11:03 AM
  4. Lỗi khi biên dịch với visual C++ toàn bị chạy last build
    Gửi bởi chuot chui trong diễn đàn Thắc mắc lập trình Visual C++
    Trả lời: 2
    Bài viết cuối: 17-03-2011, 10:09 PM
  5. biên dịch không báo lỗi nhưng khi chạy ct thì bị lỗi, anh/chị nào giúp em với!!
    Gửi bởi bethaophuong2004 trong diễn đàn Nhập môn lập trình C/C++
    Trả lời: 11
    Bài viết cuối: 27-03-2009, 09:42 AM

Quyền hạn của bạn

  • Bạn không thể gửi đề tài mới
  • Bạn không thể gửi bài trả lời
  • Bạn không thể gửi các đính kèm
  • Bạn không thể chỉnh sửa bài viết của bạn