草庐IT

c++ - ARM NEON的编码: How to start?

coder 2023-06-02 原文

背景(如果愿意,请跳过此步骤)

首先,我说我不是专家程序员。我是一名年轻的初级计算机视觉(CV)工程师,并且在C++编程方面经验丰富,主要是因为广泛使用了出色的OpenCV2 C++ API。我所学到的一切都是通过执行项目,解决问题和按时完成的需求,因为这是行业中的现实。

最近,我们开始开发用于嵌入式系统(ARM板)的CV软件,并且使用纯C++优化的代码来实现。但是,由于与传统计算机相比资源有限,因此在这种体系结构中构建实时CV系统是一个巨大的挑战。

那就是我发现NEON的时候。我已经阅读了很多有关此的文章,但这是一个相当新的主题,因此没有太多的信息,而且阅读的越多,我就越困惑。

问题

我希望使用NEON一次计算4或8个数组元素的功能来优化C++代码(主要是一些用于循环的代码)。在C++环境中是否可以使用某种库或一组函数?我感到困惑的主要原因是,我看到的几乎所有代码段都在汇编语言中,而我对此绝对没有任何背景,因此现在负担不起学习的费用。
我在Linux Gentoo中使用Eclipse IDE编写C++代码。

更新

阅读答案后,我对该软件进行了一些测试。我用以下标志编译了我的项目:

-O3 -mcpu=cortex-a9 -ftree-vectorize -mfloat-abi=hard -mfpu=neon 

请记住,该项目包括广泛的库,例如openframeworks,OpenCV和OpenNI,并且所有内容都使用这些标志进行了编译。
为了使用ARM板进行编译,我们使用Linaro工具链交叉编译器,GCC的版本为4.8.3。
您希望这会改善项目的绩效吗?因为我们完全没有经历任何变化,考虑到我在这里阅读的所有答案,这还是很奇怪的。

另一个问题:所有for循环都有明显的iteraton,但是其中许多循环遍历自定义数据类型(结构或类)。即使GCC可以循环访问自定义数据类型,也可以优化这些循环吗?

最佳答案

编辑:

从更新中,您可能会误解NEON处理器的功能。它是一个SIMD(单指令多数据) vector 处理器。这意味着在同一时间对多条数据执行一条指令(例如“乘以4”)非常好。它还喜欢做诸如“将所有这些数字加在一起”或“将这两个数字列表中的每个元素相加以创建第三个数字列表”之类的事情。因此,如果您遇到的问题看起来像是NEON处理器将为您带来巨大的帮助。

为了获得这种好处,您必须将数据以非常特定的格式放置,以便 vector 处理器可以同时加载多个数据,并行处理它们,然后同时将它们写回。您需要组织一些事情,使数学避免大多数条件(因为过早查看结果意味着往返NEON)。 vector 编程是对程序进行思考的另一种方式。全部与管道管理有关。

现在,对于许多非常常见的问题,编译器可以自动解决所有这些问题。但这仍然是处理数字以及特定格式的数字。例如,您几乎总是需要将所有数字放入内存中的连续块中。如果您要处理结构和类内部的字段,那么NEON并不能真正为您提供帮助。它不是通用的“并行处理”引擎。这是用于执行并行数学运算的SIMD处理器。

对于非常高性能的系统,数据格式就是一切。您不采用任意数据格式(结构,类等),而是尝试使其快速。您找出可以让您进行最并行工作的数据格式,然后围绕该代码编写代码。您使数据连续。您不惜一切代价避免内存分配。但这实际上不是一个简单的StackOverflow问题可以解决的问题。高性能编程是一种整体技能,是一种不同的思维方式。找到正确的编译器标志并不是您所能获得的。如您所见,默认值已经相当不错了。

您应该问的真正问题是,是否可以重新组织数据,以便可以使用更多的OpenCV。 OpenCV已经有许多优化的并行操作,几乎可以肯定会充分利用NEON。您要尽可能保持OpenCV使用的格式的数据。这可能是您获得最大改进的地方。

