Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 10 additions & 22 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -45,18 +45,18 @@ jobs:
cmake \
ninja-build \
libopenblas-dev \
pybind11-dev \
python3-dev \
${{ matrix.compiler == 'clang' && 'clang libomp-dev' || '' }}

- name: Install Python build dependencies
# pybind11's CMake config package is needed even though libpybind11-dev
# is installed above, in case find_package(pybind11) resolves via pip's
# copy instead - installing both covers either path.
run: pip install pybind11
run: python -m pip install nanobind rich

- name: Build and test (Release)
run: python build.py Release
- name: Build (Release)
# --no-tests: CMakeLists.txt no longer registers any CTest tests
# (the old test/benchmark executables were removed), so running
# ctest here would just fail on "no tests found" for no useful
# reason.
run: python build.py Release --no-tests

- name: Upload build log
if: always()
Expand Down Expand Up @@ -105,7 +105,7 @@ jobs:
!C:/vcpkg/buildtrees
!C:/vcpkg/packages
!C:/vcpkg/downloads
key: vcpkg-windows-openblas-pybind11-v1
key: vcpkg-windows-openblas-v1

- name: Bootstrap vcpkg
if: steps.vcpkg-cache.outputs.cache-hit != 'true'
Expand All @@ -114,14 +114,10 @@ jobs:
C:/vcpkg/bootstrap-vcpkg.bat

- name: Install vcpkg dependencies
# Mirrors CMakeLists.txt: only OpenBLAS and pybind11 come from
# vcpkg. SLEEF and Google Benchmark are fetched from source by CMake
# itself (see FetchContent_Declare in CMakeLists.txt) and need no
# package manager step at all.
run: C:/vcpkg/vcpkg.exe install openblas pybind11 --triplet x64-windows
run: C:/vcpkg/vcpkg.exe install openblas --triplet x64-windows

- name: Install Python build dependencies
run: pip install pybind11
run: python -m pip install nanobind rich

- name: Configure (CMake, using the windows-clang preset)
# Uses CMakePresets.json's "windows-clang" preset directly, rather
Expand All @@ -133,14 +129,6 @@ jobs:
- name: Build
run: cmake --build build/Release --config Release --parallel 2>&1 | Tee-Object -FilePath windows-build.log

- name: Run tests
run: |
$exe = Get-ChildItem -Path build/Release -Recurse -Filter "DeepityTests.exe" | Select-Object -First 1
if (-not $exe) {
throw "DeepityTests.exe not found under build/Release - check windows-build.log"
}
& $exe.FullName

- name: Upload logs
if: always()
uses: actions/upload-artifact@v4
Expand Down
157 changes: 0 additions & 157 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,14 +1,12 @@
cmake_minimum_required(VERSION 3.21)

project(deepity C CXX)
enable_testing()

set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)

option(DEEPITY_ENABLE_CUDA "Build with CUDA support" ON)
option(DEEPITY_BUILD_TESTS "Build the DeepityTests target" OFF)
option(DEEPITY_BUILD_PYTHON_BINDINGS "Build the Python bindings target" ON)
# Recommended if on Intel CPUs
option(DEEPITY_USE_MKL "Use Intel MKL instead of OpenBLAS (Intel CPUs only -- MKL has a documented history of deliberately worse performance on non-Intel CPUs via runtime dispatch)" OFF)
Expand Down Expand Up @@ -259,29 +257,6 @@ set(CMAKE_REQUIRED_QUIET ON)
FetchContent_MakeAvailable(sleef)
set(CMAKE_REQUIRED_QUIET OFF)

# --- Google Benchmark -----------------------------------------------

if(DEEPITY_BUILD_TESTS)
if(CMAKE_CXX_COMPILER_ID MATCHES "Clang")
add_compile_options(-Wno-c2y-extensions)
endif()

set(BENCHMARK_ENABLE_WERROR OFF CACHE BOOL "" FORCE)
set(BENCHMARK_ENABLE_PEDANTIC OFF CACHE BOOL "" FORCE)
set(BENCHMARK_ENABLE_TESTING OFF CACHE BOOL "" FORCE)
set(BENCHMARK_ENABLE_GTEST_TESTS OFF CACHE BOOL "" FORCE)

FetchContent_Declare(
benchmark
GIT_REPOSITORY https://github.com/google/benchmark.git
GIT_TAG v1.9.1
)

set(CMAKE_REQUIRED_QUIET ON)
FetchContent_MakeAvailable(benchmark)
set(CMAKE_REQUIRED_QUIET OFF)
endif()

# --- Deepity library ------------------------------------------------

