Sử dụng hiệu quả đa luồng trong AmiBroker

ha.anh
5 Min Read

1. Giới thiệu

AmiBroker từ phiên bản 5.50 hỗ trợ đầy đủ đa luồng, cho phép thực thi song song trên tất cả các lõi CPU trong cả biểu đồ và cửa sổ Phân tích mới. Điều này giúp tăng tốc độ xử lý và cải thiện khả năng phản hồi của giao diện. Ví dụ, trên chip Intel i7 4 lõi (8 luồng), hiệu suất có thể tăng đến 8 lần tùy theo độ phức tạp công thức và khối lượng dữ liệu xử lý.

Tuy nhiên, để tận dụng tối đa hiệu quả đa luồng, người dùng cần tránh những sai sót lập trình phổ biến có thể gây nghẽn hiệu suất.

2. Hiểu cơ chế đa luồng trong AmiBroker

AmiBroker phân bổ 1 luồng cho mỗi tác vụ trên 1 mã chứng khoán. Điều này dẫn đến các quy tắc sau:

  • 1 thao tác x 1 mã = 1 luồng
  • N thao tác x 1 mã = N luồng (ví dụ: 3 khung biểu đồ = 3 luồng)
  • 1 thao tác x N mã = N luồng
  • P thao tác x N mã = P x N luồng

Một số thao tác có phần xử lý giao diện (UI) nên chỉ phần AFL chạy đa luồng. Phiên bản 5.70 có tối ưu hóa đơn mã bằng đa luồng, tuy nhiên có giới hạn.

3. Giới hạn số luồng

  • AmiBroker Standard: Tối đa 2 luồng/phân tích
  • AmiBroker Professional: Tối đa 32 luồng/phân tích
  • Giới hạn dựa vào số lõi logic CPU Windows nhận diện (ví dụ: i7 có thể là 8 luồng).

4. Các lỗi lập trình ảnh hưởng đến hiệu suất đa luồng

4.1. Tránh OLE / CreateObject

OLE chậm và yêu cầu giao diện xử lý, gây nghẽn nếu nhiều luồng gọi đồng thời. Thay thế bằng các hàm gốc như:

RequestTimedRefresh();

SetOption(“RefreshWhenCompleted”, True);

GetOption(“FilterIncludeWatchlist”);

4.2. Giảm sử dụng AddToComposite / Foreign

Các hàm này truy cập ký hiệu khác → yêu cầu khóa toàn cục → giảm tốc độ. Ưu tiên dùng biến tĩnh.

4.3. Quản lý biến tĩnh hiệu quả

Ghi biến tĩnh nên được thực hiện một lần, chỉ tại bước đầu tiên:

if (Status(“stocknum”) == 0) {

  StaticVarSet(…);

}

Đọc biến tĩnh sau đó sẽ nhanh và an toàn. Trường hợp cần ghi từ nhiều luồng, dùng:

if (_TryEnterCS(“mysemaphore”)) {

  // đọc/ghi biến tĩnh

  _LeaveCS();

} else {

  _TRACE(“Không thể nhập CS”);

}

4.4. Xử lý khởi tạo trong cửa sổ phân tích

Chạy mã khởi tạo ở ký hiệu đầu tiên:

if (Status(“stocknum”) == 0) {

  // xử lý trước

}

Không đặt trong #include.

4.5. Tránh Foreign(“~~~Equity”) trong phân tích mới

Foreign(“~~~Equity”) chỉ có ý nghĩa sau khi kiểm tra ngược hoàn tất. Để lấy vốn chủ sở hữu hiện tại:

SetOption(“UseCustomBacktestProc”, True);

 

if (Status(“action”) == actionPortfolio) {

  bo = GetBacktesterObject();

  bo.PreProcess();

  for (bar = 0; bar < BarCount; bar++) {

    bo.ProcessTradeSignals(bar);

    PortEquity[bar] = bo.Equity;

  }

  bo.PostProcess();

}

Hoặc:

if (Status(“action”) == actionPortfolio) {

  bo = GetBacktesterObject();

  bo.Backtest();

  AddToComposite(bo.EquityArray, “~~~MY_EQUITY”, “X”, atcFlagDeleteValues | atcFlagEnableInPortfolio);

}

5. Lưu ý về kiểm tra ngược cá nhân

  • Kiểm tra ngược cá nhân = kiểm tra 1 mã → chỉ dùng 1 luồng
  • Gồm 2 giai đoạn: thu tín hiệu (đa luồng nếu có nhiều mã), và kiểm tra (giao tiếp UI/OLE → chỉ 1 luồng)
  • Phiên bản 5.70 hỗ trợ tối ưu hóa đơn mã đa luồng nhưng KHÔNG hỗ trợ backtester tùy chỉnh

6. Kỳ vọng hợp lý

Không nên xử lý dữ liệu quá lớn (VD: 100GB), vì tốc độ đọc RAM/SSD có giới hạn:

  • L1 cache: 52 GB/s
  • RAM: 11 GB/s
  • SSD: ~0.2 GB/s → Đọc 100GB từ SSD mất gần 10 phút.

Với hệ thống 64-bit, giới hạn bộ nhớ ít hơn, nhưng truy cập ổ đĩa vẫn là điểm nghẽn lớn. Hãy đảm bảo dữ liệu vừa RAM và tối ưu mã AFL để tận dụng đa luồng hiệu quả.

TAGGED:
Share This Article
Leave a Comment