我的经验是,肯定有可能手工编写击败clang和gcc的NEON程序集(至少从几年前开始,尽管编译器肯定会继续改进)。出色的ARM优化与NEON优化不同。就像@Mats指出的那样,编译器通常会在明显的情况下做得很好,但并不总是在理想情况下都能处理所有情况,而且即使是技术娴熟的开发人员有时也有可能击败它,有时甚至会打败它。 (@wallyk也是正确的,因为手动调校程序集可以最好地保存为最后;但是它仍然非常强大。)

就是说,考虑到您的发言“我绝对没有任何背景的汇编,并且在这一点上我可能负担不起学习”,那么不,您甚至不必理会。首先,至少要不了解汇编程序(特别是矢量化NEON汇编程序)的基础知识(以及一些非基础知识),然后再去猜测编译器是没有意义的。击败编译器的第一步是知道目标。

如果您愿意学习目标,我最喜欢的介绍是Whirlwind Tour of ARM Assembly。加上下面的一些其他引用,足以让我在遇到特殊问题时比编译器高2-3倍。另一方面,它们不够用,以至于当我向经验丰富的NEON开发人员展示我的代码时,他看了大约三秒钟,然后说:“您在那里停了下来。”真正好的汇编很难,但是半体面的汇编仍然比优化的C++更好。 (同样,每年随着编译器编写者变得更好而变得不那么正确,但这仍然是正确的。)

  • ARM Assembly language
  • A few things iOS developers ought to know about the ARM architecture(以iPhone为重点,但原理对于所有用途都是相同的。)
  • ARM NEON support in the ARM compiler
  • Coding for NEON

  • 旁注my experience with NEON intrinsics很少值得他们去解决。如果您要击败编译器,则需要实际编写完整的程序集。大多数时候,无论您将使用哪种内在函数,编译器都已经知道。掌握权力的地方通常是重组循环以最好地管理管道(而内部函数对此无济于事)。这可能在最近几年有所改善,但是我希望改进的 vector 优化器比其他方法更能胜过内在函数。

    关于c++ - ARM NEON的编码: How to start?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/28547697/

    有关c++ - ARM NEON的编码: How to start?的更多相关文章

    1. ruby - 什么是填充的 Base64 编码字符串以及如何在 ruby​​ 中生成它们? - 2

      我正在使用的第三方API的文档状态:"[O]urAPIonlyacceptspaddedBase64encodedstrings."什么是“填充的Base64编码字符串”以及如何在Ruby中生成它们。下面的代码是我第一次尝试创建转换为Base64的JSON格式数据。xa=Base64.encode64(a.to_json) 最佳答案 他们说的padding其实就是Base64本身的一部分。它是末尾的“=”和“==”。Base64将3个字节的数据包编码为4个编码字符。所以如果你的输入数据有长度n和n%3=1=>"=="末尾用于填充n%

    2. ruby - 用逗号、双引号和编码解析 csv - 2

      我正在使用ruby​​1.9解析以下带有MacRoman字符的csv文件#encoding:ISO-8859-1#csv_parse.csvName,main-dialogue"Marceu","Giveittohimóhe,hiswife."我做了以下解析。require'csv'input_string=File.read("../csv_parse.rb").force_encoding("ISO-8859-1").encode("UTF-8")#=>"Name,main-dialogue\r\n\"Marceu\",\"Giveittohim\x97he,hiswife.\"\

    3. ruby-on-rails - 如何优雅地重启 thin + nginx? - 2

      我的瘦服务器配置了nginx,我的ROR应用程序正在它们上运行。在我发布代码更新时运行thinrestart会给我的应用程序带来一些停机时间。我试图弄清楚如何优雅地重启正在运行的Thin实例,但找不到好的解决方案。有没有人能做到这一点? 最佳答案 #Restartjustthethinserverdescribedbythatconfigsudothin-C/etc/thin/mysite.ymlrestartNginx将继续运行并代理请求。如果您将Nginx设置为使用多个上游服务器,例如server{listen80;server

    4. C# 到 Ruby sha1 base64 编码 - 2

      我正在尝试在Ruby中复制Convert.ToBase64String()行为。这是我的C#代码:varsha1=newSHA1CryptoServiceProvider();varpasswordBytes=Encoding.UTF8.GetBytes("password");varpasswordHash=sha1.ComputeHash(passwordBytes);returnConvert.ToBase64String(passwordHash);//returns"W6ph5Mm5Pz8GgiULbPgzG37mj9g="当我在Ruby中尝试同样的事情时,我得到了相同sha

    5. ruby - 使用 `+=` 和 `send` 方法 - 2

      如何将send与+=一起使用?a=20;a.send"+=",10undefinedmethod`+='for20:Fixnuma=20;a+=10=>30 最佳答案 恐怕你不能。+=不是方法,而是语法糖。参见http://www.ruby-doc.org/docs/ProgrammingRuby/html/tut_expressions.html它说Incommonwithmanyotherlanguages,Rubyhasasyntacticshortcut:a=a+2maybewrittenasa+=2.你能做的最好的事情是:

    6. ruby - 如何计算 Liquid 中的变量 +1 - 2

      我对如何计算通过{%assignvar=0%}赋值的变量加一完全感到困惑。这应该是最简单的任务。到目前为止,这是我尝试过的:{%assignamount=0%}{%forvariantinproduct.variants%}{%assignamount=amount+1%}{%endfor%}Amount:{{amount}}结果总是0。也许我忽略了一些明显的东西。也许有更好的方法。我想要存档的只是获取运行的迭代次数。 最佳答案 因为{{incrementamount}}将输出您的变量值并且不会影响{%assign%}定义的变量,我

    7. ruby-on-rails - 有没有一种工具可以在编码时自动保存对文件的增量更改? - 2

      我最喜欢的Google文档功能之一是它会在我工作时不断自动保存我的文档版本。这意味着即使我在进行关键更改之前忘记在某个点进行保存,也很有可能会自动创建一个保存点。至少,我可以将文档恢复到错误更改之前的状态,并从该点继续工作。对于在MacOS(或UNIX)上运行的Ruby编码器,是否有具有等效功能的工具?例如,一个工具会每隔几分钟自动将Gitcheckin我的本地存储库以获取我正在处理的文件。也许我有点偏执,但这点小保险可以让我在日常工作中安心。 最佳答案 虚拟机有些人可能讨厌我对此的回应,但我在编码时经常使用VIM,它具有自动保存功

    8. c - Ruby - 源代码 - 编码风格 - 2

      查看Ruby代码,它具有以下proc_arity:staticVALUEproc_arity(VALUEself){intarity=rb_proc_arity(self);returnINT2FIX(arity);}更多的是C编码风格问题,但为什么staticVALUE在单独的一行而不是像这样的:staticVALUEproc_arity(VALUEself) 最佳答案 它来自UNIX世界,因为它有助于轻松grep函数的定义:$grep-n'^proc_arity'*.c或使用vim:/^proc_arity

    9. ruby - 如何以编程方式删除实例上的 "singleton information"以使其编码(marshal)? - 2

      我创建了一个由于“在运行时执行的单例元类定义”而无法编码的对象(这段代码的描述是否正确?)。这是通过以下代码执行的:#defineclassXthatmyusesingletonclassmetaprogrammingfeatures#throughcallofmethod:break_marshalling!classXdefbreak_marshalling!meta_class=class我该怎么做才能使对象编码正确?是否可以从对象instance_of_x的classX中“移除”单例组件?我真的需要一个建议,因为我们的一些对象需要通过Marshal.dump序列化机制进行缓存。

    10. arrays - Ruby 数组 += vs 推送 - 2

      我有一个数组数组,想将元素附加到子数组。+=做我想做的,但我想了解为什么push不做。我期望的行为(并与+=一起工作):b=Array.new(3,[])b[0]+=["apple"]b[1]+=["orange"]b[2]+=["frog"]b=>[["苹果"],["橙子"],["Frog"]]通过推送,我将推送的元素附加到每个子数组(为什么?):a=Array.new(3,[])a[0].push("apple")a[1].push("orange")a[2].push("frog")a=>[[“苹果”、“橙子”、“Frog”]、[“苹果”、“橙子”、“Frog”]、[“苹果”、“

    随机推荐