我有一些 C++ 函数正在使用 IPOPT 进行优化。虽然成本函数、约束函数等是用 C++ 编写的,但代码最初是使用 C 接口编写的。除非事实证明这是问题所在,否则我还没有费心去改变它。
无论如何......我们正在观察一些意想不到的行为,当我们编译带有/不带有矢量化标志的程序时,优化器会以不同的方式收敛。具体来说,在 CMakeLists 文件中,我们有
set(CMAKE_CXX_FLAGS "-Wall -mavx -mfma")
当我们使用这些设置运行优化器时,优化器会在大约 100 次迭代中收敛。到现在为止还挺好。
但是,我们有理由相信,当为 ARM(特别是 Android)编译时,不会发生矢量化,因为其性能与 Intel 处理器上的性能截然不同。Eigen 文档说应该始终为 64 位 ARM 启用 NEON 指令,但我们有理由怀疑这不会发生。无论如何,这不是这里的问题。
由于这种怀疑,我们想看看如果我们禁用矢量化,我们的英特尔处理器的性能会有多糟糕。这应该给我们一些关于向量化正在发生多少的迹象,以及我们可能期望在 ARM 中看到多少改进。但是,当我们将编译器标志更改为
set(CMAKE_CXX_FLAGS "-Wall")
(或者仅在我们只使用 AVX(没有 fma)的情况下),然后我们从优化器中得到相同的通用解决方案,但收敛性能非常不同。具体来说,在没有矢量化的情况下,优化器需要大约 500 次迭代才能收敛到解决方案。
总而言之:
With AVX and FMA : 100 iterations to converge
With AVX : 200 iterations to converge
Without AVX and FMA : 500 iterations to converge
我们实际上只是更改了 cmake 文件中的那一行,而不是源代码。
我想就为什么会发生这种情况提出一些建议。
我的想法和更多背景信息:
在我看来,无论是否带有矢量化的版本都必须进行一些舍入,这使得 IPOPT 收敛不同。我的印象是添加 AVX 和 FMA 标志不会改变函数的输出,而只会改变计算它们所需的时间。我好像错了。
我们观察到的现象对我来说特别奇怪,因为一方面我们观察到优化器总是收敛到相同的解决方案。这以某种方式表明问题不能太病态。然而,另一方面,优化器在有/没有矢量化标志的情况下表现不同的事实表明,该问题确实对矢量化指令生成的任何小残差敏感。
要记住的另一件事是我们将 IPOPT 预编译到一个库中,并且只是将我们的代码与该预编译库链接起来。所以我认为 AVX 和 FMA 标志不会影响优化器本身。这似乎意味着我们的函数必须输出具有明显不同值的值,具体取决于是否启用了矢量化。
对于那些感兴趣的人,这里是完整的 cmake 文件
cmake_minimum_required(VERSION 3.5)
# If a build type is not passed to cmake, then use this...
if(NOT CMAKE_BUILD_TYPE)
# set(CMAKE_BUILD_TYPE Release)
set(CMAKE_BUILD_TYPE Debug)
endif()
# If you are debugging, generate symbols.
set(CMAKE_CXX_FLAGS_DEBUG "-g")
# If in release mode, use all possible optimizations
set(CMAKE_CXX_FLAGS_RELEASE "-O3")
# We need c++11
set(CMAKE_CXX_STANDARD 11)
# Show us all of the warnings and enable all vectorization options!!!
# I must be crazy because these vectorization flags seem to have no effect.
set(CMAKE_CXX_FLAGS "-Wall -mavx -mfma")
if (CMAKE_SYSTEM_NAME MATCHES "CYGWIN")
include_directories(../../Eigen/
/cygdrive/c/coin/windows/ipopt/include/coin/
/cygdrive/c/coin/windows/ipopt/include/coin/ThirdParty/)
find_library(IPOPT_LIBRARY ipopt HINTS /cygdrive/c/coin/windows/ipopt/lib/)
else ()
include_directories(../../Eigen/
../../coin/CoinIpopt/build/include/coin/
../../coin/CoinIpopt/build/include/coin/ThirdParty/)
find_library(IPOPT_LIBRARY ipopt HINTS ../../coin/CoinIpopt/build/lib/)
endif ()
# Build the c++ functions into an executable
add_executable(trajectory_optimization main.cpp)
# Link all of the libraries together so that the C++-executable can call IPOPT
target_link_libraries(trajectory_optimization ${IPOPT_LIBRARY})