1 điểm bởi GN⁺ 2025-02-10 | 1 bình luận | Chia sẻ qua WhatsApp

Giới thiệu

  • Bài viết này nhằm đính chính những ngộ nhận phổ biến liên quan đến phân nhánh trên GPU.
  • Một số website giáo dục đang lan truyền thông tin sai lệch, nên cần được sửa lại.

Vấn đề

  • Đưa ra ví dụ mã dùng toán tử ba ngôi để triển khai thực thi có điều kiện trong mã GPU.
  • Một số người đề xuất "tối ưu hóa" bằng cách thay thế nó bằng phép toán số học, nhưng đó là cách hiểu sai.
  • Toán tử ba ngôi thực hiện di chuyển có điều kiện, và việc này được triển khai bằng các phép toán bit đơn giản.
  • Phân nhánh thực sự có xuất hiện trong mã GPU, nhưng không được dùng cho các thao tác di chuyển thanh ghi nhỏ.

Vấn đề của kiểu tối ưu hóa sai lầm

  • Kiểu tối ưu hóa được đề xuất thực tế chạy chậm hơn mã gốc.
  • Hàm step() được triển khai bằng toán tử ba ngôi, nên còn bổ sung thêm các phép nhân và cộng không cần thiết.
  • Trong mã gốc, giá trị được di chuyển trực tiếp theo điều kiện.

Phân tích mã máy

  • Có thể xác nhận qua mã máy từ trình biên dịch AMD và Microsoft rằng GPU không thực hiện phân nhánh.
  • Việc di chuyển có điều kiện được thực hiện bằng phép so sánh và mặt nạ bit.

Kết luận

  • Đề xuất tối ưu hóa bằng hàm step() là thông tin sai lệch và cần được đính chính.

  • Thông tin sai này đã lan truyền hơn 20 năm và cần được sửa lại.

  • Inigo Quilez - học đồ họa máy tính từ năm 1994.

1 bình luận

 
GN⁺ 2025-02-10
Ý kiến trên Hacker News
  • Tôi tin chắc kết luận của bài gốc là đúng, nhưng lập luận sẽ thuyết phục hơn nếu có phần sinh mã cho cả hai phiên bản, chứ không chỉ phiên bản tốt hơn

    • Bài có cho thấy mã máy được sinh ra để chứng minh phiên bản “tối ưu hóa” thực tế chạy chậm hơn nhiều so với bản gốc, nhưng không chứng minh rằng phiên bản tệ còn tệ hơn nữa
  • Giá mà có một cách tốt để biết khi nào if thực sự ép tạo nhánh

    • Lý do mọi người dùng mix/lerp đắt hơn là vì dù có thể chỉ tốn một chút overhead nhỏ, họ vẫn sợ tạo ra nhánh
    • Việc v = x > y ? a : b; thực sự hoạt động là điều tốt, nhưng đáng lo ở chỗ if là một cú pháp mà đôi khi có nhánh, đôi khi lại không
  • Bài này cũng liên quan: sửa lại lời khuyên sai về cách viết nhánh trên GPU

    • Trước đây các tối ưu hóa để tránh nhánh từng có hiệu quả, nhưng giờ thì không nên làm vậy nữa
    • Vì bộ xử lý và trình biên dịch thay đổi, tốt hơn là cung cấp nhiều biến thể và chọn cái nhanh nhất ở thời gian chạy
  • Tôi thắc mắc vì sao trình biên dịch không nhận ra rằng phiên bản “tối ưu hóa” là tương đương

    • Nó lẽ ra nên hiểu step() và có thể tối ưu riêng cho các trường hợp step() = 0.0step() == 1.0
  • Tôi từng dính đúng vấn đề này. Claude/ChatGPT cũng đề xuất đây là một cách tối ưu hóa, nhưng nó lại gây giảm hiệu năng

    • Cảm ơn Inigo
  • Tôi thắc mắc làm sao để biết một hàm OpenGL có đang bị giả lập thay vì gọi tính năng phần cứng gốc của GPU hay không

  • Khi viết mã, cần có kinh nghiệm mới có thể tự tin rằng sẽ không có nhánh điều kiện

    • Rất khó biết có bao nhiêu phép toán sau điều kiện sẽ kích hoạt nhánh, và trình biên dịch có thể lược bỏ những phép toán nào
    • Tôi băn khoăn liệu có nên dùng một bộ kiểm thử hiệu năng để kiểm tra các suy giảm hiệu năng vô tình xảy ra hay không
  • Giải thích cách các biến thể của hàm mix hoạt động với vector