Có gợi ý trình biên dịch nào cho GCC để buộc dự đoán rẽ nhánh luôn đi theo một cách nhất định không?


118

Đối với kiến ​​trúc Intel, có cách nào để hướng dẫn trình biên dịch GCC tạo mã luôn buộc dự đoán nhánh theo một cách cụ thể trong mã của tôi không? Phần cứng Intel có hỗ trợ điều này không? Điều gì về các trình biên dịch hoặc phần cứng khác?

Tôi sẽ sử dụng điều này trong mã C ++, nơi tôi biết trường hợp tôi muốn chạy nhanh và không quan tâm đến việc chạy chậm lại khi nhánh khác cần được thực hiện ngay cả khi nó gần đây đã sử dụng nhánh đó.

for (;;) {
  if (normal) { // How to tell compiler to always branch predict true value?
    doSomethingNormal();
  } else {
    exceptionalCase();
  }
}

Như một câu hỏi tiếp theo dành cho Evdzhan Mustafa, liệu gợi ý có thể chỉ định một gợi ý cho lần đầu tiên bộ xử lý gặp lệnh, tất cả dự đoán nhánh tiếp theo, hoạt động bình thường không?


cũng có thể ném một ngoại lệ nếu bất kỳ điều gì trở nên bất thường (không phụ thuộc vào trình biên dịch)
Shep

Câu trả lời:


9

Kể từ C ++ 20, các thuộc tính có thể xảy ra và không thể xảy ra nên được chuẩn hóa và đã được hỗ trợ trong g ++ 9 . Vì vậy, như đã thảo luận ở đây , bạn có thể viết

if (a>b) {
  /* code you expect to run often */
  [[likely]] /* last statement */
}

Ví dụ: trong đoạn mã sau, khối else được nội tuyến nhờ vào [[unlikely]]khối if

int oftendone( int a, int b );
int rarelydone( int a, int b );
int finaltrafo( int );

int divides( int number, int prime ) {
  int almostreturnvalue;
  if ( ( number % prime ) == 0 ) {
    auto k                         = rarelydone( number, prime );
    auto l                         = rarelydone( number, k );
    [[unlikely]] almostreturnvalue = rarelydone( k, l );
  } else {
    auto a            = oftendone( number, prime );
    almostreturnvalue = oftendone( a, a );
  }
  return finaltrafo( almostreturnvalue );
}

liên kết chốt thần so sánh sự hiện diện / vắng mặt của thuộc tính


Tại sao sử dụng [[unlikely]]trong ifso với [[likely]]trong else?
WilliamKF

không có lý do gì, chỉ cuối cùng lại ở trong chòm sao này sau khi thử xung quanh nơi thuộc tính cần đến.
pseyfert

Tuyệt đấy. Quá tệ là phương pháp này không thể áp dụng cho các phiên bản C ++ cũ hơn.
Maxim Egorushkin

Liên kết chốt chặn tuyệt vời
Lewis Kelsey

87

GCC hỗ trợ chức năng __builtin_expect(long exp, long c)cung cấp loại tính năng này. Bạn có thể kiểm tra tài liệu tại đây .

Trong trường hợp explà điều kiện sử dụng và clà giá trị kỳ vọng. Ví dụ trong trường hợp bạn muốn

if (__builtin_expect(normal, 1))

Do cú pháp khó hiểu, điều này thường được sử dụng bằng cách xác định hai macro tùy chỉnh như

#define likely(x)    __builtin_expect (!!(x), 1)
#define unlikely(x)  __builtin_expect (!!(x), 0)

chỉ để giảm bớt nhiệm vụ.

Nhớ rằng:

  1. cái này không chuẩn
  2. một trình dự đoán nhánh của trình biên dịch / cpu có thể có nhiều kỹ năng hơn bạn trong việc quyết định những điều như vậy, vì vậy đây có thể là một sự tối ưu hóa vi mô sớm

3
Có lý do gì mà bạn hiển thị macro chứ không phải constexprhàm không?
Columbo

22
@Columbo: Tôi không nghĩ rằng một constexprhàm có thể thay thế macro này. ifTôi tin nó phải có trong tuyên bố trực tiếp. Cùng một lý do assertkhông bao giờ có thể là một constexprchức năng.
Vịt kêu

1
@MooingDuck Tôi đồng ý, mặc dù có nhiều lý do hơn để khẳng định .
Shafik Yaghmour