add_library(Deepity
Expand Down Expand Up @@ -351,28 +326,6 @@ set_target_properties(Deepity PROPERTIES
INTERPROCEDURAL_OPTIMIZATION_RELEASE ${DEEPITY_IPO_SUPPORTED}
)

# --- Runtime DLL copying (Windows) -----------------------------------
# $<TARGET_RUNTIME_DLLS:...> only knows about DLLs CMake can see a full
# IMPORTED_LOCATION for (e.g. openblas_dll above). It can't discover
# libomp.dll on its own, so DEEPITY_OMP_DLL (resolved earlier) is copied
# alongside it explicitly. Centralized here instead of repeating both
# copy steps for every executable below.
function(deepity_copy_runtime_dlls target)
add_custom_command(TARGET ${target} POST_BUILD
COMMAND ${CMAKE_COMMAND} -E $<IF:$<BOOL:$<TARGET_RUNTIME_DLLS:${target}>>,copy_if_different,true>
$<TARGET_RUNTIME_DLLS:${target}>
$<TARGET_FILE_DIR:${target}>
COMMAND_EXPAND_LISTS
)
if(DEEPITY_OMP_DLL)
add_custom_command(TARGET ${target} POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_if_different
"${DEEPITY_OMP_DLL}"
$<TARGET_FILE_DIR:${target}>
)
endif()
endfunction()

# --- pydeepity ------------------------------------------------------

if(DEEPITY_BUILD_PYTHON_BINDINGS AND NOT CMAKE_BUILD_TYPE STREQUAL "Debug")
Expand Down Expand Up @@ -424,113 +377,3 @@ if(WIN32)
install(FILES ${DEEPITY_OMP_DLL} DESTINATION pydeepity)
endif()
endif()

# --- Tests ----------------------------------------------------------

if(DEEPITY_BUILD_TESTS)
add_executable(DirectKPVerify tests/tDirectKPVerify.cpp)
target_link_libraries(DirectKPVerify PRIVATE Deepity)
set_target_properties(DirectKPVerify PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
add_test(NAME DirectKPVerify COMMAND DirectKPVerify)

if(WIN32)
deepity_copy_runtime_dlls(DirectKPVerify)
endif()
endif()

# --- Profiling ------------------------------------------------------

if(DEEPITY_BUILD_TESTS)
add_executable(GaussSeidelMiddleLayerVerify tests/tGaussSeidelMiddleLayerVerify.cpp)
target_link_libraries(GaussSeidelMiddleLayerVerify PRIVATE Deepity)
set_target_properties(GaussSeidelMiddleLayerVerify PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
add_test(NAME GaussSeidelMiddleLayerVerify COMMAND GaussSeidelMiddleLayerVerify)

if(WIN32)
deepity_copy_runtime_dlls(GaussSeidelMiddleLayerVerify)
endif()
endif()

if(DEEPITY_BUILD_TESTS)
add_library(DeepityProfiled STATIC
src/DiscriminativePCLayer.cpp
src/RBLayer.cpp
src/ConvPCLayer.cpp
src/SimpleConvPCLayer.cpp
src/SimplePCLayer.cpp
src/DiscriminativePCNetwork.cpp
src/ConvPCNetwork.cpp
src/SimplePCNetwork.cpp
src/ModelIO.cpp
src/StreamAlignedBatcher.cpp
)

if(DEEPITY_USE_MKL AND DEEPITY_BLAS_RESOLVED)
target_compile_definitions(DeepityProfiled PUBLIC DEEPITY_USE_MKL)
endif()

target_include_directories(DeepityProfiled PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}/include
${BLAS_INCLUDE_DIR}
)
target_include_directories(DeepityProfiled SYSTEM PUBLIC
${sleef_SOURCE_DIR}/include
${sleef_BINARY_DIR}/include
)
target_link_libraries(DeepityProfiled PUBLIC
${BLAS_LIBRARIES}
OpenMP::OpenMP_CXX
sleef
)

target_compile_definitions(DeepityProfiled PUBLIC SLEEF_STATIC_LIBS PCN_PROFILE)

if(WIN32)
target_compile_definitions(DeepityProfiled PUBLIC NOMINMAX)
endif()

if(MSVC)
target_compile_options(DeepityProfiled PRIVATE /O2 /fp:fast /openmp:llvm)
elseif(CMAKE_CXX_COMPILER_ID MATCHES "Clang")
target_compile_options(DeepityProfiled PRIVATE -O3 -ffast-math -fvectorize -fslp-vectorize)
else()
target_compile_options(DeepityProfiled PRIVATE -O3 -ffast-math -ftree-vectorize -ftree-slp-vectorize)
endif()

add_executable(DeepityProfile tests/tProfile.cpp)
target_link_libraries(DeepityProfile PRIVATE DeepityProfiled)
target_compile_definitions(DeepityProfile PRIVATE PCN_PROFILE)

set_target_properties(DeepityProfile PROPERTIES
RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin
)
add_test(NAME DeepityProfile COMMAND DeepityProfile)

if(WIN32)
deepity_copy_runtime_dlls(DeepityProfile)
endif()

# --- README benchmark: std-library vs Deep:: activations ---------
add_executable(ActivationBenchmark tests/tActivations.cpp)
target_link_libraries(ActivationBenchmark PRIVATE DeepityProfiled benchmark)

set_target_properties(ActivationBenchmark PROPERTIES
RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin
)

if(WIN32)
deepity_copy_runtime_dlls(ActivationBenchmark)
endif()

# --- README benchmark: sustained GFLOPS during a real train step -
add_executable(GflopsBenchmark tests/tGflopsBenchmark.cpp)
target_link_libraries(GflopsBenchmark PRIVATE DeepityProfiled benchmark)

set_target_properties(GflopsBenchmark PROPERTIES
RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin
)

if(WIN32)
deepity_copy_runtime_dlls(GflopsBenchmark)
endif()
endif()
69 changes: 31 additions & 38 deletions deepity_build/cmake_runner.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,7 @@ def find_generator() -> tuple[str | None, str]:
ninja = shutil.which("ninja")
return ninja, "Ninja" if ninja else "CMake"


def configure_command(config: BuildConfig, ninja: str | None, pgo_phase: str | None = None) -> list[str]:
"""pgo_phase: None (no PGO), "GENERATE", or "USE" -- distinguishes
which pass of the two-pass PGO workflow this configure call is for.
A single config.pgo boolean can't express this on its own, since
both passes share the same BuildConfig."""
profile = config.profile

cmd = [
Expand All @@ -35,47 +30,45 @@ def configure_command(config: BuildConfig, ninja: str | None, pgo_phase: str | N
f"-DDEEPITY_ARCH_FLAGS={profile.unix_flags}",
]

if ninja:
cmd.extend(["-G", "Ninja"])

if pgo_phase in ("GENERATE", "USE"):
cmd.append(f"-DDEEPITY_PGO_MODE={pgo_phase}")
cmd.append(f"-DDEEPITY_PGO_DATA_DIR={config.pgo_data_dir}")

if profile.msvc_flags:
cmd.append(f"-DDEEPITY_MSVC_ARCH_FLAGS={profile.msvc_flags}")

if sys.platform == "win32":
if ninja:
cmd.extend(["-G", "Ninja"])

# CC being unset doesn't mean clang isn't in play -- CMake can
# auto-detect and pick it up on its own (as this project's own
# builds have shown), so check what's actually on PATH rather
# than an environment variable that may never have been set.
clang_path = shutil.which("clang")
cc_env = os.environ.get("CC", "").lower()
using_clang = clang_path is not None or "clang" in cc_env

if using_clang:
# Locate libomp.lib next to the clang.exe on PATH
omp_lib = None
if clang_path:
llvm_lib_dir = Path(clang_path).parent.parent / "lib"
candidate = llvm_lib_dir / "libomp.lib"
if candidate.is_file():
omp_lib = candidate.as_posix()

cmd.extend([
f"-DCMAKE_C_COMPILER=clang",
f"-DCMAKE_CXX_COMPILER=clang++",
"-DOpenMP_C_FLAGS=-fopenmp",
"-DOpenMP_CXX_FLAGS=-fopenmp",
"-DOpenMP_C_LIB_NAMES=omp",
"-DOpenMP_CXX_LIB_NAMES=omp",
])
if omp_lib:
cmd.append(f"-DOpenMP_omp_LIBRARY={omp_lib}")
if sys.platform == "win32" and ninja:
# CC being unset doesn't mean clang isn't in play -- CMake can
# auto-detect and pick it up on its own (as this project's own
# builds have shown), so check what's actually on PATH rather
# than an environment variable that may never have been set.
clang_path = shutil.which("clang")
cc_env = os.environ.get("CC", "").lower()
using_clang = clang_path is not None or "clang" in cc_env

if using_clang:
omp_lib = None
if clang_path:
llvm_lib_dir = Path(clang_path).parent.parent / "lib"
candidate = llvm_lib_dir / "libomp.lib"
if candidate.is_file():
omp_lib = candidate.as_posix()

cmd.extend([
f"-DCMAKE_C_COMPILER=clang",
f"-DCMAKE_CXX_COMPILER=clang++",
"-DOpenMP_C_FLAGS=-fopenmp",
"-DOpenMP_CXX_FLAGS=-fopenmp",
"-DOpenMP_C_LIB_NAMES=omp",
"-DOpenMP_CXX_LIB_NAMES=omp",
])
if omp_lib:
cmd.append(f"-DOpenMP_omp_LIBRARY={omp_lib}")
return cmd


def build_command(config: BuildConfig) -> list[str]:
return [
"cmake",
Expand All @@ -98,4 +91,4 @@ def test_command(config: BuildConfig) -> list[str]:
"--output-on-failure",
"-j",
str(config.jobs),
]
]
Loading
Loading