Chuyển tới nội dung chính

Precompiled Header (PCH)

Trong các dự án C/C++ lớn, thời gian biên dịch có thể trở thành điểm nghẽn nghiêm trọng. Việc tối ưu hoá thời gian biên dịch không chỉ giúp tăng hiệu suất làm việc mà còn cải thiện trải nghiệm lập trình. Một trong các kỹ thuật quan trọng nhất để đạt được điều đó là Precompiled Header (PCH).

Giới thiệu

Precompiled Header (PCH) là kỹ thuật giúp trình biên dịch biên dịch trước một số file header thường xuyên dùng (như <iostream>, <vector>, <windows.h>,...) và lưu lại ở dạng nhị phân, để không cần biên dịch lại mỗi lần. Điều này giúp rút ngắn đáng kể thời gian build, nhất là trong dự án lớn.

Cách tạo và sử dụng PCH

Với g++

  1. Không dùng CMake:

    # Tạo precompiled header từ file pch.hpp
    g++ -x c++-header pch.hpp -o pch.hpp.gch

    # Biên dịch file source sử dụng PCH
    # Cách 1: Dùng -include rõ ràng
    g++ -include pch.hpp main.cpp -o main.exe

    # Cách 2: Không dùng -include, nhưng trong source có #include "pch.hpp"
    g++ main.cpp -o main.exe
    • Ghi chú:
      • g++ sẽ tự động sử dụng file pch.hpp.gch nếu có file #include "pch.hpp" trong mã nguồn và file .gch nằm cùng thư mục với pch.hpp.
      • Tùy chọn -include pch.hpp sẽ ép trình biên dịch include file đó trước tất cả các file khác.
  2. Dùng CMake:

    # CMakeLists.txt
    add_executable(main main.cpp)
    target_precompile_headers(main PRIVATE pch.hpp)
    • CMake sẽ tự động tạo và sử dụng .gch nếu trình biên dịch hỗ trợ.

Với clang++

Cách làm tương tự g++, vì clang++ tương thích với cú pháp GNU


Với clang-cl hoặc cl (MSVC style)

  1. Không dùng CMake:

    # Tạo PCH (.pch) và file .obj đi kèm
    cl /c /Ycpch.hpp /Fppch.pch /Fopch.obj pch.cpp

    # Biên dịch các file khác, dùng lại .pch (chỉ compile, không link)
    cl /c /Yupch.hpp /Fppch.pch /Fomain.obj main.cpp
    • Ghi chú:
      • /Ycpch.hpp để tạo PCH từ file header được include trong pch.cpp. File pch.cpp bắt buộc phải chứa dòng #include "pch.hpp", nếu không trình biên dịch sẽ báo lỗi.
      • /Yupch.hpp để sử dụng lại file .pch đã tạo.
      • /Fppch.pch để đặt tên file .pch sinh ra.
      • /Fo"*.obj" để đặt tên file .obj sinh ra.
      • /c để chỉ biên dịch, không liên kết (link). Bắt buộc khi tạo .pch.
  2. Dùng CMake:

    # CMakeLists.txt
    add_executable(main main.cpp pch.cpp)
    target_precompile_headers(main PRIVATE pch.hpp)
    • Với trình biên dịch MSVC, CMake sẽ sinh ra các cờ /Yc (create) và /Yu (use) tự động.

Cách tổ chức file PCH

  • pch.hpp: file header chứa các include thường dùng.

  • pch.cpp (chỉ với MSVC): file source chỉ include pch.hpp, dùng để tạo .pch ban đầu.

    Ví dụ nội dung pch.hpp:

    pch.hpp
    #ifndef PCH_HPP
    #define PCH_HPP

    // Các thư viện STL thường dùng
    #include <iostream>
    #include <vector>
    #include <string>
    #include <map>
    #include <unordered_map>
    #include <memory>
    #include <algorithm>

    // Nếu là ứng dụng Windows
    // #include <windows.h>

    #endif
    pch.cpp
    // pch.cpp (chỉ dùng với MSVC)
    #include "pch.hpp"
  • Bạn nên chọn lọc những thư viện thực sự dùng thường xuyên để đưa vào pch.hpp, tránh nhồi nhét quá nhiều gây giảm hiệu quả.

Kiểm tra liệu .pch có được áp dụng

  • Có thể dùng cờ -H khi biên dịch để liệt kê tất cả thư viện đã được include (chỉ áp dụng với g++clang++, clang-clcl không có cờ tương đương)
  • Kiểm tra log build kết quả, nếu thấy ! trước thư viện .gch có nghĩa là PCH đã được áp dụng thành công.
  • Ví dụ kết quả log build:
    ! include/pch.hpp.gch
    D:\src\test.cpp
    . C:/msys64/ucrt64/include/c++/15.1.0/iostream
    . C:/msys64/ucrt64/include/c++/15.1.0/vector

Lưu ý khi dùng PCH

  • Không nên include code thay đổi thường xuyên (ví dụ: header của file bạn đang chỉnh sửa).
  • Nếu include một file khác trong pch.hpp, bạn không nên thay đổi file đó thường xuyên.
  • Dùng #pragma once hoặc include guard để tránh include đệ quy.
  • PCH không bắt buộc, nhưng rất nên dùng trong dự án lớn để tăng tốc build.
  • Không dùng PCH cho mọi file – chỉ nên dùng cho file nào thực sự cần.

Khi nào không nên dùng PCH?

  • Dự án nhỏ, ít file, thời gian build không đáng kể.
  • Khi phải liên tục thay đổi các file header – việc tạo lại PCH còn tốn thời gian hơn.
  • Nếu bạn cần kiểm soát tuyệt đối thứ tự include (PCH có thể gây khó debug lỗi include).