继续阅读华为(HUAWEI)S1700-8G-AC 非网管8口千兆以太网 交换机 参数
华为(HUAWEI)S1700-8G-AC 非网管8口千兆以太网 交换机 参数
继续阅读华为(HUAWEI)S1700-8G-AC 非网管8口千兆以太网 交换机 参数
最近新入手了 群晖 DS718+
,鉴于群晖断电容易损坏硬盘的传说,为了保证设备的安全,特别购入了 APC BK650-CH UPS
。
APC BK650-CH UPS
有多个电源插槽,可以同时为多个设备提供断电保护,但是 APC BK650-CH UPS
只有一个 USB
设备接口,这个接口用来提供电源状态信息,比如是否断电,当前电量信息等数据。这就造成收到断电通知的只能有一个设备,这个设备必须通过网络通知其他设备,才能实现全部的断电通知。
继续阅读基于APC BK650-CH UPS连接群晖DS718+通过NUT(Network UPS Tools)实现WDMyCloud Gen1断电自动关机
最近在使用 brew
升级应用的时候,报告如下错误:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
$ brew upgrade Warning: You are using macOS 10.11. We (and Apple) do not provide support for this old version. You will encounter build failures with some formulae. Please create pull requests instead of asking for help on Homebrew's GitHub, Discourse, Twitter or IRC. You are responsible for resolving any issues you experience, as you are running this old version. ==> Upgrading 7 outdated packages: doxygen 1.8.14 -> 1.8.15, ffmpeg 4.0.2 -> 4.1.3, ilmbase 2.2.1 -> 2.3.0, libpng 1.6.36 -> 1.6.37, lz4 1.8.3 -> 1.9.0, opencv 3.4.2 -> 4.1.0_1, openexr 2.2.0_1 -> 2.3.0 ==> Upgrading libpng ==> Downloading https://downloads.sourceforge.net/libpng/libpng-1.6.37.tar.xz ==> Downloading from https://jaist.dl.sourceforge.net/project/libpng/libpng16/1. ######################################################################## 100.0% ==> ./configure --disable-silent-rules --prefix=/usr/local/Cellar/libpng/1.6.37 ==> make ==> make test ==> make install ? /usr/local/Cellar/libpng/1.6.37: 27 files, 1.2MB, built in 3 minutes 59 seconds Removing: /usr/local/Cellar/libpng/1.6.36... (28 files, 1.2MB) Removing: /Users/xxxx/Library/Caches/Homebrew/libpng--1.6.36.tar.xz... (0B) ==> Upgrading ffmpeg ==> Installing dependencies for ffmpeg: aom, frei0r, libtasn1, nettle, libffi, p11-kit, libevent, unbound, gnutls, fribidi, ninja, meson, glib, pixman, cairo, gobject-introspection, graphite2, harfbuzz, libass, libbluray, libsoxr, opencore-amr, doxygen, little-cms2, openjpeg, rtmpdump, flac, libsndfile, libsamplerate, rubberband, speex, autoconf-archive, giflib, webp, leptonica, tesseract and x265 ==> Installing ffmpeg dependency: aom ==> Cloning https://aomedia.googlesource.com/aom.git Cloning into '/Users/xxxx/Library/Caches/Homebrew/aom--git'... fatal: unable to access 'https://aomedia.googlesource.com/aom.git/': Failed to connect to aomedia.googlesource.com port 443: Operation timed out Error: An exception occurred within a child process: DownloadError: Failed to download resource "aom" Failure while executing; `git clone --branch v1.0.0 https://aomedia.googlesource.com/aom.git /Users/xxxx/Library/Caches/Homebrew/aom--git` exited with 128. Here's the output: Cloning into '/Users/xxxx/Library/Caches/Homebrew/aom--git'... fatal: unable to access 'https://aomedia.googlesource.com/aom.git/': Failed to connect to aomedia.googlesource.com port 443: Operation timed out |
众所周知的原因,很早之前,就已经不能访问 Google
的服务器了,因此这个错误是正常现象。
解决方法如下:
1 2 3 4 5 6 7 8 |
$ wget https://raw.githubusercontent.com/Homebrew/homebrew-core/master/Formula/aom.rb # 用本站提供的一份代码拷贝来替代原来的地址 $ sed -i "" "s/https:\/\/aomedia\.googlesource\.com\/aom\.git/https:\/\/www.mobibrw.com\/wp-content\/uploads\/2019\/04\/aom.zip/g" aom.rb $ brew uninstall --ignore-dependencies aom $ brew install --build-from-source aom.rb --env=std |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 |
$ brew upgrade cmake Updating Homebrew... Warning: You are using macOS 10.11. We (and Apple) do not provide support for this old version. You will encounter build failures with some formulae. Please create pull requests instead of asking for help on Homebrew's GitHub, Discourse, Twitter or IRC. You are responsible for resolving any issues you experience, as you are running this old version. ==> Upgrading 1 outdated package: cmake 3.12.0 -> 3.14.2 ==> Upgrading cmake ==> Downloading https://github.com/Kitware/CMake/releases/download/v3.14.2/cmake Already downloaded: /Users/xxxx/Library/Caches/Homebrew/downloads/348cde1c4453dba1e5e72e9bb6ae7cd45fffb907d265cd06efd513841eda8541--cmake-3.14.2.tar.gz ==> ./bootstrap --prefix=/usr/local/Cellar/cmake/3.14.2 --no-system-libs --paral ==> make Last 15 lines from /Users/xxxx/Library/Logs/Homebrew/cmake/02.make: [ 23%] Building C object Source/CursesDialog/form/CMakeFiles/cmForm.dir/frm_req_name.c.o [ 23%] Building C object Source/CursesDialog/form/CMakeFiles/cmForm.dir/frm_scale.c.o [ 24%] Building C object Source/CursesDialog/form/CMakeFiles/cmForm.dir/frm_sub.c.o [ 24%] Building C object Source/CursesDialog/form/CMakeFiles/cmForm.dir/frm_user.c.o [ 24%] Building C object Source/CursesDialog/form/CMakeFiles/cmForm.dir/frm_win.c.o [ 24%] Building C object Source/CursesDialog/form/CMakeFiles/cmForm.dir/fty_alnum.c.o [ 24%] Building C object Source/CursesDialog/form/CMakeFiles/cmForm.dir/fty_alpha.c.o [ 24%] Building C object Source/CursesDialog/form/CMakeFiles/cmForm.dir/fty_enum.c.o [ 24%] Building C object Source/CursesDialog/form/CMakeFiles/cmForm.dir/fty_int.c.o [ 25%] Building C object Source/CursesDialog/form/CMakeFiles/cmForm.dir/fty_ipv4.c.o [ 25%] Building C object Source/CursesDialog/form/CMakeFiles/cmForm.dir/fty_num.c.o [ 25%] Building C object Source/CursesDialog/form/CMakeFiles/cmForm.dir/fty_regex.c.o [ 25%] Linking C static library libcmForm.a [ 25%] Built target cmForm make: *** [all] Error 2 Do not report this issue to Homebrew/brew or Homebrew/core! Error: You are using macOS 10.11. We (and Apple) do not provide support for this old version. You will encounter build failures with some formulae. Please create pull requests instead of asking for help on Homebrew's GitHub, Discourse, Twitter or IRC. You are responsible for resolving any issues you experience, as you are running this old version. |
查看日志/Users/xxxx/Library/Logs/Homebrew/cmake/02.make
,内容如下:
struts2 从如下版本:
1 2 |
<struts2.version>2.5.10.1</struts2.version> <apache.commons.version>3.5</apache.commons.version> |
升级到
1 |
<struts2.version>2.5.20</struts2.version> |
之后报错如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 |
四月 13, 2019 10:07:59 上午 org.apache.catalina.core.StandardWrapperValve invoke 严重: Servlet.service() for servlet [default] in context with path [] threw exception [Filter execution threw an exception] with root cause java.lang.NoSuchMethodError: org.apache.commons.lang3.reflect.MethodUtils.getAnnotation(Ljava/lang/reflect/Method;Ljava/lang/Class;ZZ)Ljava/lang/annotation/Annotation; at org.apache.struts2.interceptor.validation.AnnotationValidationInterceptor.doIntercept(AnnotationValidationInterceptor.java:44) at com.opensymphony.xwork2.interceptor.MethodFilterInterceptor.intercept(MethodFilterInterceptor.java:99) at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:249) at com.opensymphony.xwork2.interceptor.ConversionErrorInterceptor.doIntercept(ConversionErrorInterceptor.java:142) at com.opensymphony.xwork2.interceptor.MethodFilterInterceptor.intercept(MethodFilterInterceptor.java:99) at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:249) at com.opensymphony.xwork2.interceptor.ParametersInterceptor.doIntercept(ParametersInterceptor.java:137) at com.opensymphony.xwork2.interceptor.MethodFilterInterceptor.intercept(MethodFilterInterceptor.java:99) at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:249) at com.opensymphony.xwork2.interceptor.ParametersInterceptor.doIntercept(ParametersInterceptor.java:137) at com.opensymphony.xwork2.interceptor.MethodFilterInterceptor.intercept(MethodFilterInterceptor.java:99) at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:249) at com.opensymphony.xwork2.interceptor.StaticParametersInterceptor.intercept(StaticParametersInterceptor.java:201) at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:249) at org.apache.struts2.interceptor.MultiselectInterceptor.intercept(MultiselectInterceptor.java:67) at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:249) at org.apache.struts2.interceptor.DateTextFieldInterceptor.intercept(DateTextFieldInterceptor.java:133) at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:249) at org.apache.struts2.interceptor.CheckboxInterceptor.intercept(CheckboxInterceptor.java:89) at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:249) at org.apache.struts2.interceptor.FileUploadInterceptor.intercept(FileUploadInterceptor.java:243) at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:249) at com.opensymphony.xwork2.interceptor.ModelDrivenInterceptor.intercept(ModelDrivenInterceptor.java:101) at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:249) at com.opensymphony.xwork2.interceptor.ScopedModelDrivenInterceptor.intercept(ScopedModelDrivenInterceptor.java:142) at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:249) at com.opensymphony.xwork2.interceptor.ChainingInterceptor.intercept(ChainingInterceptor.java:160) at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:249) at com.opensymphony.xwork2.interceptor.PrepareInterceptor.doIntercept(PrepareInterceptor.java:175) at com.opensymphony.xwork2.interceptor.MethodFilterInterceptor.intercept(MethodFilterInterceptor.java:99) at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:249) at org.apache.struts2.interceptor.I18nInterceptor.intercept(I18nInterceptor.java:121) at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:249) at org.apache.struts2.interceptor.ServletConfigInterceptor.intercept(ServletConfigInterceptor.java:167) at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:249) at com.opensymphony.xwork2.interceptor.AliasInterceptor.intercept(AliasInterceptor.java:203) at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:249) at com.opensymphony.xwork2.interceptor.ExceptionMappingInterceptor.intercept(ExceptionMappingInterceptor.java:196) at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:249) at org.apache.struts2.factory.StrutsActionProxy.execute(StrutsActionProxy.java:48) at org.apache.struts2.dispatcher.Dispatcher.serviceAction(Dispatcher.java:574) at org.apache.struts2.dispatcher.ExecuteOperations.executeAction(ExecuteOperations.java:79) at org.apache.struts2.dispatcher.filter.StrutsPrepareAndExecuteFilter.doFilter(StrutsPrepareAndExecuteFilter.java:141) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:241) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208) at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:219) at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:110) at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:494) at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:169) at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:104) at org.apache.catalina.valves.AccessLogValve.invoke(AccessLogValve.java:1025) at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:116) at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:445) at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1137) at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:637) at org.apache.tomcat.util.net.JIoEndpoint$SocketProcessor.run(JIoEndpoint.java:317) at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615) at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) at java.lang.Thread.run(Thread.java:745) |
解决方法为升级
1 2 3 4 5 |
<dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-lang3</artifactId> <version>${apache.commons.version}</version> </dependency> |
到更高的版本,如下:
1 2 |
<struts2.version>2.5.20</struts2.version> <apache.commons.version>3.8.1</apache.commons.version> |
java.lang.NoSuchMethodError: org.apache.commons.lang3.math.NumberUtils.isCreatable(Ljava/lang/String
RAID 即廉价磁盘冗余阵列,其高可用性和可靠性适用于大规模环境中,相比正常使用,数据更需要被保护。RAID 是将多个磁盘整合的大磁盘,不仅具有存储功能,同时还有数据保护功能.
软件磁盘整列通过mdadm命令创建.
在编译陈硕github上面的代码时,遇到了链接错误:
1 2 3 4 5 6 7 8 9 10 |
../Thread.o: In function `(anonymous namespace)::ThreadNameInitializer::ThreadNameInitializer()': Thread.cpp:(.text+0x5f): undefined reference to `pthread_atfork' ../Thread.o: In function `muduo::Thread::~Thread()': Thread.cpp:(.text+0x3f9): undefined reference to `pthread_detach' ../Thread.o: In function `muduo::Thread::start()': Thread.cpp:(.text+0x50e): undefined reference to `pthread_create' ../Thread.o: In function `muduo::Thread::join()': Thread.cpp:(.text+0x5ce): undefined reference to `pthread_join' collect2: error: ld returned 1 exit status |
在编译时已经加了-lpthread,还是有这个问题。
在这里找到了答案:http://stackoverflow.com/questions/2373109/what-library-to-be-to-be-used-to-avoid-undefined-reference-to-pthread-atfork
pthread_atfork是POSIX标准,在编译时要加上-pthread
-lpthread是老版本的gcc编译器用的,在新版本中应该用-pthread取代-lpthread
解决undefined reference to pthread_atfork
/pthread_detach
/pthread_create
/pthread_join
搭建群晖交叉编译环境(DS718+/ubuntu 16.04.6/DSM 6.2.1-23824 Update 6),以 tcl 8.4.19/expect 5.45.4/dos2unix 7.4.0的编译为例,如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 |
$ sudo apt-get -y install git $ mkdir -p toolkit $ cd toolkit $ git clone https://github.com/SynologyOpenSource/pkgscripts-ng.git # 如果访问github存在困难,可用本站下载一份代码拷贝 # wget https://www.mobibrw.com/wp-content/uploads/2019/04/pkgscripts-ng.zip $ cd pkgscripts-ng $ sudo ./EnvDeploy -v 6.2 -p x64 # 如果已经通过其他途径下载,或者已经下载完成,执行如下命令 # `sudo ./EnvDeploy -v 6.2 -p x64 -t ../toolkit_tarballs/` # 比如: # wget https://www.mobibrw.com/wp-content/uploads/2019/04/base_env-6.2.txz -O ../toolkit_tarballs/ # wget https://www.mobibrw.com/wp-content/uploads/2019/04/ds.x64-6.2.dev.txz -O ../toolkit_tarballs/ # wget https://www.mobibrw.com/wp-content/uploads/2019/04/ds.x64-6.2.env.txz -O ../toolkit_tarballs/ # sudo ./EnvDeploy -v 6.2 -p x64 -t ../toolkit_tarballs/ # 下载 TCL 8.4.19 (不要超过这个版本号,否则 expect 链接不通过) $ sudo wget https://sourceforge.net/projects/tcl/files/Tcl/8.4.19/tcl8.4.19-src.tar.gz -O ../build_env/ds.x64-6.2/root/tcl8.4.19-src.tar.gz # 也可本站下载 sudo wget https://www.mobibrw.com/wp-content/uploads/2019/04/tcl8.4.19-src.tar.gz -O ../build_env/ds.x64-6.2/root/tcl8.4.19-src.tar.gz # 下载 Expect 5.45.4 $ sudo wget https://sourceforge.net/projects/expect/files/Expect/5.45.4/expect5.45.4.tar.gz -O ../build_env/ds.x64-6.2/root/expect5.45.4.tar.gz # 也可以本站下载 sudo wget https://www.mobibrw.com/wp-content/uploads/2019/04/expect5.45.4.tar.gz -O ../build_env/ds.x64-6.2/root/expect5.45.4.tar.gz $ sudo chroot ../build_env/ds.x64-6.2/ $ cd root $ export PREFIX="/root/build_libs" $ export HOST="x86_64-linux-gnu" $ mkdir -p $PREFIX # 编译 TCL 8.4.19 $ tar xvf tcl8.4.19-src.tar.gz $ cd tcl8.4.19 $ PKG_CONFIG_PATH=$PREFIX/lib/pkgconfig/ \ LD_LIBRARY_PATH=$PREFIX/lib/ \ ./unix/configure \ --build=`dpkg-architecture -qDEB_BUILD_GNU_TYPE` \ --host=$HOST \ --prefix=$PREFIX \ --disable-shared $ make clean $ make $ make install $ cd .. # 编译 Expect 5.45.4 $ tar xvf expect5.45.4.tar.gz $ cd expect5.45.4 $ CPPFLAGS="-I$PREFIX/include" $ PKG_CONFIG_PATH=$PREFIX/lib/pkgconfig/ \ LD_LIBRARY_PATH=$PREFIX/lib/ \ LDFLAGS="-lutil" \ ./configure \ --build=`dpkg-architecture -qDEB_BUILD_GNU_TYPE` \ --host=$HOST \ --prefix=$PREFIX \ --enable-static \ --disable-shared $ make clean $ make $ make install $ cd .. # 下载 dos2unix 7.4 # 也可本站下载 https://www.mobibrw.com/wp-content/uploads/2019/04/dos2unix-7.4.0.tar.gz $ sudo wget https://sourceforge.net/projects/dos2unix/files/dos2unix/7.4.0/dos2unix-7.4.0.tar.gz ../build_env/ds.x64-6.2/root/dos2unix-7.4.0.tar.gz $ tar xvf dos2unix-7.4.0.tar.gz $ cd dos2unix-7.4.0 $ make # 下载 sshpass 1.06 # 也可本站下载 https://www.mobibrw.com/wp-content/uploads/2019/04/sshpass_1.06.orig_.tar.gz $ sudo wget http://deb.debian.org/debian/pool/main/s/sshpass/sshpass_1.06.orig.tar.gz ../build_env/ds.x64-6.2/root/sshpass_1.06.orig.tar.gz $ tar xvf sshpass_1.06.orig.tar.gz $ cd sshpass-1.06 $ make |
对于编译好的程序,复杂的可以自己制作安装包,简单的可以直接通过SSH推送到设备的/usr/local/bin目录下即可。
比如本次编译结果:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
$ scp -r $PREFIX/lib/tcl8.4 username@10.10.10.111:~/ $ scp $PREFIX/bin/expect username@10.10.10.111:~/ $ scp /root/dos2unix-7.4.0/dos2unix username@10.10.10.111:~/ $ scp /root/sshpass-1.06/sshpass username@10.10.10.111:~/ $ ssh 10.10.10.111 -l username $ sudo mv ~/tcl8.4 /usr/local/lib/ $ sudo mv ~/expect /usr/local/bin/ $ sudo mv ~/dos2unix /usr/local/bin/ $ sudo mv ~/sshpass /usr/local/bin/ |
摘要:本文通过一个真实案例(4096点双精度浮点复数点积算法),描述了使用 Zynq-7000 NEON进行算法优化的过程以及一些关键技巧,相对于使用编译器对C代码做优化,性能提升了大约4.8倍。 本文介绍的内容对需要用到NEON实现高性能计算的开发者非常有帮助。
开发工具:Xilinx SDK 2013.4
开发板: Xilinx ZC702或者ZC706
一般来说,使用NEON优化算法有以下几种方式:
> 用现成的开源库,例如Ne10,FFTW等等
> 使用编译器的auto vectorization功能,通过配置合适的编译选项让编译器来生成NEON代码
> 使用NEON Intrinsic
> 使用NEON汇编
前三种方式简单易用,但是受限于编译器的优化能力,往往性能无法达到极致。在需要使用NEON进行高性能计算的时候,就需要用手工汇编来进行优化 了。用汇编语言写程序看起来很让人望而生畏,但实际上在一个算法里面需要高度优化的核心部分并不会太大,只要静下心来,掌握一些书写NEON汇编的技巧, 完全可以轻松的完成核心汇编程序。
下面我们来看一个例子:求两个矢量(vector)的点积(dot product),矢量的长度为4096,矢量的数据为复数(complex),复数的实部和虚部都是双精度浮点类型。,每个矢量占用内存64KB。
对Zynq-7000芯片来说,数据可以保存在片内高速SRAM(OCM)中,也可以保存在DDR3 memory中。我们首先以数据保存在OCM中为例,最后会讨论数据保存在DDR3中的情况。
1. 使用编译器的auto vectorization进行优化:
对这个算法来说,C语言的标准实现大致是这样的。
1 2 3 4 5 6 7 8 9 10 11 12 |
double a[N*2] ; double b[N*2] ; double result_golden[2] = {0.0, 0.0}; int i; result_golden[0]=0.0; result_golden[1]=0.0; for(i = 0; i < 4096; i++) { result_golden[0] += a[2 * i] * b[2 * i] - a[2 * i + 1] * b[2 * i + 1]; result_golden[1] += a[2 * i + 1] * b[2 * i] + a[2 * i] * b[2 * i + 1]; } |
我们使用以下编译选项来让编译器针对NEON进行优化。
1 |
-O2-mcpu=cortex-a9 -mfpu=neon -ftree-vectorize -mvectorize-with-neon-quad -mfloat-abi=softfp -ffast-math |
我们将这个算法运行10次,得到平均的执行时间为289.692us。缺省情况下,CPU的主频为667MHz,换算成CPU cycles,执行时时间为193128 cycles。将这个数字除以矢量长度4096,我们就得到每个双精度复数乘加需要的平均时间为47.14 CPU cycles.
通过上面的源码,我们可以看出每个复数乘加涉及到3次双精度浮点数的乘加(MLA)运算和1次双精度浮点数的乘减(MLS)运算。查询 ARM Cortex™-A9 NEON™ Media Processing Engine Technical Reference Manual文档,我们注意到对双精度浮点数,在理想情况下,MLA/MLS指令需要2个CPU cycles,即理论上执行一次复数乘加需要的时间为8 CPU cycles。
即使我们把数据在memory和NEON registers之间传输的时间也考虑进去,每个复数乘加的实测执行时间和理论执行时间之间的差距也是相当大的。这样,我们可以初步得出结论:在这个算 法上,编译器的优化能力离性能极限还有很大差距,通过适当的汇编优化,还有很大的性能提升空间。
2. 如何实现最优的指令序列
我们先来做个实验,测试一下以下函数的执行时间。在这个函数的循环体里面,有4条vmla.f64指令。测试完成后,我们从后往前每次注释掉一条vmla.f64指令,然后测试实际的执行时间,直到最后只剩1条vmla.f64指令为止。
1 2 3 4 5 6 7 8 9 10 11 12 13 |
.align 4 .global neon_instr_seq_0 .arm neon_instr_seq_0: LDR r0, =16384 neon_instr_seq_0_loop: vmla.f64 d8, d0, d4 vmla.f64 d9, d1, d5 vmla.f64 d10, d2, d6 vmla.f64 d11, d3, d7 SUBS r0, r0, #1 BNE neon_instr_seq_0_loop bx lr |
这样我们就得到了以下的表格
函数的循环体里面vmla.f64指令的数目 | 每次循环需要的CPU cycles | 平均每条NEON指令需要的CPU cycles |
4 | 8 | 2 |
3 | 6 | 2 |
2 | 6 | 3 |
1 | 6 | 6 |
从这个表格,我们可以看出:因为CPU内部微架构的原因,对一个NEON指令的目的寄存器写入后,要等待一段时间再执行下一次写入操作,否则会导致 pipeline stall导致性能下降。对双精度浮点数,相邻的三个NEON指令的目的寄存器必须是不同的。值得注意的是,对其他数据类型,例如单精度浮点数或者32- bit整数,结论会略有差异。
一般来说,在memory和register之间用VLDM/VSTM交换数据会有比较高的效率。对NEON来说,共有32个D寄存器,这样我们对 每个矢量每次可以加载4个复数(即8个双精度浮点数),其中D0-D7保存矢量1的数据,D8-D15保存矢量2的数据,其他寄存器用于保存中间结果。按 照这个思路,比较直接的汇编写法是:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
.align 4 .global neon_instr_seq_20 .arm neon_instr_seq_20: LDR r0, =16384 neon_instr_seq_20_loop: vmla.f64 d16, d0, d8 vmla.f64 d17, d0, d9 vmls.f64 d16, d1, d9 vmla.f64 d17, d1, d8 vmla.f64 d18, d2, d10 vmla.f64 d19, d2, d11 vmls.f64 d18, d3, d11 vmla.f64 d19, d3, d10 vmla.f64 d20, d4, d12 vmla.f64 d21, d4, d13 vmls.f64 d20, d5, d13 vmla.f64 d21, d5, d12 vmla.f64 d22, d6, d14 vmla.f64 d23, d6, d15 vmls.f64 d22, d7, d15 vmla.f64 d23, d7, d14 SUBS r0, r0, #1 BNE neon_instr_seq_20_loop bx lr |
每次循环使用到了16条FPU指令,理论上需要32 CPU cycles,实测每次循环需要40 CPU cycles。这也印证了一开始我们通过测试得出的结论,所以我们要通过重新排布指令的顺序来取得更好的性能。
重新排布后指令顺序如下所示。实测每次循环需要32 CPU cycles,这段代码达到了最优化。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
.align 4 .global neon_instr_seq_21 .arm neon_instr_seq_21: LDR r0, =16384 neon_instr_seq_21_loop: vmla.f64 d16, d0, d8 vmla.f64 d17, d0, d9 vmla.f64 d18, d2, d10 vmla.f64 d19, d2, d11 vmla.f64 d20, d4, d12 vmla.f64 d21, d4, d13 vmla.f64 d22, d6, d14 vmla.f64 d23, d6, d15 vmls.f64 d16, d1, d9 vmla.f64 d17, d1, d8 vmls.f64 d18, d3, d11 vmla.f64 d19, d3, d10 vmls.f64 d20, d5, d13 vmla.f64 d21, d5, d12 vmls.f64 d22, d7, d15 vmla.f64 d23, d7, d14 SUBS r0, r0, #1 BNE neon_instr_seq_21_loop bx lr |
对上面的代码,使用到的目的寄存器为8个,结合最开始的测试结论,我们只需要4个目的寄存器就够了,所以可以把上面的代码改写成下面的形式。实测每次循环需要32 CPU cycles,性能仍然是最优。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
.align 4 .global neon_instr_seq_22 .arm neon_instr_seq_22: LDR r0, =16384 neon_instr_seq_22_loop: vmla.f64 d16, d0, d8 vmla.f64 d17, d0, d9 vmla.f64 d18, d2, d10 vmla.f64 d19, d2, d11 vmla.f64 d16, d4, d12 vmla.f64 d17, d4, d13 vmla.f64 d18, d6, d14 vmla.f64 d19, d6, d15 vmls.f64 d16, d1, d9 vmla.f64 d17, d1, d8 vmls.f64 d18, d3, d11 vmla.f64 d19, d3, d10 vmls.f64 d16, d5, d13 vmla.f64 d17, d5, d12 vmls.f64 d18, d7, d15 vmla.f64 d19, d7, d14 SUBS r0, r0, #1 BNE neon_instr_seq_22_loop bx lr |
3. 如何优化使用VLDM
比较直观的使用VLDM指令后的汇编如下所示。实测每次循环需要45 CPU cycles.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
.align 4 .global neon_instr_seq_23 .arm neon_instr_seq_23: LDR r3, =16384 neon_instr_seq_23_loop: vldm r0, {d0-d7} vldm r1, {d8-d15} vmla.f64 d16, d0, d8 vmla.f64 d17, d0, d9 vmla.f64 d18, d2, d10 vmla.f64 d19, d2, d11 vmla.f64 d16, d4, d12 vmla.f64 d17, d4, d13 vmla.f64 d18, d6, d14 vmla.f64 d19, d6, d15 vmls.f64 d16, d1, d9 vmla.f64 d17, d1, d8 vmls.f64 d18, d3, d11 vmla.f64 d19, d3, d10 vmls.f64 d16, d5, d13 vmla.f64 d17, d5, d12 vmls.f64 d18, d7, d15 vmla.f64 d19, d7, d14 SUBS r3, r3, #1 BNE neon_instr_seq_23_loop bx lr |
在这里我们可以做一个有趣的实验,我们可以注释掉任意一条VLDM指令,然后测试每次循环的执行时间。实测的结果是33 CPU cycles。这是一件很有意思的事情。在Cortex-A9内核里面,NEON/FPU是一个独立的处理单元,Load/Store Unit也是一个独立处理单元,两个单元可以并行执行。Load/Store Unit可以在加载到第一个数据的时候立即把数据forward给NEON/FPU,这样在只有一条VLDM指令时,只需要引入1个CPU cycles的延迟了。这就提示我们通过合理的打散VLDM指令并和NEON/FPU指令交织(interleave),可以提升指令执行的并行度,并继 续提升软件的性能。
到了这个层面,就需要对CPU的微架构(micro architecture)有比较深入的了解了。不过对于软件工程师,没有必要在这方面花太多的时间,只需要注意一些基本的原则,反复尝试几种可能的组合就好了:
> 在VLDR/VLDM和使用到相关寄存器的数据处理指令之间留出足够的时间,避免pipeline stall
> VLDM指令能够快速的把数据forward给寄存器,所以VLDM和数据处理指令之间不要有其他load/store指令
按照上面的原则,我们对代码的顺序重新调整,得到了下面的汇编代码。实测每次循环的执行时间从45 CPU cycles缩短为39 CPU cycles。
值得注意的是在这里我们假定数据已经在L1 cache里面了。在实际中,数据要从memory加载到L1 cache中,然后再传到NEON register里面,这个过程非常复杂。通过上述方法找到的最优指令序列在真实环境中有可能未必是最优的,这时就需要测试几个在这种简单情况下最优和次 优的指令序列在真实环境中的性能,从而找出真实环境中性能最优的代码。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 |
.align 4 .global neon_instr_seq_35 .arm neon_instr_seq_35: LDR r3, =16384 neon_instr_seq_35_loop: vldr d0, [r0, #0] vldm r1, {d8-d15} vmla.f64 d16, d0, d8 vldr d2, [r0, #16] vmla.f64 d17, d0, d9 vldr d4, [r0, #32] vmla.f64 d18, d2, d10 vmla.f64 d19, d2, d11 vldr d6, [r0, #48] vmla.f64 d16, d4, d12 vmla.f64 d17, d4, d13 vldr d1, [r0, #8] vmla.f64 d18, d6, d14 vmla.f64 d19, d6, d15 vldr d3, [r0, #24] vmls.f64 d16, d1, d9 vmla.f64 d17, d1, d8 vldr d5, [r0, #40] vmls.f64 d18, d3, d11 vmla.f64 d19, d3, d10 vldr d7, [r0, #56] vmls.f64 d16, d5, d13 vmla.f64 d17, d5, d12 vmls.f64 d18, d7, d15 vmla.f64 d19, d7, d14 SUBS r3, r3, #1 BNE neon_instr_seq_35_loop bx lr |
4. 使用PLD指令优化性能
上面的讨论都假设数据已经存在于L1 cache中了。在真实的环境中未必会这样,这时就需要用PLD指令提前把数据加载到L1 cache中。算法计算量越大,PLD指令对性能的提升也越明显。
使用PLD指令需要注意:
PLD指令只能加载一个cache line,对Cortex-A9来说一个cache line为32 bytes。
Cortex-A9硬件限制最多有4条PLD指令并行执行,过多的插入PLD指令不会有额外的性能提升。
增加了PLD指令后的完整的汇编代码如下所示:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 |
.align 4 .global complex_dot_product_neon_var4 .arm complex_dot_product_neon_var4: @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ @ r0: address of source vector a @ r1: address of source vector b @ r2: address of destination complex @ r3: vector length @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ vmov.i32 d16, #0 vmov.i32 d17, #0 vmov.i32 d18, #0 vmov.i32 d19, #0 loop_var4: vldr d0, [r0, #0] vldm r1!, {d8-d15} pld [r0, #64] pld [r0, #96] pld [r1, #64] pld [r1, #96] vmla.f64 d16, d0, d8 vldr d2, [r0, #16] vmla.f64 d17, d0, d9 vldr d4, [r0, #32] vmla.f64 d18, d2, d10 vmla.f64 d19, d2, d11 vldr d6, [r0, #48] vmla.f64 d16, d4, d12 vmla.f64 d17, d4, d13 vldr d1, [r0, #8] vmla.f64 d18, d6, d14 vmla.f64 d19, d6, d15 vldr d3, [r0, #24] vmls.f64 d16, d1, d9 vmla.f64 d17, d1, d8 vldr d5, [r0, #40] vmls.f64 d18, d3, d11 vmla.f64 d19, d3, d10 vldr d7, [r0, #56] vmls.f64 d16, d5, d13 vmla.f64 d17, d5, d12 vmls.f64 d18, d7, d15 vmla.f64 d19, d7, d14 add r0, r0, #64 SUBS r3, r3, #4 BNE loop_var4 vadd.f64 d16, d16, d18 vadd.f64 d17, d17, d19 vstm r2!, {d16-d17} bx lr |
5. 测试结果及分析:
数据在OCM和DDR3上的执行结果为:
测试项 | 在OCM上的执行时间 | 在DDR3上的执行时间 |
Compiler Auto-vectorization | 289.677 | 272.526 |
asm_1 | 175.563 | 396.615 |
asm_1 with PLD | 70.203 | 180.765 |
asm_2 | 147.861 | 264.484 |
asm_2 with PLD | 60.522 | 170.733 |
> Compiler Auto-vectorization为本文最开始的C代码通过GCC编译器优化后的测试
> asm_1为不考虑第3步VLDM/VLDR和NEON数据处理指令交织的汇编代码
> asm_2为考虑到第3步VLDM/VLDR和NEON数据处理指令交织后的汇编代码
从测试结果来看,我们可以注意到几点:
> 充分优化后的代码(asm_2 with PLD)在OCM上运行的结果非常接近NEON的最优性能。NEON数据处理指令需要32 CPU cycles,根据Zynq-7000 TRM, OCM的latency为23 cycles。这时PLD指令可以在处理当前加载的数据的同时,提前把下一批要处理的数据加载到L1 cache中。
> 对代码(asm_2 with PLD)来说,数据在DDR3上的性能要差于在OCM上的性能。这个也比较好理解,因为DDR3的latency要远大于NEON数据处理指令执行时间 (32 CPU cycles),在整个运算过程中,PLD指令并不能有效的提前把下一批待处理的数据加载到L1 cache中。或者说,只有部分数据能够被提前加载到L1 cache中。
> NEON数据处理指令执行时间越长,就越容易抵消掉内存系统延迟带来的影响。
> 数据在OCM上时,系统性能要优于数据在DDR3上。不过这时还要额外考虑数据在OCM上搬进搬出的开销。当然,如果在系统设计时就考虑到这一点,由硬件直接使用 OCM,系统性能就能够得到明显的提升。
6. 小结
通过这个真实案例,我们演示了一些NEON/FPU汇编开发的技巧。通过活学活用这些技巧,我们就能充分发挥Zynq-7000的计算能力,实现一个高度优化的高性能嵌入式系统。
7.5.1. The PLD instruction -- DDI0434C_cortex_a5_mpcore_trm