根据这个question,我认为在C ++ 17中,带有默认分配器的std :: vector应该处理对齐类型。但是,以下代码
#include <iostream>
#include <iterator>
#include <array>
#include <vector>
template<typename T, size_t N, size_t Alignment>
struct alignas(Alignment) AlignedArray : public std::array<T, N>
{
friend std::ostream& operator<<(std::ostream& o, const AlignedArray& a)
{
std::copy(a.cbegin(), a.cend(), std::ostream_iterator<T>(o, " "));
return o;
}
};
int main()
{
using Array = AlignedArray<double, 24, 64>;
std::vector<Array> v(10);
for(const auto& e : v)
{
auto arr(e);
std::cout << arr << std::endl;
}
return 0;
}
当我使用clang 6.0.1和arr
编译-mavx
时,会错误创建-mavx
。如果没有clang++ -I<path_to_libcxx>/include/c++/v1 -g -mavx -std=c++17 main.cpp -stdlib=libc++ -lc++abi -o alignastest -L<path_to_libcxx>/lib -L<path_to_libcxxabi>/lib
开关,它将运行正常(CPU是E5-2697 v2)。我用
std::aligned_alloc
。
我在旧的RHEL 6.9上运行此代码,在该版本中我编译了clang 6.0.1和libcxx,libcxxabi。
我在另一个系统(Ubuntu 18.10,gcc 8)上进行了测试,它可以正常工作。
关于对齐方式,我发现libc ++中__config.h
的实现依赖于C11功能,该功能仅在最新的glibc版本(#if __GLIBC_PREREQ(2, 17)
#define _LIBCPP_HAS_C11_FEATURES
#endif
)中启用:
ldd (GNU libc) 2.12
很遗憾,RHEL 6.9仅安装了alignas
。 debug] [BaseDriver] Event 'newSessionStarted' logged at 1535090782935 (11:36:22 GMT+0530 (India Standard Time))
[W3C] Encountered internal error running command: Error: Cannot start the 'com.infosys.android.ui' application. Original error: Error executing adbExec. Original error: 'Command '/home/shubhambansal/Android/Sdk/platform-tools/adb -P 5037 -s 42007dee9bec64d3 shell am start -W -n com.infosys.android.ui/com.infosys.android.ui.controller.GroupMainActivity -S -a android.intent.action.MAIN -c android.intent.category.LAUNCHER -f 0x10200000' exited with code 1'; Stderr: 'java.lang.SecurityException: Permission Denial: starting Intent { act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] flg=0x10200000 cmp=com.infosys.android.ui/.controller.GroupMainActivity launchParam=MultiScreenLaunchParams { mDisplayId=0 mBaseDisplayId=0 mFlags=0 } } from null (pid=5830, uid=2000) not exported from uid 10136
[W3C] at android.os.Parcel.readException(Parcel.java:1704)
[W3C] at android.os.Parcel.readException(Parcel.java:1654)
[W3C] at android.app.ActivityManagerProxy.startActivityAndWait(ActivityManagerNative.java:3716)
[W3C] at com.android.commands.am.Am.runStart(Am.java:658)
[W3C] at com.android.commands.am.Am.onRun(Am.java:392)
[W3C] at com.android.internal.os.BaseCommand.run(BaseCommand.java:51)
[W3C] at com.android.commands.am.Am.main(Am.java:125)
[W3C] at com.android.internal.os.RuntimeInit.nativeFinishInit(Native Method)
[W3C] at com.android.internal.os.RuntimeInit.main(RuntimeInit.java:315)'; Code: '1'
[W3C] at ADB.callee$0$0$ (/home/linuxbrew/.linuxbrew/lib/node_modules/appium/node_modules/appium-adb/lib/tools/apk-utils.js:125:11)
[W3C] at tryCatch (/home/linuxbrew/.linuxbrew/lib/node_modules/appium/node_modules/babel-runtime/regenerator/runtime.js:67:40)
[W3C] at GeneratorFunctionPrototype.invoke [as _invoke] (/home/linuxbrew/.linuxbrew/lib/node_modules/appium/node_modules/babel-runtime/regenerator/runtime.js:315:22)
[W3C] at GeneratorFunctionPrototype.prototype.(anonymous function) [as throw] (/home/linuxbrew/.linuxbrew/lib/node_modules/appium/node_modules/babel-runtime/regenerator/runtime.js:100:21)
[W3C] at GeneratorFunctionPrototype.invoke (/home/linuxbrew/.linuxbrew/lib/node_modules/appium/node_modules/babel-runtime/regenerator/runtime.js:136:37)
[HTTP] <-- POST /wd/hub/session 500 31877 ms - 3846
是否也取决于glibc版本?
答案 0 :(得分:3)
我发现编译后的代码有问题,但是我还没有找到解决方案。但这表明,这只是一个叮叮当当的问题,使用g ++即可解决。
通过显示一些最终的汇编代码可以最好地说明该问题。 auto arr(e);
代码行被编译为一些移动指令,以将数据从向量复制到堆栈中,clang使用(当使用-mavx进行编译时)类似于以下(AT&T语法)的avx2指令:
vmovaps 0xa0(%rax),%ymm0
vmovaps %ymm0,0x120(%rsp)
...
其中%rax是向量中当前数组的地址。目标arr位于0x80(%rsp)。该程序将以32字节的块(256位avx2指令)进行复制。
但是,在调试测试中查看以下值时,问题变得很明显:%rax = 0x55555556be70
。问题是,将vmovaps(移动对齐的压缩后的单精度)移至256位avx2寄存器时,期望目标和源在256bit或32byte(0x20)边界对齐,但是%rax仅16byte对齐。当不使用alignas进行编译时,clang使用vmovups(相同的指令,但不需要对齐数据)。
所以问题是,std :: vector的分配器不遵守alignas,并且不以64byte边界对齐数组。 g ++也不将向量内的数组与32byte边界对齐,并且在不同时使用-O [not 0]时不使用avx指令。但是,g ++始终使用128位xmm寄存器,该寄存器仅需要对齐到16个字节,分配器会将这两个寄存器与两个编译器对齐。
编辑:
我刚刚意识到,我忘记了使用-std = c ++ 17进行编译。带有该标志,适用于clang ++。该代码看起来相同,但是分配器将代码正确地对齐在64字节边界处。所以我想这与一个旧库有关。也许您可以将二进制文件发送给我,然后我可以对其进行更详细的了解。