7
@Columbo một lý do để sử dụng macro là vì đây là một trong số ít các vị trí trong C hoặc C ++ nơi macro đúng về mặt ngữ nghĩa hơn là một hàm. Hàm chỉ hoạt động vì tối ưu hóa (nó một tối ưu hóa: constexprchỉ nói về ngữ nghĩa giá trị, không phải là nội tuyến của lắp ráp cụ thể của triển khai); giải thích đơn giản (không có nội dòng) của mã là vô nghĩa. Không có lý do gì để sử dụng một hàm cho việc này.
Leushenko

2
@Leushenko Hãy coi rằng __builtin_expectbản thân nó là một gợi ý tối ưu hóa, vì vậy lập luận rằng một phương pháp đơn giản hóa việc sử dụng nó phụ thuộc vào tối ưu hóa là ... không thuyết phục. Ngoài ra, tôi đã không thêm thông số constexprđể làm cho nó hoạt động ngay từ đầu, nhưng để làm cho nó hoạt động trong các biểu thức không đổi. Và có, có những lý do để sử dụng một hàm. Ví dụ, tôi sẽ không muốn làm ô nhiễm toàn bộ không gian tên của mình bằng một cái tên nhỏ dễ thương chẳng hạn likely. Tôi sẽ phải sử dụng ví dụ LIKELY, để nhấn mạnh rằng nó là một macro và tránh va chạm, nhưng điều đó đơn giản là xấu xí.
Columbo

46

gcc có __builtin_expect dài (kinh nghiệm dài, c dài) ( tôi nhấn mạnh ):

Bạn có thể sử dụng __builtin_expect để cung cấp cho trình biên dịch thông tin dự đoán nhánh. Nói chung, bạn nên sử dụng phản hồi hồ sơ thực tế cho điều này (-fprofile-arcs), vì các lập trình viên nổi tiếng là tệ trong việc dự đoán chương trình của họ thực sự hoạt động như thế nào . Tuy nhiên, có những ứng dụng khó thu thập dữ liệu này.

Giá trị trả về là giá trị của exp, phải là một biểu thức tích phân. Ngữ nghĩa của tích hợp được mong đợi là exp == c. Ví dụ:

if (__builtin_expect (x, 0))
   foo ();

chỉ ra rằng chúng ta không mong đợi gọi foo, vì chúng ta mong đợi x bằng 0. Vì bạn bị giới hạn trong các biểu thức tích phân cho exp, bạn nên sử dụng các cấu trúc như

if (__builtin_expect (ptr != NULL, 1))
   foo (*ptr);

khi kiểm tra giá trị con trỏ hoặc dấu phẩy động.

Như tài liệu lưu ý, bạn nên thích sử dụng phản hồi hồ sơ thực tế hơn và bài viết này cho thấy một ví dụ thực tế về điều này và cách nó trong trường hợp của họ ít nhất kết thúc là một sự cải tiến so với việc sử dụng __builtin_expect. Ngoài ra, hãy xem Cách sử dụng tối ưu hóa có hướng dẫn hồ sơ trong g ++? .

Chúng tôi cũng có thể tìm thấy một bài báo dành cho người mới nhân Linux về các macro kernal có khả năng () và không thể () sử dụng tính năng này:

#define likely(x)       __builtin_expect(!!(x), 1)
#define unlikely(x)     __builtin_expect(!!(x), 0)

Lưu ý việc !!sử dụng trong macro, chúng ta có thể tìm thấy lời giải thích cho điều này trong Tại sao sử dụng !! (điều kiện) thay vì (điều kiện)? .

Chỉ vì kỹ thuật này được sử dụng trong nhân Linux không có nghĩa là sử dụng nó lúc nào cũng hợp lý. Chúng ta có thể thấy từ câu hỏi này mà gần đây tôi đã trả lời sự khác biệt giữa hiệu suất hàm khi truyền tham số là hằng số thời gian biên dịch hoặc biến mà nhiều kỹ thuật tối ưu hóa cuộn thủ công không hoạt động trong trường hợp chung. Chúng ta cần lập hồ sơ mã cẩn thận để hiểu liệu một kỹ thuật có hiệu quả hay không. Nhiều kỹ thuật cũ thậm chí có thể không phù hợp với tối ưu hóa trình biên dịch hiện đại.

Lưu ý, mặc dù nội trang không di động nhưng cũng hỗ trợ __builtin_expect .

