diff --git a/CMakeLists.txt b/CMakeLists.txt index 07fc88a..4a27230 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -36,6 +36,9 @@ if(MSVC) $<$:NDEBUG> ) + # SIMD optimization option for MSVC + option(ENABLE_SIMD_AVX2 "Enable AVX2 SIMD optimizations for MSVC" ON) + target_compile_options(NNets PRIVATE /W3 # Warning level 3 $<$:/Od> # Disable optimization for Debug @@ -45,6 +48,12 @@ if(MSVC) $<$:/Oy> # Enable frame pointer omission ) + # Add SIMD flags for MSVC + if(ENABLE_SIMD_AVX2) + target_compile_options(NNets PRIVATE /arch:AVX2) + message(STATUS "SIMD: AVX2 enabled for MSVC") + endif() + # Set runtime library set_property(TARGET NNets PROPERTY MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>") @@ -56,6 +65,11 @@ else() strcpy_s=strcpy # Use standard strcpy instead of MSVC's strcpy_s ) + # SIMD optimization option (default: native architecture detection) + option(ENABLE_SIMD_NATIVE "Enable SIMD optimizations for native architecture" ON) + option(ENABLE_AVX "Enable AVX SIMD optimizations explicitly" OFF) + option(ENABLE_SSE "Enable SSE SIMD optimizations explicitly" OFF) + target_compile_options(NNets PRIVATE -Wall -Wextra @@ -66,6 +80,21 @@ else() $<$:-O0> $<$:-O3> ) + + # Add SIMD flags based on options + if(ENABLE_SIMD_NATIVE) + # Use -march=native for automatic detection of CPU SIMD capabilities + target_compile_options(NNets PRIVATE -march=native) + message(STATUS "SIMD: Using native architecture detection (-march=native)") + elseif(ENABLE_AVX) + target_compile_options(NNets PRIVATE -mavx -mavx2) + message(STATUS "SIMD: AVX/AVX2 explicitly enabled") + elseif(ENABLE_SSE) + target_compile_options(NNets PRIVATE -msse -msse2 -msse4.1) + message(STATUS "SIMD: SSE/SSE2/SSE4.1 explicitly enabled") + else() + message(STATUS "SIMD: No SIMD optimizations (scalar operations)") + endif() endif() # Set output directories @@ -211,3 +240,19 @@ set_tests_properties(test_retraining_mode PROPERTIES TIMEOUT 600 LABELS "retraining;training" ) + +# Test 9: SIMD optimization test +# Tests SIMD vector operations and --no-simd flag +add_test( + NAME test_simd_optimization + COMMAND ${CMAKE_COMMAND} + -DNNETS_EXE=$ + -DCONFIG_DIR=${CMAKE_SOURCE_DIR}/configs + -DWORK_DIR=${CMAKE_BINARY_DIR} + -P ${CMAKE_SOURCE_DIR}/cmake/test_simd_benchmark.cmake + WORKING_DIRECTORY ${CMAKE_BINARY_DIR} +) +set_tests_properties(test_simd_optimization PROPERTIES + TIMEOUT 120 + LABELS "simd;performance" +) diff --git a/cmake/test_simd_benchmark.cmake b/cmake/test_simd_benchmark.cmake new file mode 100644 index 0000000..a42dfa4 --- /dev/null +++ b/cmake/test_simd_benchmark.cmake @@ -0,0 +1,49 @@ +# test_simd_benchmark.cmake +# Тест для проверки работоспособности SIMD оптимизаций + +message(STATUS "=== SIMD Benchmark Test ===") + +# Run benchmark with SIMD enabled (default) +message(STATUS "Testing with SIMD enabled...") +execute_process( + COMMAND ${NNETS_EXE} -c ${CONFIG_DIR}/simple.json -b --single-thread + WORKING_DIRECTORY ${WORK_DIR} + RESULT_VARIABLE SIMD_RESULT + OUTPUT_VARIABLE SIMD_OUTPUT + ERROR_VARIABLE SIMD_ERROR + TIMEOUT 120 +) + +if(NOT SIMD_RESULT EQUAL 0) + message(FATAL_ERROR "SIMD enabled test failed: ${SIMD_ERROR}") +endif() + +# Check that SIMD info is displayed +if(NOT SIMD_OUTPUT MATCHES "SIMD:") + message(FATAL_ERROR "SIMD info not displayed in output") +endif() + +message(STATUS "SIMD enabled test passed") + +# Run benchmark with SIMD disabled +message(STATUS "Testing with SIMD disabled...") +execute_process( + COMMAND ${NNETS_EXE} -c ${CONFIG_DIR}/simple.json -b --single-thread --no-simd + WORKING_DIRECTORY ${WORK_DIR} + RESULT_VARIABLE NO_SIMD_RESULT + OUTPUT_VARIABLE NO_SIMD_OUTPUT + ERROR_VARIABLE NO_SIMD_ERROR + TIMEOUT 120 +) + +if(NOT NO_SIMD_RESULT EQUAL 0) + message(FATAL_ERROR "SIMD disabled test failed: ${NO_SIMD_ERROR}") +endif() + +# Verify SIMD was disabled +if(NOT NO_SIMD_OUTPUT MATCHES "disabled via --no-simd") + message(FATAL_ERROR "--no-simd flag did not work properly") +endif() + +message(STATUS "SIMD disabled test passed") +message(STATUS "=== SIMD Benchmark Test Complete ===") diff --git a/experiments/test_simd_micro.cpp b/experiments/test_simd_micro.cpp new file mode 100644 index 0000000..e0cf9a8 --- /dev/null +++ b/experiments/test_simd_micro.cpp @@ -0,0 +1,155 @@ +/* + * test_simd_micro.cpp - Микро-бенчмарк для тестирования SIMD операций + * + * Тестирует производительность SIMD-оптимизированных векторных операций + * на больших массивах для измерения реального ускорения. + * + * Компиляция: + * g++ -O3 -march=native -o test_simd_micro test_simd_micro.cpp + * + * Запуск: + * ./test_simd_micro + */ + +#include +#include +#include +#include +#include +#include + +// Глобальный флаг для SIMD (требуется для simd_ops.h) +bool UseSIMD = true; + +#include "../include/simd_ops.h" + +using namespace std; +using namespace std::chrono; + +// Количество повторений для усреднения результатов +const int ITERATIONS = 1000; + +// Размеры массивов для тестирования +const int SIZES[] = {16, 48, 100, 256, 1000, 10000}; +const int NUM_SIZES = sizeof(SIZES) / sizeof(SIZES[0]); + +// Инициализация массива случайными значениями +void initRandom(float* arr, int size) { + for (int i = 0; i < size; i++) { + arr[i] = (float)rand() / RAND_MAX * 2.0f - 1.0f; + } +} + +// Измерение времени выполнения операции сложения +double benchmarkAdd(float* r, const float* a, const float* b, int size, bool useSIMD) { + UseSIMD = useSIMD; + auto start = high_resolution_clock::now(); + + for (int iter = 0; iter < ITERATIONS; iter++) { + op_add_simd(r, a, b, size); + } + + auto end = high_resolution_clock::now(); + return duration_cast(end - start).count() / (double)ITERATIONS; +} + +// Измерение времени выполнения операции вычитания +double benchmarkSub(float* r, const float* a, const float* b, int size, bool useSIMD) { + UseSIMD = useSIMD; + auto start = high_resolution_clock::now(); + + for (int iter = 0; iter < ITERATIONS; iter++) { + op_sub_simd(r, a, b, size); + } + + auto end = high_resolution_clock::now(); + return duration_cast(end - start).count() / (double)ITERATIONS; +} + +// Измерение времени выполнения операции умножения +double benchmarkMul(float* r, const float* a, const float* b, int size, bool useSIMD) { + UseSIMD = useSIMD; + auto start = high_resolution_clock::now(); + + for (int iter = 0; iter < ITERATIONS; iter++) { + op_mul_simd(r, a, b, size); + } + + auto end = high_resolution_clock::now(); + return duration_cast(end - start).count() / (double)ITERATIONS; +} + +// Проверка корректности результатов +bool verifyResults(const float* r_simd, const float* r_scalar, int size) { + for (int i = 0; i < size; i++) { + if (fabs(r_simd[i] - r_scalar[i]) > 1e-6) { + return false; + } + } + return true; +} + +int main() { + cout << "=== SIMD Micro-Benchmark ===" << endl; + cout << "SIMD Extension: " << getSIMDInfo() << endl; + cout << "Iterations per test: " << ITERATIONS << endl; + cout << endl; + + srand(42); // Фиксированный seed для воспроизводимости + + cout << fixed << setprecision(2); + cout << "| Size | Op | Scalar (ns) | SIMD (ns) | Speedup |" << endl; + cout << "|---------|------|-------------|-----------|---------|" << endl; + + for (int s = 0; s < NUM_SIZES; s++) { + int size = SIZES[s]; + + // Выделяем массивы + vector a(size), b(size), r_simd(size), r_scalar(size); + + // Инициализируем случайными значениями + initRandom(a.data(), size); + initRandom(b.data(), size); + + // Тест сложения + double scalarAddTime = benchmarkAdd(r_scalar.data(), a.data(), b.data(), size, false); + double simdAddTime = benchmarkAdd(r_simd.data(), a.data(), b.data(), size, true); + double addSpeedup = scalarAddTime / simdAddTime; + + if (!verifyResults(r_simd.data(), r_scalar.data(), size)) { + cout << "ERROR: Add verification failed for size " << size << endl; + } + + cout << "| " << setw(7) << size << " | ADD | " << setw(11) << scalarAddTime + << " | " << setw(9) << simdAddTime << " | " << setw(6) << addSpeedup << "x |" << endl; + + // Тест вычитания + double scalarSubTime = benchmarkSub(r_scalar.data(), a.data(), b.data(), size, false); + double simdSubTime = benchmarkSub(r_simd.data(), a.data(), b.data(), size, true); + double subSpeedup = scalarSubTime / simdSubTime; + + if (!verifyResults(r_simd.data(), r_scalar.data(), size)) { + cout << "ERROR: Sub verification failed for size " << size << endl; + } + + cout << "| " << setw(7) << size << " | SUB | " << setw(11) << scalarSubTime + << " | " << setw(9) << simdSubTime << " | " << setw(6) << subSpeedup << "x |" << endl; + + // Тест умножения + double scalarMulTime = benchmarkMul(r_scalar.data(), a.data(), b.data(), size, false); + double simdMulTime = benchmarkMul(r_simd.data(), a.data(), b.data(), size, true); + double mulSpeedup = scalarMulTime / simdMulTime; + + if (!verifyResults(r_simd.data(), r_scalar.data(), size)) { + cout << "ERROR: Mul verification failed for size " << size << endl; + } + + cout << "| " << setw(7) << size << " | MUL | " << setw(11) << scalarMulTime + << " | " << setw(9) << simdMulTime << " | " << setw(6) << mulSpeedup << "x |" << endl; + } + + cout << endl; + cout << "=== End Micro-Benchmark ===" << endl; + + return 0; +} diff --git a/experiments/test_simd_speedup.sh b/experiments/test_simd_speedup.sh new file mode 100755 index 0000000..b9b4a22 --- /dev/null +++ b/experiments/test_simd_speedup.sh @@ -0,0 +1,76 @@ +#!/bin/bash +# +# test_simd_speedup.sh - Тест ускорения векторных операций с SIMD +# +# Сравнивает производительность обучения нейросети с включённым и +# выключенным SIMD для измерения реального ускорения. +# +# Использование: +# ./experiments/test_simd_speedup.sh +# + +set -e + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +PROJECT_DIR="$(dirname "$SCRIPT_DIR")" +BUILD_DIR="$PROJECT_DIR/build" +# NNets executable can be in different locations depending on CMake generator +NNETS_EXE="$BUILD_DIR/NNets" +[ -f "$BUILD_DIR/bin/NNets" ] && NNETS_EXE="$BUILD_DIR/bin/NNets" +[ -f "$BUILD_DIR/bin/Release/NNets" ] && NNETS_EXE="$BUILD_DIR/bin/Release/NNets" +CONFIG_FILE="$PROJECT_DIR/configs/benchmark.json" + +echo "=== SIMD Speedup Test ===" +echo "" +echo "Project directory: $PROJECT_DIR" +echo "Build directory: $BUILD_DIR" + +# Check if executable exists +if [ ! -f "$NNETS_EXE" ]; then + echo "Building project..." + mkdir -p "$BUILD_DIR" + cd "$BUILD_DIR" + cmake .. -DCMAKE_BUILD_TYPE=Release + cmake --build . --config Release -j $(nproc 2>/dev/null || sysctl -n hw.ncpu 2>/dev/null || echo 4) + cd "$PROJECT_DIR" +fi + +# Verify executable exists after build +if [ ! -f "$NNETS_EXE" ]; then + echo "Error: Could not find NNets executable at $NNETS_EXE" + exit 1 +fi + +echo "" +echo "Testing with config: $CONFIG_FILE" +echo "" + +# Run benchmark with SIMD enabled (default) +echo "=== Test 1: SIMD Enabled (default) ===" +"$NNETS_EXE" -c "$CONFIG_FILE" -b --single-thread 2>&1 | tee /tmp/simd_enabled.log +SIMD_TIME=$(grep "Training time:" /tmp/simd_enabled.log | awk '{print $3}') +echo "" + +# Run benchmark with SIMD disabled +echo "=== Test 2: SIMD Disabled (--no-simd) ===" +"$NNETS_EXE" -c "$CONFIG_FILE" -b --single-thread --no-simd 2>&1 | tee /tmp/simd_disabled.log +NO_SIMD_TIME=$(grep "Training time:" /tmp/simd_disabled.log | awk '{print $3}') +echo "" + +# Calculate speedup +echo "=== SIMD Speedup Summary ===" +echo "SIMD Enabled: $SIMD_TIME ms" +echo "SIMD Disabled: $NO_SIMD_TIME ms" + +if [ -n "$SIMD_TIME" ] && [ -n "$NO_SIMD_TIME" ] && [ "$SIMD_TIME" -gt 0 ]; then + SPEEDUP=$(echo "scale=2; $NO_SIMD_TIME / $SIMD_TIME" | bc) + echo "Speedup: ${SPEEDUP}x" +else + echo "Could not calculate speedup (missing timing data)" +fi + +echo "" +echo "=== End SIMD Speedup Test ===" + +# Cleanup +rm -f /tmp/simd_enabled.log /tmp/simd_disabled.log diff --git a/include/simd_ops.h b/include/simd_ops.h new file mode 100644 index 0000000..ee4d38c --- /dev/null +++ b/include/simd_ops.h @@ -0,0 +1,361 @@ +/* + * simd_ops.h - SIMD-оптимизированные векторные операции + * + * Этот модуль содержит SIMD-версии базовых векторных операций: + * - Сумма (op_1) + * - Разность z1 - z2 (op_2) + * - Разность z2 - z1 (op_3) + * - Произведение (op_4) + * + * Поддерживаемые наборы инструкций (в порядке приоритета): + * - AVX (256-bit, 8 float за раз) + * - SSE (128-bit, 4 float за раз) + * - Скалярный код (fallback) + * + * Примечание: Для включения SIMD оптимизаций необходимо + * компилировать с флагами -mavx/-msse или использовать -march=native + */ + +#ifndef SIMD_OPS_H +#define SIMD_OPS_H + +// Определяем доступность SIMD инструкций на этапе компиляции +// AVX проверяется первым, так как он более эффективен +#if defined(__AVX__) || defined(__AVX2__) + #define SIMD_AVX_ENABLED 1 + #include +#elif defined(__SSE__) || defined(__SSE2__) || defined(__SSE4_1__) + #define SIMD_SSE_ENABLED 1 + #include + #include +#endif + +// Флаг для включения/выключения SIMD во время выполнения +// Позволяет тестировать производительность с/без SIMD +extern bool UseSIMD; + +// ============================================================================ +// Скалярные версии операций (базовые, без оптимизаций) +// ============================================================================ + +/** + * Скалярная сумма векторов: r[i] = z1[i] + z2[i] + */ +inline void op_add_scalar(float* r, const float* z1, const float* z2, const int size) { + for (int i = 0; i < size; i++) { + r[i] = z1[i] + z2[i]; + } +} + +/** + * Скалярная разность векторов: r[i] = z1[i] - z2[i] + */ +inline void op_sub_scalar(float* r, const float* z1, const float* z2, const int size) { + for (int i = 0; i < size; i++) { + r[i] = z1[i] - z2[i]; + } +} + +/** + * Скалярная обратная разность векторов: r[i] = z2[i] - z1[i] + */ +inline void op_rsub_scalar(float* r, const float* z1, const float* z2, const int size) { + for (int i = 0; i < size; i++) { + r[i] = z2[i] - z1[i]; + } +} + +/** + * Скалярное произведение векторов: r[i] = z1[i] * z2[i] + */ +inline void op_mul_scalar(float* r, const float* z1, const float* z2, const int size) { + for (int i = 0; i < size; i++) { + r[i] = z1[i] * z2[i]; + } +} + +// ============================================================================ +// AVX версии операций (256-bit, 8 float за итерацию) +// ============================================================================ + +#ifdef SIMD_AVX_ENABLED + +/** + * AVX сумма векторов: r[i] = z1[i] + z2[i] + * Обрабатывает 8 элементов за итерацию + */ +inline void op_add_avx(float* r, const float* z1, const float* z2, const int size) { + int i = 0; + + // Обработка по 8 элементов (256 бит) + for (; i <= size - 8; i += 8) { + __m256 v1 = _mm256_loadu_ps(z1 + i); + __m256 v2 = _mm256_loadu_ps(z2 + i); + __m256 result = _mm256_add_ps(v1, v2); + _mm256_storeu_ps(r + i, result); + } + + // Обработка оставшихся элементов скалярно + for (; i < size; i++) { + r[i] = z1[i] + z2[i]; + } +} + +/** + * AVX разность векторов: r[i] = z1[i] - z2[i] + */ +inline void op_sub_avx(float* r, const float* z1, const float* z2, const int size) { + int i = 0; + + for (; i <= size - 8; i += 8) { + __m256 v1 = _mm256_loadu_ps(z1 + i); + __m256 v2 = _mm256_loadu_ps(z2 + i); + __m256 result = _mm256_sub_ps(v1, v2); + _mm256_storeu_ps(r + i, result); + } + + for (; i < size; i++) { + r[i] = z1[i] - z2[i]; + } +} + +/** + * AVX обратная разность векторов: r[i] = z2[i] - z1[i] + */ +inline void op_rsub_avx(float* r, const float* z1, const float* z2, const int size) { + int i = 0; + + for (; i <= size - 8; i += 8) { + __m256 v1 = _mm256_loadu_ps(z1 + i); + __m256 v2 = _mm256_loadu_ps(z2 + i); + __m256 result = _mm256_sub_ps(v2, v1); + _mm256_storeu_ps(r + i, result); + } + + for (; i < size; i++) { + r[i] = z2[i] - z1[i]; + } +} + +/** + * AVX произведение векторов: r[i] = z1[i] * z2[i] + */ +inline void op_mul_avx(float* r, const float* z1, const float* z2, const int size) { + int i = 0; + + for (; i <= size - 8; i += 8) { + __m256 v1 = _mm256_loadu_ps(z1 + i); + __m256 v2 = _mm256_loadu_ps(z2 + i); + __m256 result = _mm256_mul_ps(v1, v2); + _mm256_storeu_ps(r + i, result); + } + + for (; i < size; i++) { + r[i] = z1[i] * z2[i]; + } +} + +#endif // SIMD_AVX_ENABLED + +// ============================================================================ +// SSE версии операций (128-bit, 4 float за итерацию) +// ============================================================================ + +#ifdef SIMD_SSE_ENABLED + +/** + * SSE сумма векторов: r[i] = z1[i] + z2[i] + * Обрабатывает 4 элемента за итерацию + */ +inline void op_add_sse(float* r, const float* z1, const float* z2, const int size) { + int i = 0; + + // Обработка по 4 элемента (128 бит) + for (; i <= size - 4; i += 4) { + __m128 v1 = _mm_loadu_ps(z1 + i); + __m128 v2 = _mm_loadu_ps(z2 + i); + __m128 result = _mm_add_ps(v1, v2); + _mm_storeu_ps(r + i, result); + } + + // Обработка оставшихся элементов + for (; i < size; i++) { + r[i] = z1[i] + z2[i]; + } +} + +/** + * SSE разность векторов: r[i] = z1[i] - z2[i] + */ +inline void op_sub_sse(float* r, const float* z1, const float* z2, const int size) { + int i = 0; + + for (; i <= size - 4; i += 4) { + __m128 v1 = _mm_loadu_ps(z1 + i); + __m128 v2 = _mm_loadu_ps(z2 + i); + __m128 result = _mm_sub_ps(v1, v2); + _mm_storeu_ps(r + i, result); + } + + for (; i < size; i++) { + r[i] = z1[i] - z2[i]; + } +} + +/** + * SSE обратная разность векторов: r[i] = z2[i] - z1[i] + */ +inline void op_rsub_sse(float* r, const float* z1, const float* z2, const int size) { + int i = 0; + + for (; i <= size - 4; i += 4) { + __m128 v1 = _mm_loadu_ps(z1 + i); + __m128 v2 = _mm_loadu_ps(z2 + i); + __m128 result = _mm_sub_ps(v2, v1); + _mm_storeu_ps(r + i, result); + } + + for (; i < size; i++) { + r[i] = z2[i] - z1[i]; + } +} + +/** + * SSE произведение векторов: r[i] = z1[i] * z2[i] + */ +inline void op_mul_sse(float* r, const float* z1, const float* z2, const int size) { + int i = 0; + + for (; i <= size - 4; i += 4) { + __m128 v1 = _mm_loadu_ps(z1 + i); + __m128 v2 = _mm_loadu_ps(z2 + i); + __m128 result = _mm_mul_ps(v1, v2); + _mm_storeu_ps(r + i, result); + } + + for (; i < size; i++) { + r[i] = z1[i] * z2[i]; + } +} + +#endif // SIMD_SSE_ENABLED + +// ============================================================================ +// Диспетчеризация: выбор оптимальной реализации +// Использует флаг UseSIMD для возможности отключения SIMD во время выполнения +// ============================================================================ + +/** + * Сумма векторов с автоматическим выбором реализации + * Выбирает AVX -> SSE -> скалярный код в зависимости от доступности и флага UseSIMD + */ +inline void op_add_simd(float* r, const float* z1, const float* z2, const int size) { +#ifdef SIMD_AVX_ENABLED + if (UseSIMD) { + op_add_avx(r, z1, z2, size); + } else { + op_add_scalar(r, z1, z2, size); + } +#elif defined(SIMD_SSE_ENABLED) + if (UseSIMD) { + op_add_sse(r, z1, z2, size); + } else { + op_add_scalar(r, z1, z2, size); + } +#else + op_add_scalar(r, z1, z2, size); +#endif +} + +/** + * Разность векторов с автоматическим выбором реализации + */ +inline void op_sub_simd(float* r, const float* z1, const float* z2, const int size) { +#ifdef SIMD_AVX_ENABLED + if (UseSIMD) { + op_sub_avx(r, z1, z2, size); + } else { + op_sub_scalar(r, z1, z2, size); + } +#elif defined(SIMD_SSE_ENABLED) + if (UseSIMD) { + op_sub_sse(r, z1, z2, size); + } else { + op_sub_scalar(r, z1, z2, size); + } +#else + op_sub_scalar(r, z1, z2, size); +#endif +} + +/** + * Обратная разность векторов с автоматическим выбором реализации + */ +inline void op_rsub_simd(float* r, const float* z1, const float* z2, const int size) { +#ifdef SIMD_AVX_ENABLED + if (UseSIMD) { + op_rsub_avx(r, z1, z2, size); + } else { + op_rsub_scalar(r, z1, z2, size); + } +#elif defined(SIMD_SSE_ENABLED) + if (UseSIMD) { + op_rsub_sse(r, z1, z2, size); + } else { + op_rsub_scalar(r, z1, z2, size); + } +#else + op_rsub_scalar(r, z1, z2, size); +#endif +} + +/** + * Произведение векторов с автоматическим выбором реализации + */ +inline void op_mul_simd(float* r, const float* z1, const float* z2, const int size) { +#ifdef SIMD_AVX_ENABLED + if (UseSIMD) { + op_mul_avx(r, z1, z2, size); + } else { + op_mul_scalar(r, z1, z2, size); + } +#elif defined(SIMD_SSE_ENABLED) + if (UseSIMD) { + op_mul_sse(r, z1, z2, size); + } else { + op_mul_scalar(r, z1, z2, size); + } +#else + op_mul_scalar(r, z1, z2, size); +#endif +} + +// ============================================================================ +// Информация о доступных SIMD расширениях +// ============================================================================ + +/** + * Возвращает строку с информацией о доступных SIMD расширениях + */ +inline const char* getSIMDInfo() { +#ifdef SIMD_AVX_ENABLED + return "AVX (256-bit, 8 floats per operation)"; +#elif defined(SIMD_SSE_ENABLED) + return "SSE (128-bit, 4 floats per operation)"; +#else + return "None (scalar operations)"; +#endif +} + +/** + * Проверяет, включены ли SIMD оптимизации при компиляции + */ +inline bool isSIMDEnabled() { +#if defined(SIMD_AVX_ENABLED) || defined(SIMD_SSE_ENABLED) + return true; +#else + return false; +#endif +} + +#endif // SIMD_OPS_H diff --git a/main.cpp b/main.cpp index a355088..e606189 100644 --- a/main.cpp +++ b/main.cpp @@ -27,6 +27,7 @@ #include #include #include +#include "simd_ops.h" using namespace std; using json = nlohmann::json; @@ -99,28 +100,32 @@ string g_autoSavePath = ""; // Путь для авт // ============================================================================ // Операции нейронов - элементарные математические действия +// SIMD-оптимизированные версии для ускорения вычислений // ============================================================================ typedef void(__fastcall *oper)(float*, const float*, const float*, const int); -// Сумма +// Флаг для включения/выключения SIMD во время выполнения +bool UseSIMD = true; + +// Сумма - SIMD оптимизированная версия void __fastcall op_1(float* r, const float* z1, const float* z2, const int size) { - for (int i = size; i > 0; i--, r++, z1++, z2++) *r = *z1 + *z2; + op_add_simd(r, z1, z2, size); } -// Разность (z1 - z2) +// Разность (z1 - z2) - SIMD оптимизированная версия void __fastcall op_2(float* r, const float* z1, const float* z2, const int size) { - for (int i = size; i > 0; i--, r++, z1++, z2++) *r = *z1 - *z2; + op_sub_simd(r, z1, z2, size); } -// Разность (z2 - z1) +// Разность (z2 - z1) - SIMD оптимизированная версия void __fastcall op_3(float* r, const float* z1, const float* z2, const int size) { - for (int i = size; i > 0; i--, r++, z1++, z2++) *r = *z2 - *z1; + op_rsub_simd(r, z1, z2, size); } -// Произведение +// Произведение - SIMD оптимизированная версия void __fastcall op_4(float* r, const float* z1, const float* z2, const int size) { - for (int i = size; i > 0; i--, r++, z1++, z2++) *r = *z1 * *z2; + op_mul_simd(r, z1, z2, size); } // Деление (z1 / z2) @@ -257,6 +262,7 @@ void printUsage(const char* programName) { cout << "PERFORMANCE OPTIONS:" << endl; cout << " -j, --threads Number of threads to use (0 = auto, default)" << endl; cout << " --single-thread Disable multithreading (use single thread)" << endl; + cout << " --no-simd Disable SIMD optimizations (use scalar operations)" << endl; cout << endl; cout << "GENERAL OPTIONS:" << endl; cout << " -h, --help Show this help message" << endl; @@ -403,6 +409,8 @@ int main(int argc, char* argv[]) NumThreads = atoi(argv[++i]); } else if (arg == "--single-thread") { UseMultithreading = false; + } else if (arg == "--no-simd") { + UseSIMD = false; } else if (arg == "-h" || arg == "--help") { printUsage(argv[0]); return 0; @@ -588,6 +596,9 @@ int main(int argc, char* argv[]) cout << "Multithreading: disabled (single-threaded mode)" << endl; } + // Вывод информации о SIMD + cout << "SIMD: " << getSIMDInfo() << (UseSIMD ? "" : " (disabled via --no-simd)") << endl; + // Вычисляем производные значения после загрузки конфигурации Images = const_words.size(); Inputs = Receptors + base_size; @@ -761,6 +772,7 @@ int main(int argc, char* argv[]) cout << " Images: " << Images << endl; cout << " Neurons created: " << (Neirons - Inputs) << endl; cout << " Threads: " << NumThreads << (UseMultithreading ? " (multithreaded)" : " (single-threaded)") << endl; + cout << " SIMD: " << getSIMDInfo() << (UseSIMD ? " (enabled)" : " (disabled)") << endl; cout << "Timing:" << endl; cout << " Training time: " << trainingDuration.count() << " ms" << endl; cout << " Training iterations: " << trainingIterations << endl;