0

IPOPT で最適化する C++ 関数がいくつかあります。コスト関数、制約関数などは C++ で記述されていますが、コードはもともと C インターフェイスを使用するように記述されています。それが問題であることが判明しない限り、私はまだそれを変更することを気にしていません.

とにかく...ベクトル化フラグを使用して/使用せずにプログラムをコンパイルすると、オプティマイザーが異なる方法で収束するという予期しない動作が観察されています。具体的には、CMakeLists ファイルには、

set(CMAKE_CXX_FLAGS "-Wall -mavx -mfma")

これらの設定でオプティマイザーを実行すると、オプティマイザーは約 100 回の反復で収束します。ここまでは順調ですね。

ただし、ARM (特に Android) 用にコンパイルした場合、パフォーマンスが Intel プロセッサとは大幅に異なるため、ベクトル化は発生しないと考える理由があります。Eigen のドキュメントには、64 ビット ARM では NEON 命令を常に有効にする必要があると記載されていますが、それが行われていないのではないかと疑う理由があります。とにかく、それはここでの問題ではありません。

この疑いがあるため、ベクトル化を無効にした場合に Intel プロセッサのパフォーマンスがどの程度低下するかを確認したいと考えました。これにより、ベクトル化がどの程度行われているか、また 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ファイルの1行だけを変更しています。

なぜこれが発生するのかについて、いくつかの提案をお願いします。


私の考えとその他の背景情報:

ベクトル化を使用するバージョンと使用しないバージョンのどちらでも何らかの丸め処理を行う必要があり、それによって IPOPT の収束が異なるように思われます。AVX フラグと FMA フラグを追加しても、関数の出力は変わらず、計算にかかる時間だけが変わるという印象を受けました。私は間違っているようです。

私たちが観察している現象は、オプティマイザーが常に同じ解に収束することを観察しているため、私には特に奇妙に思えます。これはどういうわけか、問題があまりにも悪条件であってはならないことを示唆しています。しかし一方で、ベクトル化フラグの有無でオプティマイザーの動作が異なるという事実は、ベクトル化された命令によって生成される小さな残差が何であれ、問題が実際に敏感であることを示唆しています。

心に留めておくべきもう 1 つのことは、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 つの連続する double を追加すると、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 に答える