Ngoài ra trên một số kiến trúc, nó có thể không tạo ra sự khác biệt .


Những gì đủ tốt cho nhân Linux thì không đủ cho C ++ 11.
Maxim Egorushkin

@MaximEgorushkin lưu ý, tôi không thực sự khuyên bạn nên sử dụng nó, trên thực tế, tài liệu gcc mà tôi trích dẫn là trích dẫn đầu tiên của tôi thậm chí không sử dụng kỹ thuật đó. Tôi muốn nói rằng lực đẩy chính của câu trả lời của tôi là cân nhắc các lựa chọn thay thế một cách cẩn thận trước khi đi theo con đường này.
Shafik Yaghmour

44

Không có. (Ít nhất là trên bộ xử lý x86 hiện đại.)

__builtin_expectđược đề cập trong các câu trả lời khác ảnh hưởng đến cách gcc sắp xếp mã lắp ráp. Nó không ảnh hưởng trực tiếp đến dự đoán nhánh của CPU. Tất nhiên, sẽ có những ảnh hưởng gián tiếp đến dự đoán rẽ nhánh do sắp xếp lại mã. Nhưng trên các bộ vi xử lý x86 hiện đại, không có lệnh nào cho CPU biết "giả sử nhánh này được / không được sử dụng".

Xem câu hỏi này để biết thêm chi tiết: Dự đoán nhánh tiền tố Intel x86 0x2E / 0x3E thực sự được sử dụng?

Nói rõ hơn, __builtin_expectvà / hoặc việc sử dụng -fprofile-arcs có thể cải thiện hiệu suất mã của bạn, bằng cách đưa ra gợi ý cho bộ dự đoán nhánh thông qua bố cục mã (xem Tối ưu hóa hiệu suất của lắp ráp x86-64 - Căn chỉnh và dự đoán nhánh ), đồng thời cũng cải thiện hành vi của bộ đệm bằng cách giữ mã "không chắc" tránh xa mã "có thể xảy ra".


9
Điều này là không đúng. Trên tất cả các phiên bản hiện đại của x86, thuật toán dự đoán mặc định là dự đoán rằng các nhánh chuyển tiếp không được thực hiện và các nhánh lùi là (xem phần mềm.intel.com/en-us/articles/… ). Vì vậy, bằng cách sắp xếp lại mã của bạn, bạn có thể đưa ra gợi ý hiệu quả cho CPU. Đây chính xác là những gì GCC thực hiện khi bạn sử dụng __builtin_expect.
Nemo

6
@Nemo, bạn đã đọc qua câu đầu tiên của câu trả lời của tôi chưa? Mọi thứ bạn đã nói đều được bao hàm bởi câu trả lời của tôi hoặc trong các liên kết được đưa ra. Câu hỏi được hỏi liệu bạn có thể "buộc dự đoán nhánh luôn đi theo một cách nhất định", mà câu trả lời là "không", và tôi không cảm thấy các câu trả lời khác đủ rõ ràng về điều này.
Artelius

4
OK, đáng lẽ tôi nên đọc kỹ hơn. Đối với tôi câu trả lời này có vẻ đúng về mặt kỹ thuật, nhưng vô dụng, vì người hỏi rõ ràng đang tìm kiếm __builtin_expect. Vì vậy, đây chỉ nên là một bình luận. Nhưng nó không phải là sai, vì vậy tôi đã gỡ bỏ phiếu phản đối của mình.
Nemo

IMO nó không vô ích; đó là một giải thích hữu ích về cách CPU và trình biên dịch thực sự hoạt động, có thể liên quan đến phân tích hiệu suất có / không có các tùy chọn này. Ví dụ: bạn thường không thể sử dụng __builtin_expectđể tạo một trường hợp thử nghiệm mà bạn có thể đo lường bằng cách perf statnày sẽ có tỷ lệ dự đoán sai nhánh rất cao. Nó chỉ ảnh hưởng đến bố cục chi nhánh . Và BTW, Intel kể từ Sandybridge hoặc ít nhất là Haswell không sử dụng dự đoán tĩnh nhiều / chút nào; luôn có một số dự đoán trong BHT, cho dù đó là một bí danh cũ hay không. xania.org/201602/bpu-part-two
Peter Cordes

24

Cách chính xác để xác định các macro có khả năng xảy ra / không chắc chắn trong C ++ 11 là như sau:

#define LIKELY(condition) __builtin_expect(static_cast<bool>(condition), 1)
#define UNLIKELY(condition) __builtin_expect(static_cast<bool>(condition), 0)

Phương pháp này tương thích với tất cả các phiên bản C ++, không giống như [[likely]], nhưng dựa vào phần mở rộng không chuẩn __builtin_expect.


Khi các macro này xác định theo cách này:

#define LIKELY(condition) __builtin_expect(!!(condition), 1)

Điều đó có thể thay đổi ý nghĩa của các ifcâu lệnh và phá vỡ mã. Hãy xem xét đoạn mã sau:

#include <iostream>

struct A
{
    explicit operator bool() const { return true; }
    operator int() const { return 0; }
};

#define LIKELY(condition) __builtin_expect((condition), 1)

int main() {
    A a;
    if(a)
        std::cout << "if(a) is true\n";
    if(LIKELY(a))
        std::cout << "if(LIKELY(a)) is true\n";
    else
        std::cout << "if(LIKELY(a)) is false\n";
}

Và đầu ra của nó:

if(a) is true
if(LIKELY(a)) is false

Như bạn có thể thấy, định nghĩa của LIKELY sử dụng !!như một diễn viên để boolphá vỡ ngữ nghĩa của if.

Vấn đề ở đây không phải là điều đó operator int()operator bool()nên có liên quan. Đó là thực hành tốt.

Thay vì sử dụng !!(x)thay vì static_cast<bool>(x)làm mất ngữ cảnh cho các chuyển đổi theo ngữ cảnh C ++ 11 .


Lưu ý rằng chuyển đổi theo ngữ cảnh đã xuất hiện thông qua một khiếm khuyết vào năm 2012 và thậm chí vào cuối năm 2014 vẫn có sự phân kỳ triển khai. Trên thực tế, có vẻ như trường hợp tôi đã liên kết vẫn không hoạt động cho gcc.
Shafik Yaghmour

@ShafikYaghmour Đó là một quan sát thú vị liên quan đến chuyển đổi theo ngữ cảnh có liên quan switch, cảm ơn. Chuyển đổi theo ngữ cảnh có liên quan ở đây là một phần để nhập boolvà năm ngữ cảnh cụ thể được liệt kê ở đó , không bao gồm switchngữ cảnh.
Maxim Egorushkin

Điều này chỉ ảnh hưởng đến C ++, phải không? Vì vậy, không có lý do gì để đi và thay đổi các dự án C hiện có để sử dụng (_Bool)(condition), vì C không có tính năng nạp chồng toán tử.
Peter Cordes

2
Trong ví dụ của bạn, bạn đã sử dụng just (condition), not !!(condition). Cả hai đều truesau khi thay đổi điều đó (được thử nghiệm với g ++ 7.1). Bạn có thể xây dựng một ví dụ thực sự chứng minh vấn đề bạn đang nói đến khi bạn sử dụng !!booleanize không?
Peter Cordes

3
Như Peter Cordes đã chỉ ra, bạn nói "Khi các macro này [được] định nghĩa theo cách này:" và sau đó hiển thị macro bằng cách sử dụng '!!', "có thể thay đổi ý nghĩa của câu lệnh if và phá vỡ mã. Hãy xem xét đoạn mã sau:" ... và sau đó bạn hiển thị mã không sử dụng '!!' ở tất cả - đã được biết là đã bị phá vỡ ngay cả trước C ++ 11. Vui lòng thay đổi câu trả lời để hiển thị một ví dụ mà macro đã cho (sử dụng !!) bị sai.
Carlo Wood

18

Vì các câu trả lời khác đều đã được gợi ý đầy đủ, bạn có thể sử dụng __builtin_expectđể cung cấp cho trình biên dịch một gợi ý về cách sắp xếp mã hợp ngữ. Như các tài liệu chính thức đã chỉ ra, trong hầu hết các trường hợp, trình lắp ráp được tích hợp trong bộ não của bạn sẽ không tốt bằng trình lắp ráp do nhóm GCC chế tạo. Tốt nhất bạn nên sử dụng dữ liệu hồ sơ thực tế để tối ưu hóa mã của bạn, thay vì phỏng đoán.

