我有以下功能(来自开源项目 "recast navigation" ):
/// Derives the dot product of two vectors on the xz-plane. (@p u . @p v)
/// @param[in] u A vector [(x, y, z)]
/// @param[in] v A vector [(x, y, z)]
/// @return The dot product on the xz-plane.
///
/// The vectors are projected onto the xz-plane, so the y-values are ignored.
inline float dtVdot2D(const float* u, const float* v)
{
return u[0]*v[0] + u[2]*v[2];
}
u[0]*v[0] + u[2]*v[2]是 HitTest 的 CPU。bool dtClosestHeightPointTriangle(const float* p, const float* a, const float* b, const float* c, float& h) {
float v0[3], v1[3], v2[3];
dtVsub(v0, c,a);
dtVsub(v1, b,a);
dtVsub(v2, p,a);
const float dot00 = dtVdot2D(v0, v0);
const float dot01 = dtVdot2D(v0, v1);
const float dot02 = dtVdot2D(v0, v2);
const float dot11 = dtVdot2D(v1, v1);
const float dot12 = dtVdot2D(v1, v2);
// Compute barycentric coordinates
const float invDenom = 1.0f / (dot00 * dot11 - dot01 * dot01);
const float u = (dot11 * dot02 - dot01 * dot12) * invDenom;
const float v = (dot00 * dot12 - dot01 * dot02) * invDenom;
最佳答案
在纸上尝试了一些东西后,我想出了一些可能对你有用的东西。它是 SSE 中该函数的正确并行化/矢量化实现。
然而,它确实需要数据重组,因为我们将同时对 4 个三角形进行并行处理。
我将逐步分解它并在这里和那里使用指令名称,但请使用 C 内在函数(_mm_load_ps()、_mm_sub_ps() 等,它们在 VC 中的 xmmintrin.h 中)——当我说到寄存器时只是意味着__m128。
第1步。
我们根本不需要 Y 坐标,因此我们设置了指向 X 和 Z 对的指针。每次调用至少提供 4 对(即总共 4 个三角形)。 我将把每个 X 和 Z 对称为一个顶点。
第2步。
使用 MOVAPS(要求指针对齐到 16 位)将每个指针指向的前两个顶点加载到寄存器中。
从 a 加载的寄存器将如下所示: [ a0.x, a0.z, a1.x, a1.z ]
第 3 步。
现在,使用单个减法指令,您可以一次计算 2 个顶点的增量(您的 v0、v1、v2)。
不仅要计算前 2 个三角形,还要计算后 2 个三角形的 v0、v1 和 v2!
正如我所说,您应该为每个输入提供总共 4 个顶点或 8 个浮点数。刚刚对该数据重复步骤 2 和 3 .
现在我们有 2 对 vx 寄存器,每对包含 2 个三角形的结果。我将它们称为 vx_0(第一对)和 vx_1(第二对),其中 x 是从 0 到 2。
第四步。
点产品。为了并行化重心计算(稍后),我们需要 4 个三角形中的每一个的每个点积的结果,在 1 个单个寄存器中。
因此,例如,在您计算 dot01 的地方,我们将执行相同的操作,但一次计算 4 个三角形。每个 v 寄存器包含 2 个 vector 的结果,因此我们从将它们相乘开始。
让我们说 u 和 v - 您的点积函数中的参数 - 现在是 v0_0 和 v1_0(至于计算 dot01):
u 和 v 相乘得到:[ (v0_0.x0) * (v1_0.x0), (v0_0.z0) * (v1_0.z0), (v0_0.x1) * (v1_0.x1), (v0_0.z1) * (v1_0.z1) ]
由于 .x0/.z0 和 .x1/.z1,这可能看起来很困惑,但看看第 2 步加载的内容:a0、a1。
如果现在这仍然感觉模糊,请拿起一张纸从头开始写。
接下来,仍然对于相同的点积,对 v0_1 和 v1_1 进行乘法运算( 即第二对三角形 )。
现在我们有 2 个寄存器,每个寄存器有 2 个 X 和 Z 对(总共 4 个顶点),相乘并准备好加在一起形成 4 个单独的点积。 SSE3 有一条指令可以做到这一点,它被称为 HADDPS:
xmm0 = [A、B、C、D]
xmm1 = [E、F、G、H]
HADDPS xmm0, xmm1 这样做:
xmm0 = [A+B、C+D、E+F、G+H]
它将从我们的第一个寄存器中取出 X & Z 对,从第二个寄存器中取出 X 和 Z 对,将它们相加并将它们存储在目标寄存器的第一个、第二个、第三个和第四个组件中。因此:在这一点上,我们已经为所有 4 个三角形得到了这个特殊的点积!
**现在对所有点积重复此过程:dot00 等等。 **
第 5 步。
最后一个计算(就我提供的代码所见)是重心的东西。这是您代码中的 100% 标量计算。但是您现在的输入不是标量点积结果(即单个浮点数),它们是 SSE vector/寄存器,其中 4 个三角形中的每一个都有一个点积。
因此,如果您通过使用对所有 4 个浮点数进行操作的并行 SSE 操作来展开矢量化,您最终将得到 1 个寄存器(或结果),其中包含 4 个高度,每个三角形 1 个高度。
由于我的午休时间已经过时了,我不打算详细说明这个问题,但考虑到我给出的设置/想法,这是最后一步,应该不难弄清楚。
我知道这个想法有点牵强,需要从上面的代码中得到一些爱,也许还需要一些铅笔和纸的美好时光,但它会很快(如果你愿意,你甚至可以在之后添加 OpenMP )。
祝你好运 :)
(请原谅我的模糊解释,如果需要,我可以启动该功能 =))
更新
我已经编写了一个实现,但它没有按我预期的那样进行,主要是因为 Y 组件涉及的代码超出了您最初粘贴在问题中的代码段(我查了一下)。我在这里所做的不仅是要求您将所有点重新排列为 XZ 对并每 4 个点输入它们,而且还使用 4 个三角形中每个三角形的 Y 值输入 3 个指针(对于点 A、B 和 C)。从本地的角度来看,这是最快的。我仍然可以修改它以减少被调用方的侵入性更改,但请让我知道什么是可取的。
然后免责声明:这段代码非常简单(我发现在 SSE 方面与编译器配合得很好……他们可以根据需要进行重组,x86/x64 CPU 也可以参与其中)。还有命名,这不是我的风格,也不是任何人的,只要你觉得合适就行。
希望它有所帮助,如果没有,我很乐意再重温一遍。如果这是一个商业项目,我猜也可以选择让我加入;)
不管怎样,我已经把它放在了 pastebin 上:http://pastebin.com/20u8fMEb
关于c++ - 如何使用 SSE 或 GLSL 优化 "u[0]*v[0] + u[2]*v[2]"代码行,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/11125083/
我正在学习如何使用Nokogiri,根据这段代码我遇到了一些问题:require'rubygems'require'mechanize'post_agent=WWW::Mechanize.newpost_page=post_agent.get('http://www.vbulletin.org/forum/showthread.php?t=230708')puts"\nabsolutepathwithtbodygivesnil"putspost_page.parser.xpath('/html/body/div/div/div/div/div/table/tbody/tr/td/div
总的来说,我对ruby还比较陌生,我正在为我正在创建的对象编写一些rspec测试用例。许多测试用例都非常基础,我只是想确保正确填充和返回值。我想知道是否有办法使用循环结构来执行此操作。不必为我要测试的每个方法都设置一个assertEquals。例如:describeitem,"TestingtheItem"doit"willhaveanullvaluetostart"doitem=Item.new#HereIcoulddotheitem.name.shouldbe_nil#thenIcoulddoitem.category.shouldbe_nilendend但我想要一些方法来使用
我有一个Ruby程序,它使用rubyzip压缩XML文件的目录树。gem。我的问题是文件开始变得很重,我想提高压缩级别,因为压缩时间不是问题。我在rubyzipdocumentation中找不到一种为创建的ZIP文件指定压缩级别的方法。有人知道如何更改此设置吗?是否有另一个允许指定压缩级别的Ruby库? 最佳答案 这是我通过查看rubyzip内部创建的代码。level=Zlib::BEST_COMPRESSIONZip::ZipOutputStream.open(zip_file)do|zip|Dir.glob("**/*")d
类classAprivatedeffooputs:fooendpublicdefbarputs:barendprivatedefzimputs:zimendprotecteddefdibputs:dibendendA的实例a=A.new测试a.foorescueputs:faila.barrescueputs:faila.zimrescueputs:faila.dibrescueputs:faila.gazrescueputs:fail测试输出failbarfailfailfail.发送测试[:foo,:bar,:zim,:dib,:gaz].each{|m|a.send(m)resc
很好奇,就使用rubyonrails自动化单元测试而言,你们正在做什么?您是否创建了一个脚本来在cron中运行rake作业并将结果邮寄给您?git中的预提交Hook?只是手动调用?我完全理解测试,但想知道在错误发生之前捕获错误的最佳实践是什么。让我们理所当然地认为测试本身是完美无缺的,并且可以正常工作。下一步是什么以确保他们在正确的时间将可能有害的结果传达给您? 最佳答案 不确定您到底想听什么,但是有几个级别的自动代码库控制:在处理某项功能时,您可以使用类似autotest的内容获得关于哪些有效,哪些无效的即时反馈。要确保您的提
假设我做了一个模块如下:m=Module.newdoclassCendend三个问题:除了对m的引用之外,还有什么方法可以访问C和m中的其他内容?我可以在创建匿名模块后为其命名吗(就像我输入“module...”一样)?如何在使用完匿名模块后将其删除,使其定义的常量不再存在? 最佳答案 三个答案:是的,使用ObjectSpace.此代码使c引用你的类(class)C不引用m:c=nilObjectSpace.each_object{|obj|c=objif(Class===objandobj.name=~/::C$/)}当然这取决于
我正在尝试使用ruby和Savon来使用网络服务。测试服务为http://www.webservicex.net/WS/WSDetails.aspx?WSID=9&CATID=2require'rubygems'require'savon'client=Savon::Client.new"http://www.webservicex.net/stockquote.asmx?WSDL"client.get_quotedo|soap|soap.body={:symbol=>"AAPL"}end返回SOAP异常。检查soap信封,在我看来soap请求没有正确的命名空间。任何人都可以建议我
关闭。这个问题是opinion-based.它目前不接受答案。想要改进这个问题?更新问题,以便editingthispost可以用事实和引用来回答它.关闭4年前。Improvethisquestion我想在固定时间创建一系列低音和高音调的哔哔声。例如:在150毫秒时发出高音调的蜂鸣声在151毫秒时发出低音调的蜂鸣声200毫秒时发出低音调的蜂鸣声250毫秒的高音调蜂鸣声有没有办法在Ruby或Python中做到这一点?我真的不在乎输出编码是什么(.wav、.mp3、.ogg等等),但我确实想创建一个输出文件。
我正在尝试测试是否存在表单。我是Rails新手。我的new.html.erb_spec.rb文件的内容是:require'spec_helper'describe"messages/new.html.erb"doit"shouldrendertheform"dorender'/messages/new.html.erb'reponse.shouldhave_form_putting_to(@message)with_submit_buttonendendView本身,new.html.erb,有代码:当我运行rspec时,它失败了:1)messages/new.html.erbshou
我在从html页面生成PDF时遇到问题。我正在使用PDFkit。在安装它的过程中,我注意到我需要wkhtmltopdf。所以我也安装了它。我做了PDFkit的文档所说的一切......现在我在尝试加载PDF时遇到了这个错误。这里是错误:commandfailed:"/usr/local/bin/wkhtmltopdf""--margin-right""0.75in""--page-size""Letter""--margin-top""0.75in""--margin-bottom""0.75in""--encoding""UTF-8""--margin-left""0.75in""-