0

我有一些 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})
4

1 回答 1

2

如果您的算法在数值上不稳定,启用 FMA 将导致不同的舍入行为,这可能导致非常不同的结果。此外,在 Eigen 中启用 AVX 将导致不同的加法顺序,并且由于浮点数学是非关联的,这也可能导致行为略有不同。

为了说明为什么非关联性可以产生影响,当a[8]使用 SSE3 或 AXV 添加 8 个连续的双精度时,Eigen 通常会生成与以下内容等效的代码:

// SSE:
double t[2]={a[0], a[1]};
for(i=2; i<8; i+=2)
   t[0]+=a[i], t[1]+=a[i+1]; // addpd
t[0]+=t[1];                  // haddpd

// AVX:
double t[4]={a[0],a[1],a[2],a[3]};
for(j=0; j<4; ++j) t[j]+=a[4+j]; // vaddpd
t[0]+=t[2]; t[1]+=t[3];          // vhaddpd
t[0]+=t[1];                      // vhaddpd

如果没有更多细节,很难说出您的情况到底发生了什么。

于 2018-03-23T10:26:13.260 回答