Dọc theo các dòng tương tự, nhưng chưa được đề cập, là một cách cụ thể của GCC để buộc trình biên dịch tạo mã trên một đường dẫn "lạnh". Điều này liên quan đến việc sử dụng các thuộc tính noinlinecold, làm chính xác những gì chúng nghe giống như chúng làm. Các thuộc tính này chỉ có thể được áp dụng cho các hàm, nhưng với C ++ 11, bạn có thể khai báo các hàm lambda nội tuyến và hai thuộc tính này cũng có thể được áp dụng cho các hàm lambda.

Mặc dù điều này vẫn thuộc danh mục chung của tối ưu hóa vi mô, và do đó, lời khuyên tiêu chuẩn được áp dụng — hãy thử nghiệm đừng đoán — tôi cảm thấy nó thường hữu ích hơn __builtin_expect. Hầu như không có bất kỳ thế hệ nào của bộ xử lý x86 sử dụng gợi ý dự đoán nhánh ( tham khảo ), vì vậy điều duy nhất bạn có thể ảnh hưởng đến dù sao là thứ tự của mã lắp ráp. Vì bạn biết mã xử lý lỗi hoặc mã "trường hợp cạnh" là gì, bạn có thể sử dụng chú thích này để đảm bảo rằng trình biên dịch sẽ không bao giờ dự đoán một nhánh với nó và sẽ liên kết nó với mã "nóng" khi tối ưu hóa kích thước.

Sử dụng mẫu:

void FooTheBar(void* pFoo)
{
    if (pFoo == nullptr)
    {
        // Oh no! A null pointer is an error, but maybe this is a public-facing
        // function, so we have to be prepared for anything. Yet, we don't want
        // the error-handling code to fill up the instruction cache, so we will
        // force it out-of-line and onto a "cold" path.
        [&]() __attribute__((noinline,cold)) {
            HandleError(...);
        }();
    }

    // Do normal stuff
    
}

Thậm chí tốt hơn, GCC sẽ tự động bỏ qua điều này để hỗ trợ phản hồi hồ sơ khi nó có sẵn (ví dụ: khi biên dịch với -fprofile-use ).

Xem tài liệu chính thức tại đây: https://gcc.gnu.org/onlineocs/gcc/Common-Function-Attributes.html#Common-Function-Attributes


2
Các tiền tố gợi ý dự đoán nhánh bị bỏ qua vì chúng không cần thiết; bạn có thể đạt được hiệu quả tương tự chỉ bằng cách sắp xếp lại mã của mình. (Thuật toán dự đoán nhánh mặc định là đoán rằng các nhánh lùi được thực hiện và các nhánh chuyển tiếp thì không.) Vì vậy, trên thực tế, bạn có thể cung cấp cho CPU một gợi ý, và đây là những gì __builtin_expectthực hiện. Nó hoàn toàn không vô dụng. Bạn đúng rằng coldthuộc tính cũng hữu ích, nhưng bạn đánh giá thấp tiện ích của __builtin_expecttôi.
Nemo

Các CPU Intel hiện đại không sử dụng dự đoán nhánh tĩnh. Thuật toán bạn mô tả, @Nemo, trong đó các nhánh ngược được dự đoán là lấy và các nhánh chuyển tiếp được dự đoán là không được lấy đã được sử dụng trong các bộ xử lý trước đó và lên thông qua Pentium M hoặc tương tự, nhưng các thiết kế hiện đại về cơ bản chỉ đoán ngẫu nhiên, lập chỉ mục vào nhánh của chúng các bảng tại nơi nó mong đợi để tìm thông tin trên nhánh đó và sử dụng bất kỳ thông tin nào ở đó (mặc dù về cơ bản nó có thể là rác). Vì vậy, các gợi ý dự đoán nhánh về mặt lý thuyết sẽ hữu ích, nhưng có lẽ không hữu ích trong thực tế, đó là lý do tại sao Intel loại bỏ chúng.
Cody Grey

Nói rõ hơn, việc triển khai dự đoán rẽ nhánh là cực kỳ phức tạp và các hạn chế về không gian trong các bình luận buộc tôi phải đơn giản hóa quá mức. Đây thực sự sẽ là một câu trả lời toàn bộ. Có thể vẫn còn dấu tích của dự đoán nhánh tĩnh trong các vi kiến ​​trúc hiện đại, như Haswell, nhưng nó gần như không đơn giản như trước đây.
Cody Grey

Bạn có tài liệu tham khảo cho "CPU Intel hiện đại không sử dụng dự đoán nhánh tĩnh" không? Bài viết riêng của Intel ( software.intel.com/en-us/articles/... ) nói cách khác ... Nhưng đó là từ năm 2011
Nemo

