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

Đề tài: Max out memory bandwidth!

  1. #1
    Ngày gia nhập
    10 2007
    Bài viết
    169

    Mặc định Max out memory bandwidth!

    - Nếu bạn phải thường xuyên xử lý một lượng lớn data thì việc này rất quan trọng, nó sẻ giúp chương trình của bạn chạy nhanh hơn mà không phải lo nghĩ nhiều về các thuật toán!

    1.Align data:
    - Nếu bạn dùng máy tính 32 bits, bạn phải truyền data như thế nào để tận dụng được tối đa băng thông bộ nhớ?! Bạn cần phải truyền một lúc 32bits data = 4 bytes! Nhưng nếu data của bạn không đủ 4 bytes thì sao? Lúc này bạn cần align data theo 32bits!

    - Giả sử bạn có một struct như sau:
    C++ Code:
    1. struct SColor24
    2. {
    3. unsigned char Blue,Green,Red;
    4. };
    5.  
    6. SColor24* pBuffer = new SColor24[800*600];
    -Bây giờ bạn muốn set toàn bộ phần memory đó với một giá trị nào đó! Bạn có thể làm như sau:
    C++ Code:
    1. // Cách đơn giản nhất
    2. for(UINT i = 800*600;i;--i) pBuffer[i] = <value>;
    3.  
    4. // Hay
    5. for(SColor24* pBufferPtr = pBuffer[800*600];pBufferPtr > pBuffer;--pBufferPtr)
    6. {
    7. *pBufferPtr = <value>
    8. }
    - Nhưng nếu dùng hai cách như trên, bạn chỉ có thể tận dụng được 1/4 -> 1/3 bandwidth của bộ nhớ! Vì trong code sẻ sinh ra các mã trung gian nhằm gán giá trị unalign vào vùng nhớ (ở đây là 24bits = 3 bytes, trong khi máy bạn dùng 32 bits)
    - Bạn nghĩ có thể dùng memset ở đây không?! memset rõ ràng chạy nhanh hơn nhưng nó chỉ cho bạn set giá trị 1 char = 1 byte, trong khi ở đây bạn muốn set 3 bytes!
    - Do đó nếu bạn muốn có đc tốc độ tối đa, bạn phải align vùng data đó theo 32 bits, để cho mỗi lần truyền, nó sẻ truyền đúng 32 bits, và sẻ không có việc chuyển đỗi giá trị trung gian, gây chậm việc xử lý!

    - Việc bạn cần làm chỉ đơn giản là báo cho compiler biết struct của bạn được align bao nhiêu!
    C++ Code:
    1. [B]__declspec(align(4) )[/B] struct SColor24
    2. {
    3. unsigned char Blue,Green,Red;
    4. };
    5.  
    6. SColor24* pBuffer = new SColor24[800*600];
    - Nghĩa là bạn chỉ cần thêm dòng "__declspec(align(4))" là được! Số 4 ở đây là 4 bytes, tức là bạn muốn align theo 8*4 = 32 bits!
    - Với cách này, compiler sẻ ngầm thêm 1 byte vào trong struct của bạn và biến nó thành struct có 4 bytes data! Nhưng nói như vậy cũng có nghĩa là struct của bạn lúc này sẻ tương đương với:
    C++ Code:
    1. struct SColor32
    2. {
    3. unsigned char Blue,Green,Red,Alpha;
    4. };
    - Nếu vậy thì thêm align làm gì, add thêm 1 byte nủa là đc rồi ...! Cũng không hẳn như vậy, việc khai báo các data bên trong struct đôi khi bị các compiler sửa đổi khiến cho nó không có size như bạn mong muốn, chẳng hạn như khi trong struct có nhiều kiểu data (char, WORD,DWORD,...). Việc dùng align giúp bạn tránh được việc đó và cũng tránh khai báo các dữ liệu dư thừa!
    - Vậy là xong, bây giờ bạn thử lại xem có tí gì cải thiện về tốc độ không! Test trên máy của mình với size = 800*600, speed tăng 2 lần mả chỉ cần làm việc đơn giản là thêm "__declspec(align(4))" vào trước struct!

    2.Sử dụng các hàm intrinsic và ASM:
    - Intrinsic cũng là các hàm như các hàm bình thường thôi! Nhưng nó thấp hơn và gần với ASM hơn! Trên các máy 32 bits, bạn có thể dùng song song intrinsic và ASM, nhưng thường thì người ta dùng ASM! Nếu khả năng viết code ASM của bạn không tốt lắm thì cứ dùng các hàm intrinsic có sẳn sẻ tốt hơn! Đều quan trong bạn cần chú ý là một số intrinsic chỉ chạy trên platform xác định như 32 hoặc 64 bits, số khác lại chạy được trên 31 lẩn 64 bits! Dùng sai thì chương trình của bạn sẻ không chạy được trên platform khác!

    3.Cần quan tâm đến data type nhiều hơn:
    - khi viết code hai khai báo các data, người ta thường nghĩ tới việc tối ưu resource nhiều hơn là đến hiệu năng của nó! Thời của CPU 8 bits, 16 bits đả qua rồi, bạn đừng nên tiết kiệm resource một cách quá đáng mà làm ảnh hưởng đến hiệu xuất của chương trình!
    - Với hầu hết các máy 32 bits và các thế hệ sau này, việc dùng (hay support) BYTE (8 bits) và WORD (16 bits) càng giảm dần! Nghĩa là việc xử lý trên BYTE và WORD sẻ chậm hơn trên DWORD hay QWORD (hiện tại dùng WORD thì vẩn còn dùng được, nhưng dùng BYTE thì rõ ràng là chậm hơn)! Vì vậy nếu data của bạn có dùng đến BYTE, hãy nghĩ cách gôm chung 4 bytes lại thành 1 DWORD và xử lý cái DWORD đó! VD:
    C++ Code:
    1. DWORD Vl1,Vl2;
    2. DWORD Cl = ((*(DWORD*)&Vl >> 1) & 0xFF7F7F7F) + ((*(DWORD*)&Vl2 >> 1) & 0xFF7F7F7F);
    3.  
    4. // Tương đương
    5. BYTE Vl1,Vl2,Vl3,Vl4;
    6. BYTE Vl5,Vl6,Vl7,Vl8;
    7.  
    8. BYTE Cl1 = (Vl1 + Vl5) >> 1;
    9. BYTE Cl1 = (Vl2 + Vl6) >> 1;
    10. BYTE Cl1 = (Vl3 + Vl7) >> 1;
    11. BYTE Cl1 = (Vl4 + Vl8) >> 1;
    - Trên các chip 128 và 256 bits hiện nay, người ta chỉ còn support dạng DWORD mà thôi, nếu dùng đến WORD hay BYTE thì buộc nó phải cast nguyên phần data đó ra DWORD rồi mới xử lý, xử lý xong lại phải cast thành WORD hay BYTE, rồi lại ghi vào memory...!

    - Đối với việc xử lý nhiều data cùng một lúc, bạn có thể dùng đến SIMD, nó cũng là một dạng intrinsic! Việc transfer memory bằng các multimedia register (mmx,xmm,...) chỉ có hiệu quả tốt với vùng memory nhỏ (với máy mình lả <= 640*480*4 bytes)! Nghĩa là khi vùng memory của bạn có size không quá lớn và được align theo 16 bytes của SIMD, cộng với dùng các intrinsic align của SIMD thì tốc độ transfer memory tăng lên rất đáng kể! VD với đoạn code như sau:
    C++ Code:
    1. __m128i BkColorS = _mm_set_epi32(BkColor,BkColor,BkColor,BkColor);
    2. __m128i* pBitsEndPtrS = (__m128i*)&pBits[ArraySize];
    3. for(__m128i* pBitsPtrS = (__m128i*)pBits;pBitsPtrS < pBitsEndPtrS;++pBitsPtrS) _mm_store_si128(pBitsPtrS,BkColorS);
    - Với size = 320*240*4, speed của chương trình tăng lên 3 lần! nhưng khi size = 640*480*4 thì speed tương đương cách transfer bình thường!

    4.Nếu không thể dùng align data trong ứng dụng của bạn?
    - Vì việc align data sẻ làm tăng thêm một số byte trong struct của bạn để nó bằng với lượng mà bạn muốn align! Nếu struct của bạn cần chính xác về vùng dữ liệu thì sao?! Chẳng hạn bạn có một ảnh bitmap 24 bits, và bạn chứa nó trong một vùng memory như sau:
    C++ Code:
    1. struct SColor24
    2. {
    3. unsinged char Blue,Green,Red;
    4. };
    5.  
    6. SColor24* pImage;
    - Việc align struct này sẻ hủy hoại image của bạn! vì một pixel ảnh của bạn chỉ có 3 bytes = 24 bits, trong khi nếu bạn align struct này thì lại vô tình làm cho mỗi pixel = 4 bytes!
    - Vậy phải làm sao? Chúng ta phải chuyển phần memory đó về dạng 32 bits! Với VD cụ thể ở đây chúng ta có vùng memory là các struct 3 bytes, hay mỗi pixel ở đây là 3 bytes! Mà 4*3 bytes = 3*4 bytes, hay 4 pixel = 3 DWORD! Vậy tóm lại khi xử lý, bạn làm cách nào đó để chuyển 4 pixel đó về 3 DWORD và xử lý là được! Tùy theo nhu cầu mà bạn chuyển thôi, không có cách chuyển cụ thể nào ở đây cả...!
    - Với VD về set phần data như ở trên, speed của chương trình mình test tăng lên 2.5 -> 3 lần!

    5.Ứng dụng 64bits:
    - Ai cũng nói là ứng dụng 64 bít chạy nhanh hơn 32 bits, nhưng nó nhanh hơn ra sao?! Với các application ko dùng nhiều đến tính toán hay transfer memory thì 64 hay 32 bits cứ y hệt như nhau, ko hề nhanh hơn!
    - Về transfer memory, mình chỉ thấy nó tăng đc 50% về tốc độ (chỉ tăng nếu bạn transfer 64 bits = 8 bytes một lúc)!
    - Xử lý, tính toán thì tăng ~ 2 lần!


    - Tóm lại là khi các bạn thao tác trên memory thì hãy nghĩ ra mọi cách để tận dụng tối đa băng thông bộ nhớ, nếu không chỉ phí...điện mà còn phụ lòng mong mỏi của hardware !

  2. #2
    Ngày gia nhập
    10 2006
    Nơi ở
    In Your Bugs
    Bài viết
    823

    Thật là 1 bài viết hay và giá trị.

  3. #3
    Ngày gia nhập
    09 2006
    Bài viết
    711

    memset trong thư viện RTL của các compiler hiện tại không set theo từng byte, mà set theo từng dword, phần dư còn lại mới set từng word, từng byte.

  4. #4
    Ngày gia nhập
    10 2007
    Bài viết
    169

    - VD bạn muốn dùng hàm có sẳn thì có thể dùng Intrinsic __movsb, __movsd, __movsq, __movsw! Nó có một list các intrinsic theo compiler trong MSDN, với các compiler khác thì mình không rỏ nhưng đây là các tập lệnh của CPU nên các compiler sẻ có (tên có thể khác chút ít thôi)
    C++ Code:
    1. void __movsq(
    2.    unsigned char* Dest,
    3.    unsigned char* Source,
    4.    size_t Count
    5. );
    - Hơn nửa khi mình test thì các hàm memset hay memcpy,... không thể nhanh hơn các intrinsic khác và chậm hơn cả dùng align (align data và Intrinsic nhanh hơn khoảng 40 -> 50%, chẳng hiểu tại sao nửa T_T)!

    - Với memset, đây cũng là một intrinsic khác nhưng cao hơn mấy cái kia!
    C++ Code:
    1. void *memset(
    2.    void *dest,
    3.    int c,
    4.    size_t count
    5. );
    - Khi dùng, nó sẻ bắt bạn truyền vào 1 value với kiểu int để set! Với value đó, nó cũng parse ra và lấy a byte cuối của value, sau đó gom 4 byte đó vào 1 DWORD, rồi sét nguyên DWORD. Phần dư thì set theo BYTE như TQN nói!

    - Có thêm một ý nửa là, viết ứng dụng 64bits thì không dùng đc inline ASM nửa mà chỉ dùng đc intrinsic!

    - Nếu bạn dùng x64 thì có thể dùng "_mm_stream_si64x" để set, đây làm thứ nhanh nhất mà mình tìm đc trong các tập lệnh!

  5. #5
    Ngày gia nhập
    07 2007
    Nơi ở
    TP.HCM
    Bài viết
    199

    Nó báo lỗi không tìm thấy từ align bạn ơi.

    Visual C++ Code:
    1. __declspec(align(4))struct SColor24
    2. {
    3.     char Blue;
    4.     char Green;
    5.     char Red;
    6. };

  6. #6
    Ngày gia nhập
    10 2007
    Bài viết
    169

    Mặc định Max out memory bandwidth!

    Nó báo lỗi không tìm thấy từ align bạn ơi.

    __declspec(align(4))struct SColor24
    {
    char Blue;
    char Green;
    char Red;
    };
    - O_O mình bị...yếu tim, đừng hù mình tội nghiệp...!!!

    C++ Code:
    1. __declspec(align(4)) struct SColor24
    2. {
    3. char Blue;
    4. char Green;
    5. char Red;
    6. };

    - À, quên mất! Với hàm memset, dù bạn truyền vào là 1 DWORD hay int thì khi set, nó sẻ set toàn bộ vùng memory về byte đấu tiên của DWORD hay int đó! Hay nói cách khác là tất cả các byte của vùng nhớ đếu có cùng giá trị! Còn mình nói set theo DWORD nghĩa là từng byte đc set của DWORD đó khác nhau, kết quả là mỗi DWORD trên cùng nhớ mới giống nhau chứ không phải mỗi byte như hàm memset!
    Đã được chỉnh sửa lần cuối bởi RadicalLight : 14-02-2009 lúc 10:17 PM. Lý do: Update!

  7. #7
    Ngày gia nhập
    03 2009
    Bài viết
    10

    em đang cần tham khảo, cảm ơn các bác

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

  1. Ebook: Memory in C/C++
    Gửi bởi wolverine trong diễn đàn Tài liệu, ebooks và công cụ
    Trả lời: 6
    Bài viết cuối: 01-02-2015, 06:53 PM
  2. Trả lời: 0
    Bài viết cuối: 01-03-2012, 10:41 PM
  3. Memory Management in C++
    Gửi bởi haian trong diễn đàn Giới thiệu website, sản phẩm của bạn
    Trả lời: 1
    Bài viết cuối: 27-02-2010, 09:58 AM
  4. memory leak Lỗi này có hay dẫn đến đơ chương trình
    Gửi bởi trần trân trong diễn đàn Thắc mắc lập trình Visual C++
    Trả lời: 1
    Bài viết cuối: 09-12-2009, 10:01 AM
  5. Memory tracker
    Gửi bởi NamVoDang trong diễn đàn Tutorials và Thủ thuật Visual C++
    Trả lời: 0
    Bài viết cuối: 27-08-2009, 09:55 PM

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