Không thực sự có tài liệu tham khảo chính thức, @Nemo. Intel cực kỳ kín tiếng về các thuật toán dự đoán nhánh được sử dụng trong các chip của mình, coi chúng là bí mật thương mại. Hầu hết những gì đã biết đã được tìm ra bằng thử nghiệm thực nghiệm. Như mọi khi, vật liệu của Agner Fog là nguồn tài nguyên tốt nhất, nhưng ngay cả ông ấy cũng nói: "Công cụ dự báo nhánh dường như đã được thiết kế lại trong Haswell, nhưng rất ít thông tin về cấu tạo của nó." Thật không may, tôi không thể nhớ lại nơi lần đầu tiên tôi thấy các điểm chuẩn chứng minh BP tĩnh không được sử dụng nữa.
Cody Grey

5

__builtin_expect có thể được sử dụng để cho trình biên dịch biết bạn mong đợi một nhánh đi theo hướng nào. Điều này có thể ảnh hưởng đến cách mã được tạo. Các bộ xử lý điển hình chạy mã tuần tự nhanh hơn. Vì vậy, nếu bạn viết

if (__builtin_expect (x == 0, 0)) ++count;
if (__builtin_expect (y == 0, 0)) ++count;
if (__builtin_expect (z == 0, 0)) ++count;

trình biên dịch sẽ tạo ra mã như

if (x == 0) goto if1;
back1: if (y == 0) goto if2;
back2: if (z == 0) goto if3;
back3: ;
...
if1: ++count; goto back1;
if2: ++count; goto back2;
if3: ++count; goto back3;

Nếu gợi ý của bạn là đúng, điều này sẽ thực thi mã mà không có bất kỳ nhánh nào thực sự được thực hiện. Nó sẽ chạy nhanh hơn chuỗi thông thường, trong đó mỗi câu lệnh if sẽ phân nhánh xung quanh mã điều kiện và sẽ thực thi ba nhánh.

Bộ xử lý x86 mới hơn có hướng dẫn cho các nhánh dự kiến ​​sẽ được thực hiện hoặc cho các nhánh dự kiến ​​không được thực hiện (có một tiền tố hướng dẫn; không chắc chắn về chi tiết). Không chắc liệu bộ xử lý có sử dụng cái đó hay không. Nó không hữu ích lắm, vì dự đoán rẽ nhánh sẽ xử lý điều này tốt. Vì vậy, tôi không nghĩ rằng bạn thực sự có thể ảnh hưởng đến dự đoán nhánh .


2

Liên quan đến OP, không, không có cách nào trong GCC để yêu cầu bộ xử lý luôn cho rằng nhánh được lấy hoặc không được sử dụng. Những gì bạn có là __builtin_expect, thực hiện những gì người khác nói. Hơn nữa, tôi nghĩ rằng bạn không muốn nói với bộ vi xử lý cho dù các chi nhánh được thực hiện hay không luôn . Các bộ vi xử lý ngày nay, chẳng hạn như kiến ​​trúc Intel có thể nhận ra các mẫu khá phức tạp và thích ứng một cách hiệu quả.

Tuy nhiên, đôi khi bạn muốn kiểm soát xem theo mặc định, một nhánh được dự đoán có được thực hiện hay không: Khi bạn biết mã sẽ được gọi là "lạnh" đối với thống kê nhánh.

Một ví dụ cụ thể: Mã quản lý ngoại lệ. Theo định nghĩa, mã quản lý sẽ xảy ra đặc biệt, nhưng có lẽ khi nó xảy ra hiệu suất tối đa là mong muốn (có thể có một lỗi nghiêm trọng cần xử lý càng sớm càng tốt), do đó bạn có thể muốn kiểm soát dự đoán mặc định.

Một ví dụ khác: Bạn có thể phân loại đầu vào của mình và nhảy vào mã xử lý kết quả phân loại của bạn. Nếu có nhiều phân loại, bộ xử lý có thể thu thập số liệu thống kê nhưng mất chúng vì việc phân loại tương tự không xảy ra đủ sớm và các nguồn dự đoán được dành cho mã được gọi gần đây. Tôi ước sẽ có một nguyên thủy để nói với bộ xử lý "vui lòng không dành tài nguyên dự đoán cho mã này" theo cách mà đôi khi bạn có thể nói "không lưu vào bộ nhớ cache này".

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.