Quantcast
Channel: 英特尔开发人员专区文章
Viewing all 583 articles
Browse latest View live

通过持久内存技术提升高性能计算能力

$
0
0

简介

持久内存 (PMEM) 技术提供(每个双路系统最高 6 TB)速度接近动态随机访问内存的大量字节可寻址非易失性内存,且只需 DRAM 的一小部分成本,即可为内存计算领域带来革命性的变化。内存计算最显著的优势包括缩短应用启动时间(无需重新创建内存数据结构)和增加内存容量。鉴于这些优势,关于高性能计算 (HPC) 是否也可以利用 PMEM 技术的问题出现了。

本文将潜在影响分为系统、中间件和应用三个方面,并从这三个方面探讨这一问题。我们将针对每个方面提供一般信息、潜在架构源代码变更和实际应用示例。本文并未涵盖所有可能情况,由于这是一项新技术,因此本文举出的示例还未最终确定。

持久内存 (PMEM) 技术概述

PMEM 概述

PMEM 技术可被视为 NVM 技术的最新进展。NVM 技术包括传统的磁带、硬盘和软盘,只读内存芯片和光盘,以及如今市面上最新的固态盘 (SSD)。与 DRAM 相比,所有这些技术的共同点是容量大但性能低。这会创建两个级别的系统存储(主存储和二级存储),这两种存储我们很熟悉。

主存储专为快速运行而设计,以便在计算过程中为 CPU 提供所需的所有“热”数据。二级存储用于保存目前不需要但在断电后仍能留存的“冷”数据和程序。尽管二级存储可用于存储“热”数据,但却在 DRAM 容量不够时使用(例如将内存页面切换为磁盘)。由于不可忽略的性能影响,此方法不可取。简言之,主存储是快速、小型的易失性存储,而二级存储是缓慢、庞大的持久性存储。

考虑到这一点,我们可以明白,这两种存储在设计方面的一个关键差异在于数据访问粒度。主存储支持 CPU 寻址并随机访问单个字节的数据,二级存储中的数据访问单元通常为 4KB 大小(有时甚至更大)的数据块。二级存储需要通过这种批量数据访问方式来弥补访问延迟,这比主存储的访问延迟还要大几个数量级。由于存在这种主存储和二级存储之间的访问粒度差异,因此需要为应用创建两种数据模型:

  • 主存储的数据模型更加复杂和丰富,如树、堆、链表、哈希表等
  • 二级存储的数据模型不太灵活,如标记语言(例如 XML)中的序列化数据结构、逗号分隔值文件、结构化查询语言表等

PMEM 有何不同?

PMEM 的创新性在于它支持字节寻址且速度快(如主存储),同时仍具备二级存储的许多优势,如大容量、持久性和每字节低成本(见图 1)。由英特尔和美光科技* 联合开发的 3D Xpoint™ 内存技术除了提供接近 DRAM 的访问延迟之外,还具有所有这些优势。CPU 可直接访问 PMEM DIMM,从而消除 PCIe *总线传输协议等中间开销。尽管我们仍需要使用二级存储来实现低成本的海量数据归档,PMEM 的定位是成为一种允许大量应用和系统统一其数据模型的技术。

chart
图 1.PMEM 技术与 DRAM 和固态盘的性能和容量对比。该图还显示了 PMEM 如何实现字节寻址和持久性。

希望在 PMEM 设备上保留某些数据结构的应用需要确保对数据结构的修改是在原子级别完成的(例如通过使用事物),以避免未在断电前按时刷新的 CPU 缓存中存储的数据造成损坏。不过,如果应用只需要增加主存储容量,就可以放弃这一部分。这可以在内存受限的 HPC 中派上用场,我们将在下文中看到。

系统视角

PMEM 为 HPC 带来的第一个优势是更大的主存储容量(每个双路系统 6 TB)。若要了解 HPC 应用可能如何利用 PMEM,我们首先要从概念上了解这种新技术如何适应整个内存层级。

三种逻辑架构

如图 2 所示,应用在集成 PMEM 时可以使用三种逻辑架构:DRAM 作为缓存、PMEM 作为 DRAM 扩展以及 DRAM 作为临时缓存。

CPU 架构的图像
图 2.使用 PMEM 作为内存受限应用的扩展容量的三种逻辑架构

在 DRAM 作为缓存的场景(见图 2a)中,应用将使用 PMEM 作为内存层级内的新层。应用为其 PMEM 中的数据结构分配内存,因此使用 PMEM 作为主存储,而仅将 DRAM 用作四级缓存。但如果采用这种方法,CPU 在常规计算过程中使用(即寻址)的所有数据仍然是针对 DRAM 的。这意味着,DRAM 和 PMEM 之间的数据移动需要使用某种显式缓存支持代码进行处理。

在 PMEM 作为 DRAM 扩展的场景(见图 2b),应用将使用所有可用内存容量作为单个内存池。可以先在 DRAM 中分配内存;如果需要更多内存,则在 PMEM 中继续分配。通过采用这种方法,CPU 在常规计算过程中使用的数据将来自 DRAM 或 PMEM,从而导致访问延迟变化,具体情形取决于 CPU 访问哪些数据。

在 DRAM 作为临时缓冲区的场景(见图 2c)中,应用由不同的计算内核组成,每个内核使用不同的内存使用模式。根据每个内核的具体情况,可以使用一种内存或另一种内存。此类内核的一个示例是 3D 快速傅里叶变换 (3D-FFT),后者会对数据进行转换以用于谱相方法。3D-FFT 需要对相同的数据点进行多次传递,因此最好始终针对 DRAM 进行计算。请注意,逻辑架构 (a) 实际上是 (c) 的一个子集。

模板应用

内存受限的大规模 HPC 应用将直接受益于分配更大规模问题的能力。此类应用的一个示例是模板(即最近邻居)计算。模板应用用于通过迭代有限差分技术实施偏微分方程求解器。求解 3D 热方程式是一个典型的模板问题。

Ht+1 [i,j,k]=a Ht [i,j,k]+b (Ht [i-1,j,k]+Ht [i,j-1,k]+Ht[i,j,k-1]+Ht[i+1,j,k]+Ht [i,j+1,k]+Ht [i,j,k+1])

该方程式是一个模板,代表针对 3D 网格中的每个数据点 (i,j,k) 执行的单个不适当(即新值存储在 Ht+1中而非 Ht中)Jacobi 迭代。由于这种访问模式是固定且可预测的,数据可在使用前智能预取到 DRAM 中,因此是 DRAM 作为缓存架构的理想之选。事实上,这种数据预取 在文献中被称为“阻塞”。例如,通过阻塞,数据被拆分为大小相同的核心块,这些数据块很好地融入到三级缓存中,可以大大减少缓存未命中。同样,核心块可以随后被拆分为用于并行化的线程块甚至 寄存器组,以充分利用数据级并行性(即矢量化)。

按照这一逻辑,我们可以考虑一下DRAM 块,它是一个额外的阻塞层,用于优化到 DRAM 的数据预取。图 3 显示了用最左边的新增层进行阻塞的概述。

”块数据的图像"
图 3使用为 PMEM 添加的额外层进行阻塞,预取模板应用的数据。此图是图 2 的修订版本,在

中间件视角

HPC 应用利用 PMEM 的另一种方式是增强位于中间件层的库和服务,使其具有 PMEM 感知能力。这种方法的目的是将这种新技术的优势带入到应用中,同时避免繁重的编码工作。

能够感知 PMEM 的检查点/重新启动

通过在本地节点级别添加一个 PMEM 感知缓冲区,可增强高性能计算中的检查点/重新启动 (C/R)。这些检查点可以从 PMEM 异步传输到远程分布式文件系统 (DFS) 或中间 突发缓冲服务器,且不会对执行进度有很大影响。这种 C/R 使用方式在社区中被称为“分级 C/R”(见图 4)。

持久性可确保在所有进程完成 PMEM 的检查点后(甚至在完成到 DFS 的远程复制之前),最后一个检查点完成的应用随时可用。当然,只要有问题的故障不影响保存在 PMEM DIMM 上的数据,情况就会如此。持久性还可以帮助降低 DFS 远程检查点的频率。经典的杨氏公式 (Tc= √(2 × C ×MTBF)) 表明,检查点的频率 (1/Tc) 与平均故障间隔时间 (MTBF) 成反比。由于 PMEM 为数据增加了额外的安全层,故障的可能性降低,从而使 MTBF 增加。频率较低的远程检查点意味着整体开销较小(远程传输大量数据并不便宜)。

尽管有执行本地检查点的其他替代方案(如固态盘),PMEM 的独特功能很可能使其成为这方面的关键技术。

image map
图 4.PMEM作为第一级的分级检查点。

由于 PMEM 的容量大于 DRAM,因此可以一次存储多个检查点(如需要)。通过协调远程复制与低应用网络流量的状态,这一缓冲可以帮助提高网络利用率。

MPI-PMEM 扩展

在高性能计算领域,最知名的中间件可能就是 消息传递接口 (MPI)了。本节将介绍为 MPICH* 编写的两个扩展:美国阿贡国家实验室开展的 MPI 开源实施。您可以在链接的 GitHub* 库中下载、使用并修改。当然,这些扩展并不是唯一可能的扩展。

第一个扩展位于 mpi_one_sided_extension目录下,通过允许进程声明“持久窗口”,使得 MPI 具有单向通信 PMEM 感知能力。这些窗口的生命周期超过了应用的执行时间,对 C/R 很有用。另一个用例是参数扫描场景,需要使用相同的输入数据运行相同的应用,但使用的输入参数不同。对于这些用例,输入数据只能从二级存储加载到 PMEM 中,然后重复用于多次执行,因此可缩短应用启动时间。

第二个扩展位于 mpiio_extension2目录下,使用两种模式将 PMEM 集成到 MPI-IO 中:(1) PMEM_IO_AWARE_FS 和 (2) PMEM_IO_DISTRIBUTED_CACHE。在 (1) 中,连接到所有节点的所有可用 PMEM DIMM 都合并为一个逻辑视图,以形成 PMEM DFS。当前节点中不存在的数据请求将使用代理转发到适当的节点。在 (2) 中,连接到节点的 PMEM DIMM 作为存储在远程 DFS 中的数据的巨大缓存(每个双路系统高达 6 TB)。如果我们还认为 PMEM 将享有接近 DRAM 的速度,那么不能忽视模式 (2) 提升 MPI-IO 性能的潜力。

持久工作流引擎

科学界使用的另一类 HPC 中间件是工作流引擎 (WE),例如Galaxy* 或 Swift*。为工作流程运行的作业必须将工作分成单独的任务。这些任务彼此独立运行(它们与数据无关,因此它们不共享任何状态,并且所有输入数据都按值传递),并且每个任务通常都是一个独立的软件。您可以将 WE 视为粘合不同软件的粘合剂,它们本身不会相互通信,从而创建一个整体 ─ 将输出与输入、调度和分配所需资源连接起来。

WE 的一个主要问题在于,在大多数情况下,任务通过文件(有时通过数据库引擎)相互通信。任务通常用于执行特定分析,它读取其输入文件并将结果写入输出文件,这些输出文件将被其他任务用作输入等等。在这里我们可以看到如何利用 PMEM 为工作流的中间数据创建快速缓存,而非在很大程度上依赖文件。您还可以通过优化任务直接使用特定的内存数据结构,而不必像平时一样利用平面文件进行重新创建,这也可以帮助简化代码并加快执行速度。

应用视角

在应用级别,应用本身直接负责定义哪些数据结构应该是永久的,并采取相应行动(例如通过原子方式写入,以避免可能的损坏)。英特尔正在通过存储和网络行业协会 (SNIA) 与业内的其他主要厂商密切合作,该协会开发了基于 NVM 编号模型 (NPM) 标准持久内存开发工具包 (PMDK)。PMDK 由多个开源库和 API 组成,其目标是帮助程序员调整其应用,使其适应 PMEM。尽管不是强制使用,但我们推荐您使用,特别是新手。

对于 HPC 应用,特别是模拟,持久数据结构的优势尚不明确。当我们考虑访问延迟时,我们需要问:持久性具有哪些额外优势,以弥补其访问延迟较长和交易开销大的缺点?此外,HPC 模拟的数据会随着时间推移而发生变化,是否值得留存很快会改变的数据?除了检查点之外,很难想出持久性能够为 HPC 模拟带来的其他明显优势。如果您不这样认为并能提供好的用例,请联系我。

不过,与 HPC 模拟结合使用的其他应用可以从持久数据结构中获益。现场可视化就是一个示例。

借助 PMEM 进行交互式现场可视化

现场可视化是一种旨在避免运行模拟的 HPC 系统与渲染图形的可视化系统之间过多移动数据的技术(见图 5)。可视化本身(或其一部分,因为可视化通常表示为一系列数据转换)是在模拟生成数据的同时在 HPC 系统中完成的,而不是将数据检查点设置到稍后作为可视化输入的文件系统中。可视化库在每个时步的末尾被调用,原始数据在大多数情况下通过引用(尽可能避免数据复制)进行传递。只有在完成可视化后,模拟才能继续进行下一步。

visualization map
图 5。(a) 传统 HPC 可视化与 (b) 现场可视化在 (a) 中,模拟执行昂贵的检查点(第 2 步),以存储用于可视化的原始数据。但在 (b) 中,大部分数据转换和渲染(如果不是全部)是在 HPC 系统中执行的。然后,转换和/或渲染的数据(比原始数据小)将被转发到可视化应用中,或存储起来以供未来使用。

这种方法的局限之一是缺乏灵活性。一旦模拟进行到下一个时步,来自上一个时步的数据通常会在内存中重写,这会限制与可视化交互的机会,例如更改用于着色的参数值,添加或移除过滤器,切片,添加额外灯光, 移动摄像头等等。此外,通过重启可能已经运行数天的模拟来改变一些可视化参数也是不可行的。

在这里,PMEM 可以允许时步窗口的持久性,从而提供帮助。这个窗口可以通过更改参数以及从窗口开始重新渲染模拟来进行模拟交互。您可以设想一下这种情形:用户对目前生成的可视化效果不满,可能想在继续模拟之前研究一下如何使用不同的可视化选项和参数,但不想重启。由于窗口是持久性的,因此比模拟的生命周期长。从理论上而言,模拟完成后很长时间都可以访问可视化并与其交互。

我正在使用 PMDK 的libpmemobj库开发一个借助 PMEM 进行交互式现场可视化的原型。我选择将 HPC 社区中众所周知的 ParaView* 作为可视化应用。我将向所有人公布我的进展以及从这一精彩项目中学到的经验教训。

总结

文本介绍了如何借助 PMEM 技术提高高性能计算能力。我们从 PMEM 的一般定义开始,然后介绍了它对系统、中间件和应用的潜在影响,以及潜在的架构代码变更和每个方面的实际应用示例。本文所举的所有示例仍在制定中,可能会发生改变。欢迎社区提供新的想法和意见,我的电子邮箱为 eduardo.berrocal@intel.com

关于作者

Eduardo Berrocal 于 2017 年 7 月加入英特尔,担任云软件工程师。他拥有伊利诺斯州芝加哥伊利诺理工学院 (IIT) 的计算机科学博士学位。Eduardo Berrocal 的博士研究课题是 HPC 的数据分析和容错。之前,他曾做过贝尔实验室(诺基亚)的暑期实习生、阿贡国家实验室的研究助理、芝加哥大学的科学程序员和 Web 开发人员以及西班牙 CESVIMA 实验室的实习生。

资源

  1. 一流多核架构上的模板计算优化和自动调整, Kaushik Datta 等人http://mc.stanford.edu/cgi-bin/images/e/ec/SC08_stencil_autotuning.pdf
  2. GitHub 中 MPI-PMEM 扩展代码的链接
  3. 持久内存开发工具包 (PMDK)
  4. 非易失性内存编程 (NMP) 标准https://www.snia.org/tech_activities/standards/curr_standards/npm
  5. 开源多平台数据分析和可视化应用 ParaViewhttps://www.paraview.org/
  6. 现场可视化:一些一流用例, Marzia Rivi 等人,CINECA,贝尔格莱德大学贝尔格莱德物理研究所科学计算实验室http://www.prace-ri.eu/IMG/pdf/In-situ_Visualization_State-of-the-art_and_Some_Use_Cases-2.pdf
  7. 消息传递接口 (MPI)
  8. 基于用户级 InfiniBand 的文件系统和用于突发缓冲区的检查点策略,Kento Sato 等人,2014 年第 14 届 IEEE/ACM 国际集群、云和网格计算 (CCGrid) 研讨会,http://ieeexplore.ieee.org/abstract/document/6846437/
  9. MPICH:高性能、可广泛移植的消息传递接口 (MPI) 标准实施http://www.mpich.org
  10. Galaxy 工作流引擎项目https://galaxyproject.org/
  11. Swift 工作流工具http://swift-lang.org/main/
  12. 优化不确定执行尺度的多级检查点模型, Sheng Di 等人,高性能计算、网络、存储和分析国际会议论文集 (SC’14),2014 年,https://dl.acm.org/citation.cfm?id=2683692

Unreal Engine*:制定布料模拟 CPU 优化蓝图

$
0
0

布料模拟

逼真的布料运动可大幅提升游戏的视觉沉浸感。我们可以使用 PhysX Clothing 是实现这一目标,无需手工绘制动画。将这些模拟整合到 Unreal Engine* 4 中非常容易,但该流程需占用较多 CPU,因此最好了解模拟的性能特征及优化方式。

禁用布料模拟

在 Unreal* 中,布料模拟位于相应的关卡,无论是否可见。优化可防止该风险。优化模拟布料时请勿依赖 Disable Cloth 设置,因此它仅在构建中有用,在游戏进行时毫无用处。

Unreal Physics 统计s

为更好了解布料模拟及其对游戏和系统的影响,我们可在 Unreal 中使用控制台命令 Stat PHYSICS。

在命令行中输入 Stat PHYSICS,然后会出现物理结构表覆盖(图 1)。删除它请在控制台中输入相同命令。

Physics overlay table
图 1.物理结构覆盖表。

尽管有大量信息可用,但我们还存在两方面(Cloth Total 和 Cloth Sim)担忧。

Cloth Total 表示场景中的布料绘制总数,Cloth Sim(模拟)表示当前模拟的活跃布料网格数量。确保这两个数目处于目标平台的合理水平内,有助于防止处理布料造成 CPU 过载和帧速下降。通过在该水平中添加更多布料网格,CPU 一次可处理的模拟数量便显而易见。

暂停布料模拟

Unreal Engine 蓝图系统还具备另外一项功能,即在骨架网格上暂停和恢复布料模拟。这些增加的节点可解决之前使用布料优化的细节级别方法时,每次都需要重置布料模拟的问题。

Rendered function switch
图 2.was recently rendered 功能转换的恢复和暂停。

出于本文的目的,“细节级别”部分所探讨的所有方法都适用,但现在可以将 Set Min LOD 节点和 Resume and Suspend Clothing Simulation 节点进行互换。

时间延迟转换

通过暂停布料模拟,我们可以增加布料的动态性同时优化性能。然而,仅使用遮挡转换可能会导致出现旗帜降落问题;其中布料模拟中包含动态移动,玩家走开(暂停布料模拟),一段时间后回来继续移动之前看到布料模拟在半空中飘扬。

为了解决这一问题,我们可以使用遮挡转换并添加一个布尔值来查看暂停;使用这种方法时可以在暂停模拟之前可以使用延迟,这样在最终停止之前布料有充足的时间完成移动并保持暂停。

Time delay switch
图 3.Time delay switch.

细节级别

在创建骨架网格并在其中附加一个顶点布料文件时,布料模拟将始终与该网格细节级别 (LOD) 的零值关联。如果网格的 LOD 0 被禁用,布料模拟将不再发生。为发挥其作用,我们可创建与 LOD 0 完全相同的 LOD 1(减去布料顶点文件),并将其用作转换,无论是否使用布料模拟(图 4)。

Level of detail information
图 4.细节级别信息。

布尔转换

既然已有转换,我们可设置简单的蓝图控制它。通过创建事件(或函数),我们可在模拟布料 (LOD 0) 和不模拟布料之间使用布尔转换,实施分支。该事件可在输入的触发器上调用,以开始在下一个区域模拟布料网格,并在玩家离开该区域时再次调用,以停止模拟或任何数量的方法,取决于游戏关卡。

Occlusion Culling Switch
图 5.转换蓝图。

遮挡剔除转换

如果希望采用更自动化的方法,可将遮挡剔除用作交换变量。为此,调用Was Recently Rendered函数,并返回转换分支(图 6)。这会在对象不再被渲染时阻止布料模拟。

Recently Rendered function in the switch blueprint.
图 6.转换蓝图中的”Was Recently Rendered”函数。

该方法的问题在于模拟恢复启动时发生的模拟重置。如果布料网格与模拟时显著不同,玩家将始终看到这种转换。为降低这种情况发生的概率,可通过导入设置扩大网格边界。然而,这也意味着故意渲染玩家看不到的对象,因此请确保其有助于满足游戏的渲染需求。

解决此问题的一个关卡设计方法包括确保所有动态布料网格(如旗帜)与风处于同一个方向。

我们可以使用 C++ 设计一种方法,以保存布料模拟每个顶点的位置数据,并在模拟恢复启动时恢复网格的位置。这种方法会占用较多资源,具体取决于使用的数据结构以及关卡中的布料模拟数量。

Cloth simulations without occlusion culling switch
图 7.未使用遮挡剔除转换的布料模拟。

Cloth simulations with occlusion culling switch.
图 8.使用遮挡剔除转换的布料模拟。

组合/Set Piece 转换

如果关卡碰巧具有一个极易变化且始终非常重要的 set piece,可在对象中附加一个使用布尔转换的额外分支。如图 9 所示,我们将其称为Optimize Cloth?

Set piece switch.
图 9.Set Piece 转换。

使用这种新转换时,某些布料网格非常重要,应始终通过将其Optimize Cloth?值切换至 false 进行模拟。

使用 Set Piece 转换

在下面的图 10 中,三个布料网格为转向朝后的旗帜(相对于起始位置)。需要数秒时间呈现自然状态,但由于它们确实并非手工绘制动画,因此我将它们设置为 Set Pieces(Optimize Cloth? false),它们可始终被模拟。

Complex flags used with set piece switches.
图 10.使用了 set piece 转换的复杂旗帜。

基于全新的英特尔® 酷睿™ i9 处理器的系统以卓越的原始计算性能支持极限大型任务处理

$
0
0

最早的计算机即使在执行简单任务时也通常被推至性能极限,在疯狂地锤击硬盘、更换内存和处理计算之间循环往复。随着 Microsoft Windows* 3.1 和后来 Windows 95 的推出,开始形成多任务处理,因为系统最终能够一次处理多个程序。现在,随着单颗 CPU 中开始采用双位数内核,“大型任务处理”的概念开始受到越来越多的关注。面向发烧友的最新产品家族为英特尔® 酷睿™ X 系列处理器家族,采用 4-18 颗内核。这些英特尔® 酷睿™ i9 处理器可同时处理多个任务 — 进入极限大型任务处理,而之前这要求使用多个完整的系统才能完成。

 Intel® Core™ i9 Processor Extreme想想同时畅玩、录制和流传输虚拟现实 (VR) 游戏所带来的挑战。游戏工作室依靠预告片激发受众对最新 VR 游戏的兴趣,但很难在 2D 视频中展现精彩的 3D 游戏体验,因为简单录制玩家的所见所闻只是完整游戏情节的一部分。有一种方法可以解决这一问题,即混合现实,首先在绿色屏幕前拍摄玩家,然后将这一视角与沉浸于虚拟世界的玩家的第三人称视角相融合。(更多有关混合显示技术的信息请参阅本篇文章。)通常要求一台 PC 畅玩和拍摄游戏,另一台 PC 采集玩家的摄像头馈入。加上另一想法,即将全时段会话现场流传输至全球满心期待的粉丝,你可能需要第三个系统来负责将输出编码成高质量的可上传格式。但英特尔团队近期证明,生产人员现在可以在基于单颗英特尔® 酷睿™ i9 处理器的系统上完成这些所有的 CPU 密集型任务,每个参与的内核都保持正常运行,物尽其用。

摩尔定律和系统规格

摩尔定律最初由英特尔联合创始人戈登 摩尔于 1965 年提出,他预测,单枚芯片上的晶体管数量每隔两年便会增加约一倍(图 1)。尽管晶体管的数量和频率有所增加,但目前通常根据可用内核的数量来 衡量原始计算性能。每个内核都可用作 CPU,并用来处理不同的任务,支持更高效的多任务处理。但简单的多任务处理开始变成极限大型任务处理,需要同时运行特意对齐的计算密集型多线程工作负载。

Moore's Law change for technology
图 1.摩尔定律体现了不断加快的技术变革(来源:time.com)

每秒浮点运算次数 (FLOPS) 的计算最初用来衡量超级计算机的性能,而现在应用于台式机游戏 PC。它们用于衡量涉及小数的数学运算,这比整数运算更难。公式为:

FLOPS =(插槽) x (每插槽内核数) x (每秒循环数) x (每循环 FLOPS)

假设一颗单路 CPU,采用 6 个内核,运行频率为 3.46 GHz,每次循环使用单精度 (8) 或双精度 (16) FLOPS。结果为 166 gigaflops(单精度)和 83 gigaflops(双精度)。对比之下,1976 年 Cray-1 超级计算机的性能仅为 160 megaflops。全新英特尔® 酷睿™ i9-7980XE 至尊版处理器的 运行频率约为 4.3 GHz(超频更快),因此计算能力可达到 1.3 teraflops。从这一方面来说,世界上最快的超级计算机运行 1065 万个内核,性能可达 124.5 petaflops。1961 年,1 gigaflop 需要花费大约 190 亿美元的硬件(相当于目前的大约 1450 亿美元)。到 2017 年,成本已降低至 3000 万美元。

为达到这种原始计算性能,英特尔® 酷睿™ i9-7980XE 至尊版处理器进行了多项技术升级。平台上多达 68 条 PCIe* 3.0 通道支持玩家通过快速的英特尔® 固态硬盘(英特尔® SSD)、多达 4 个独立 GFX 卡和超快速 Thunderbolt™ 3 技术解决方案来扩展系统。升级为英特尔® 睿频加速 Max 技术 3.0 显著提高了内核性能。英特尔® 智能高速缓存具备全新的节能特性,可根据需求动态刷新内存。英特尔® 酷睿™ X 系统处理器家族还可进行释放,从而为超频提供更多空间。全新特性包括支持内核超频、英特尔® 高级矢量扩展指令集 512(英特尔® AVX-512)比例控制(以提高稳定性),以及面向极限场景的 VccU 电压控制。与英特尔® 至尊调试实用程序 (Intel® Extreme Tuning Utility)、英特尔® Extreme Memory Profile(英特尔® XMP)等工具相结合,您可获得功能强大的套件最大限度地提升性能。

英特尔报告,相比前代英特尔® 处理器,内容创建者将体验到 20% 的VR 内容创建性能提升,以及 30% 的 4K 视频编辑速度提升(见图 2)。这意味着他们将显著缩短等待时间,而更多地专注于设计新游戏和新体验。玩家和发烧友将在游戏中体验到比前代产品提升 30% 的极限大型任务处理速度。

英特尔公司客户端计算事业部高级副总裁兼总经理 Gregory Bryant 在 2017 年台北国际电脑展表示,这一全新的处理器系将在整个生态系统中释放无限可能。“内容创作者可以拥有快速的图像渲染、视频编码、音频制作和实时预览功能,所有这些功能都可以无缝并行运行,因此可以帮助他们减少等待时间,增加创作时间。玩家可以使用支持 12K 体验的多个显示屏且最多使用四个独立显卡,畅玩其最喜欢的游戏,同时对游戏进行传输、录制和编码,并在社交媒体上共享。”

 Intel® Core™ X-series processor family partial specifications.
图 2.英特尔® 酷睿™ X系列处理器产品家族部分规格。

另外一种衡量系统性能的方法是通过 CPU 利用率,可以通过 Task Manager > Resource Monitor 了解自己的 Microsoft Windows PC 的 CPU 利用率。致力于游戏和 VR 社区的英特尔开发人员关系内容专家 Josh Bancroft 在 2017 年早期台北国际电脑展参与了英特尔® 酷睿™ 至尊版处理器的部署,并在展示 CPU 利用率的过程中帮助创造了术语“极限大型任务处理”。Bancroft 利用一台基于全新酷睿 i9 X-系列的 pC 展示绿屏 VR 混合现实演示,同时以 90 fps 的速度畅玩 VR 游戏、录制游戏播放、从独立的摄像头将玩家合并到场景之中,然后精确地组合和合成图像,并将结果现场流传输至 Twitch*。

之后,Bancroft 在洛杉矶举办的 E3 游戏展参与部署了首颗英特尔® 酷睿™ i9 至尊版,并在采用 18 个内核的系统上进行了相同的演示。他对此仍然记忆犹新:“非常高兴在基于包含 18 个内核的 i9 处理器的系统上进行全球首次公开演示。那次活动真是不可思议,有两根流着蓝色透明液体的水环,非常漂亮。”

Gregory Bryant 主持的演示非常顺利,但也充满了紧张感。Bancroft 解释说:“当你同时堆栈 4 或 5 个极限任务时,系统可能过载并导致崩溃。”当 18 个内核的表现完美,CPU 利用率图表显示了系统内部的运行情况。“当我们开始录制的时候,开始流传输的时候,当我们处理所有任务的时候,可以看到 36 张图全都飙升至超过 90% 的利用率。你可以看到所有线程都在非常努力的运行。”

演示充分说明了英特尔对 VR、PC 游戏,以及将多核处理性能集成至一款小巧的设备当中的承诺。由于 VR 要求大量资源才能正常运行,因此它最适合用来演示新系统。Bancroft 的混合现实技术支持开发人员、流媒体工作者和内容创建者制作预告片,并向人们展示 VR 体验,无需让他们佩戴头盔。最棒的是,一款新系统就可以替代之前所需的多台设备。

在独立开发人员的推广工具套件中,预告片是最重要的工具。对于开发游戏的独立开发人员来说,创建令人叹服、极具吸引力的 VR 游戏预告片具有不可或缺的重要意义。但 2G 预告片不能很好地传达 3D VR 体验,这里就需要用到混合现实技巧。位于加拿大温哥华,由夫妻档 Sarah 和 Colin Northway 运营的Northway Games* 率先尝试使用混合现实 VR,他们在基于 Unity 的游戏 Fantastic Contraption*中添加了支持代码(图 3)。能够记录玩家在有过程中的所见所闻以及它们在第三人称视角中的样子,通过传达这种体验很好地帮助推广 VR 游戏。此外,Northways 还增加了观众坐在沙发上观看游戏和大笑的镜头,以此展示了游戏的娱乐性。

Fantastic Contraption*
图 3.现在可在单台 PC 上创建和流传输混合现实预告片 — 比如Fantastic Contraption*预告片。

并未发明,只是增强

Bancroft 很快对这次在由脚手架、灯光、绿色屏幕和摄像机组成的狭窄工作室所学到混合现实单机演示表达了谢意。Northways 写了一篇博客,详细分享了所涉及的任务的 演练步骤,Bancroft 在很大程度上借鉴了这篇博客。从那之后,他和他的团队进行了许多其他的调整,全都公开开发和分享。

许多软件程序都要求强大的性能;仅仅以 90 fps 的速度玩面向 Oculus Rift* 或 HTC VIVE* 的 VR 游戏就是一项非常艰巨的任务。如果帧速率较低,玩家可能会出现头晕、呕吐和其他身体反应,因此设备必须首先具备能够畅玩游戏的卓越性能,然后才能处理更多负载。

Bancroft 喜欢使用 MixCast* 进行混合与合并,这是一款不断发展的 VR 广播和演示工具,能够简化混合现实视频的创建过程。这款工具由交互式技术领域的领导者、位于加拿大温哥华的 Blueprint Studios* 创建,用于将 MixCast VR SDK 拖放至 Unity 项目,以便最终用户实时展示他们的体验。

此外,Bancroft 还使用 Open Broadcaster Software (OBS),这是一款大多数流媒体工作者非常熟悉的免费开源软件程序,用于合并、录制和现场流传输。它可提供高性能、实时音视频捕捉和混合;面向图像掩码的视频滤波器、色彩纠正和色度键控;并支持流传输平台,比如 Twitch*、Facebook* 和 YouTube*。

淡然还有许多工具用于创建相同的最终结果,但这是当前的软件堆栈。请访问 <link to Mega-tasking step-by-step article> 查看关于 Bancroft 此次工作的完整介绍。

Jerry Makare 是英特尔® 软件 TV 视频制作人,与 Josh Bancroft 合作制作了测试极限大型任务处理的原始计算性能界限的视频。他发现使用一个强大的系统支持 VR 具备许多重要优势。他说:“能将任务分布在多个位置,尤其是渲染,非常重要。一旦开始渲染,基本上机器到最后就会报废。你没有任何办法。将大型的计算机密集型任务(比如渲染与合并)分布在多个区域为我们节省了大量的时间。”

Makare 非常渴望让基于英特尔® 酷睿™ i9 处理器的系统完成构建大规模房间的任务,使用 3D 建模程序获取有关时间节省的基准。他还希望让新系统运行一些真实应用,以便团队从中学习。

展望未来

借助如此强大的原始计算性能,用不同的方法使用这些新系统令人激动万分。玩家将获得更生动、更逼真、更真实的游戏体验。从原始 4K 镜头创建和编辑视频曾经是一项极其复杂的处理密集型任务,但现在不管是专业人员还是新手,都可以在原生 4K 中进行编辑,打造令人惊叹的视觉效果,以及编排更有深度、更玄妙的音乐。VR 技术的应用范围将越来越广,从最初的游戏到现在的虚拟演练、建造规划、城市建模和五叔的模拟场景。借助极限大型任务处理背后强大的原始计算性能,生物学、地质学、化学、医药、天文学等领域的科学家将逐步揭开更多奥秘。

其他资源

一门 VR:首款关于采用 MSI* 背包式 PC 的无线 VR 概念验证

$
0
0

Corey Warning 和 Will Lewis 是俄勒冈波特兰市的一家独立游戏工作室 Rose City Games* 的联合创始人。

Rose City Games 最近获得了一笔用于创建 VR 背包早期创新项目的开发补贴和设备预算。面临的挑战是想出一个只能在无线 VR 设置下实现的创意。在本文中,您将了解如何关于项目的设想和设计,我们所学到的知识,以及我们对未来的期望。以下视频详细介绍了这一项目。

项目灵感:一扇门

今年年初,我们的团队参加了俄勒冈波特兰市的 Resident Evil Escape Room我们是该游戏的忠实粉丝,通过全新的媒介体验这一游戏世界令人非常兴奋。此外,通过这一体验我们思考了还有哪些其他的体验能够以类似的方式进行跨越。

当时,我们尝试了所有能够动手实施的 VR 体验。当我们听说有机会尝试无线 VR 体验,我们就知道一定会发生有趣的事情。

目前,我们和从事各种 VR 项目的朋友在共同办公空间之外工作。WILD组员曾经尝试过将真实空间和 VR 相融合,所以我问 Gabe Paez是否记得当时项目进行过程中所遇到的具体挑战。他回答说是“门”,然后我决定继续创建“VR Escape Room”体验,其核心概念是穿过一扇门!

概述

项目范围是使用 MSI* One VR 背包创建 VR 应用概念验证。我们尝试创建一种只能使用硬件(具体来说是无线设置)实现的独特体验。

我们知道这一项目需要有一个装置,因此我们不考虑使用面向大众市场的产品。它可能是用于参加比如 GDC Alt.CtrlUnite*VR La等展览的有趣内容。

一门游戏概念

玩家将身处完全虚拟的空间中,与一扇实实在在的门进行交互。他们要背上 MSI VR One* 背包,并佩戴一个 HTC VIVE* 控制器和一个 VIVE 头盔。玩家必须在每一个关卡中完成一个简单的谜题或动作。完成后,玩家将能够打开门,跨过门槛进入下一关。这时他们面对是一个新的谜题或动作,游戏就是以这种方式向前推进。


图 1.一扇门概念验证设置

玩家可以随时打开门。但如果没有完成谜题或动作,另一端将显示同样的关卡/门。我们考虑使用 VIVE 跟踪来处理实际的门,这样我们就能轻松跟踪和校准玩家需要抓取的位置。


图 2.一扇门前视图


图 3.一扇门顶部视图

安装细节

  • 这扇门必须非常轻。
  • 支撑梁必须确保门不会倒向墙。
    • 底座位置的沙袋非常重要。
  • 应使用支架或类似工具,方便快速安装和拆卸,确保每次不会破坏整体性。
  • VIVE 灯塔的设置必须高于墙壁,以便捕捉整个游戏区域。
    • 需要质量优良的架子和较多沙袋。
  • 支撑梁/灯塔周围可能需要摆放比如豆袋椅,以确保人不会绊到这些东西。
    • 另外一个注意事项是必须安排人随时看着这一装置。


图 4.灯塔内的一门场地设置

我们的增建设施

  • 带有门把手和底座的移动门
  • MSI VR One 背包和用于开发的站外计算机
    • 其他必需的 DisplayPort-to-HDMI 线缆
    • 鼠标/键盘/显示器
    • OBS,用于捕捉视频
  • 2 个灯塔
    • 架子
    • 可调节抓爪,以将灯塔指向任意角度
    • 成对角线放置在门的两边
  • 1 个 VIVE 跟踪器
    • Gaffe 胶带,将跟踪器贴在门上,留出充电端口
    • 延伸线缆和充电线缆连接至跟踪器,以便充电
  • 2 个 VIVE 控制器
    • 我们用不到这两个控制器,但显示手部位置有利于录制视频
  • iPhone*,用于捕捉真实世界视频


图 5.门上贴有 VIVE*

这个项目是一次很好的 VR 开发培训。我们第一次使用 VIVE,并实施了其他物理增建设施来打造全新的交互式体验,是一次重要的学习过程。我认为我们遇到的大多数难题和所有 VR 开发人员所遇到的一样,但当然我们也给自己提出了许多独特的挑战,现在非常高兴有过这次体验。强烈建议 VR 开发人员在第一次独立开发项目之前,认真探讨以下主题,并学习我们的假设和过程。

首次使用 HTC VIVE*

我们经常用 VIVE 玩游戏,但却是第一次用它来开发项目。设置通用开发人员环境和 Unity* 插件并不费时间,但我们必须从战略上思考如何开发和测试更无缝地通过那个点。通常,在现场安排两个人可帮助我们节省大量的时间:一个人负责 Unity,另一个人移动控制器和跟踪器、重新调节灯塔、调节房间规模,就像人的第二双眼睛。


图 6.一门 VR 开发和测试

关于具体硬件以及需要使用物理道具的项目,我们反复进行了设计,以便灯塔能够跟踪到设备,甚至在连接显示器方面遇到了一些麻烦。由于 MSI VR One 背包只有一个 HDMI 输出和一个 DisplayPort 输入,我们必须借用(后来购买了)DisplayPort-to-HDMI 转换器,以便同时开发应用和使用 VIVE 头盔。幸运的是,这并没有过多地延缓开发进程,而且比最开始的解决方法还要好,我们之前将 HDMI 输出连接至已有的 HDMI 交换机,在显示器/设备环境和头盔之间切换。如果在开发过程中继续使用这种解决方案是非常不切实际的,一定会浪费大量时间。

我们在这次项目开发过程中获得了很多全新的体验,比如能够在家里使用 Unity 的协作功能远程工作,探索无线 VR 体验的精妙之处,以及熟悉以多快的速度启动 VR 项目。

预算

除了测试新设备和搭建物理增建设施,预算是我们不得不解决的另一个挑战。专项资金无法完全覆盖英特尔提供的设备推荐列表,因此我们只能挑选用于项目的最小数量的设备,然后考虑剩余的款项如何满足经验丰富的开发人员所投入的时间。幸运的是,由于我们与当地游戏开发人员社区的密切关系,一位有兴趣试验这种项目的朋友与我们合作开发这一项目。如果我们从头开始这个项目,我们一定提高预算,因为我们要在最低要求中考虑使用至少两台以上的跟踪器、转换器线缆、用于灯塔架顶端的可调节接头以及其他设备,以通过更完美的产品在紧迫的时间期限内完成这一项目。

位置和空间

从消费者的角度来看,我们知道对许多消费者来说,房间规模的 VR 是不现实的,而且在我们进行项目规划和实施的过程中,仍然遇到了一些问题。如果其他开发人员开发房间规模 VR,我们强烈建议提前购买卷尺,并确保有单独用于该项目的空间,保证开发的完整性。我们与其他 20 位当地的 VR 开发人员、美术师、游戏开发人员和网站设计师共用一个共同办公空间,因此每次开发会议结束之后需要将增建设施推至房间一侧,这样增加了整体的设置时间。的确我们练习了如何设置,也提高了我们对设备的熟悉程度,但同时也揭示了一个有趣的现象,那就是我们无法从家里进行这项工作!

独特的增建设施

由于项目涉及道具(一扇全尺寸树立的门),我们必须充分考虑如何移动、保存以及它对灯塔的遮挡。我们考虑进入原型之后的下一阶段时,更多的问题浮现出来。考虑如何让这一项目在今后继续发挥作用,比如用作技术演示、节日/博物馆装置或 resume piece,我们还必须考虑,除了我们自己和直接赞助方外,我们需要向更多人展示这一项目。这样出现了另一个需要考虑的因素:安全性。我们肯定会走捷径快速构建一个功能性原型,但考虑到改进和运输就绪性,我们绝对建议花费更多的时间和资源为不熟悉 VR 的人打造更加安全的体验。

 

因为我们构建过原型,因此能够记住抬脚不被绊倒,慢慢向前移动避免撞到门上,并且能够毫不费力地找到门把手。我们的成果看用作优秀的技术演示,但考虑到作为可供消费的公共产品或体验之前,我们肯定会用不同的方式处理门道具。为了让运输更方便,我们还用不同的方式建造门,以便在运输过程中拆卸。

展望未来

我们坚信,我们的项目能够作为技术演示,展示如何轻松、有趣、自由地使用 MSI VR One 背包。而且对于在这一过程中所学到的知识和最后的成果,我们感到非常自豪和高兴。因此我们想继续实施简单谜题、美术、画外音和可访问性特性,以便将该项目更完美地呈现出来。经过其他的测试和改进,我们想对原型货比三家、寻找与内容和 IP、VR 技术、交互装置相关的赞助商或展会,以便与广大受众一起分享这一项目!英特尔是此次合作的首选,我们希望在新一轮演示之后继续跟进。

非常感谢让我们参与其中!

代码示例 (Unity)

使用与门一般大小的外设时,关于灯塔和跟踪器设置 — 尤其是跟踪器,房间的设计必须恰到好处,我们将跟踪器粘在门上以随时测量方位,这样有利于我们分辨门是否关闭或打开。我们编写了一个简单的设置脚本,以定位门、门框和门支架/稳定器。

Setup Helper 是一个简单的工具,支持解决相对于 VIVE 跟踪器位置的门和门框的位置和旋转问题。Setup Helper 在 Editor 模式下运行,不必切换至 Play 模式就可进行更新,运行应用之后必须禁用该工具,以允许门在游戏中独立于门框旋转。可创建多个 Setup Helper 以定位需要与门相对的其他几何体,如房间墙壁、地板、房间装饰等,以避免潜在的面向视觉/碰撞的间隙或修剪。

Setup Helper 层级结构如上所示。以下应用于用蓝色凸显的区域,包括跟踪器(粘在门上)和门口。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
[ExecuteInEditMode]
public class SetupHelper :MonoBehaviour {
	public bool setDoorFrameToTracker = false;
	public GameObject doorFrameGo;
	public Transform trackerTransform;
    public bool trackRotation = false;
	public Vector3 doorframeShift;//used to set the difference in placement to make it fit perfectly on the tracker position
	// Use this for initialization
	void Start () {

	}

	// Update is called once per frame
#if UNITY_EDITOR
	void Update () {
		if (setDoorFrameToTracker)
			SetDoorFrameToTracker();
	}
	void SetDoorFrameToTracker()
	{
		doorFrameGo.transform.position = trackerTransform.position + doorframeShift;
        if (trackRotation)
            doorFrameGo.transform.rotation = trackerTransform.parent.rotation;
	}
#endif

}

关于作者

Corey Warning 和 Will Lewis 是俄勒冈波特兰市的一家独立游戏工作室 Rose City Games* 的联合创始人。

使用英特尔® 数据分析加速库对英特尔® 至强® 处理器进行性能优化

$
0
0

摘要

本文对英特尔® 至强® 金牌处理器的性能进行了比较研究,选取了《人工智能:一种现代方法》(AIMA)教材(作者:Stuart Russell 和 Peter Norvig)中的朴素贝叶斯算法和 scikit-learn*(SkLearn),并运行了 PyDAAL 编程接口,以显示英特尔® 数据分析加速库(英特尔® DAAL)的优势。在英特尔® 至强® 处理器中计算并比较了上述不同类别朴素贝叶斯分类器的准确度。通过观察发现,PyDAAL(多项)中朴素贝叶斯的性能要明显优于 SkLearn 和 AIMA 的性能。我们还发现,相比 AIMA,SkLearn 的性能更高。

测试与系统配置

环境设置

我们使用以下环境设置运行代码,并确定测试处理器的性能。

处理器系统内核存储(RAM)Python* 版本PyDAAL 版本
英特尔®  至强®  金牌 6128 处理器 3.40 GHzCentOS*(7.4.1708)2492 GB3.6.22018.0.0.20170814

测试设置

我们使用以下规则和方法执行测试,并比较数值:

  • 为了运行 PyDAAL 中的朴素贝叶斯分类器,我们使用了 Conda* 虚拟环境。
  • AIMA 中描述的朴素贝叶斯分类器由 GitHub* 代码中的 learning_apps.ipynb文件提供。
  • 使用 AIMA 中的朴素贝叶斯学习方法计算 learning_apps.ipynb(转换为 .py)的平均执行时间和准确度。
  • 使用 SkLearn 和 PyDAAL 中的朴素贝叶斯分类器计算 learning_apps.ipynb(转换为 .py)的平均执行时间和准确度。
  • 为了计算平均执行时间,使用以下 Linux* time 命令:
    • 示例: time(cmd="python learning_apps.py"; for i in $(seq 10); do $cmd; done)
    • 平均执行时间=时间/10。
  • 为了计算准确度,在所有情况下均使用 SkLearn 中的 accuracy_score 方法。
  • 性能提升百分比= ((AIMA - PyDAAL)/AIMA)× 100 或者((SkLearn - PyDAAL)/SkLearn)× 100。
  • 性能提升(比值)= AIMA(s)/PyDAAL(s) 或者 Sklearn (s)/PyDAAL(s)。
  • 性能提升百分比的值越高,表明 PyDAAL 的性能越好。
  • 性能提升(比)值高于 1,表明 PyDAAL 的性能更好。
  • 仅比较 learning_apps.ipynb文件中的朴素贝叶斯部分。

代码和条件概率

将 AIMA 中给出的朴素贝叶斯学习方法代码部分与 SkLearn(高斯和多项)和 PyDAAL(多项)中的相应实施进行比较。以下是相关代码示例:

AIMA
from learning import

temp_train_lbl = train_lbl.reshape((60000,1))
training_examples = np.hstack((train_img, temp_train_lbl))

MNIST_DataSet = DataSet(examples=training_examples, distance=manhattan_distance)
nBD = NaiveBayesLearner(MNIST_DataSet, continuous=False)
y_pred = np.empty(len(test_img),dtype=np.int)
for i in range (0,len(test_img)-1):
y_pred[i] = nBD(test_img[i])

temp_test_lbl = test_lbl.reshape((10000,1))
temp_y_pred_np = y_pred.reshape((10000,1))
SkLearn(高斯)
from sklearn.naive_bayes import GaussianNB

classifier=GaussianNB()
classifier = classifier.fit(train_img, train_lbl)

churn_predicted_target=classifier.predict(test_img)
SkLearn(多项)
from sklearn.naive_bayes import MultinomialNB

classifier=MultinomialNB()
classifier = classifier.fit(train_img, train_lbl)

churn_predicted_target=classifier.predict(test_img)
PyDAAL(多项)
from daal.data_management import HomogenNumericTable, BlockDescriptor_Float64, readOnly
from daal.algorithms import classifier
from daal.algorithms.multinomial_naive_bayes import training as nb_training
from daal.algorithms.multinomial_naive_bayes import prediction as nb_prediction

def getArrayFromNT(table, nrows=0):
bd = BlockDescriptor_Float64()
if nrows == 0:
nrows = table.getNumberOfRows()
table.getBlockOfRows(0, nrows, readOnly, bd)
npa = np.copy(bd.getArray())
table.releaseBlockOfRows(bd)
return npa

temp_train_lbl = train_lbl.reshape((60000,1))
train_img_nt = HomogenNumericTable(train_img)
train_lbl_nt = HomogenNumericTable(temp_train_lbl)
temp_test_lbl = test_lbl.reshape((10000,1))
test_img_nt = HomogenNumericTable(test_img)
nClasses=10
nb_train = nb_training.Online(nClasses)

# Pass new block of data from the training data set and dependent values to the algorithm
nb_train.input.set(classifier.training.data, train_img_nt)
nb_train.input.set(classifier.training.labels, train_lbl_nt)
# Update ridge regression model
nb_train.compute()
model = nb_train.finalizeCompute().get(classifier.training.model)

nb_Test = nb_prediction.Batch(nClasses)
nb_Test.input.setTable(classifier.prediction.data,  test_img_nt)
nb_Test.input.setModel(classifier.prediction.model, model)
predictions = nb_Test.compute().get(classifier.prediction.prediction)

predictions_np = getArrayFromNT(predictions)

aima-python-master”的“learning_apps.ipynb”被用作本实验的参考代码。该文件以传统的方式使用“朴素贝叶斯分类器”实施了 MNIST 数据集的分类。但是这种数据分类方法消耗了大量时间。

为了检查是否改进了性能,使用面向 Python* 的高性能数据分析库(PyDAAL)实施了相同的实验。在该实验中,数据结构主要使用“NumericTables”(一种通用数据类型)表示内存中的数据。

在代码中,使用“load_MNIST()”函数将数据加载为 train_imgtrain_lbltest_imgtest_lbl。“train_img”和“test_img” 表示训练数据和测试数据,train_lbltest_lbl表示用于训练和测试的标签。检查“C 连续”属性后,这些输入数据被转换为“HomogenNumericTable”。这样做的原因是只有在输入数据为“C 连续”时,才会发生转换。

创建一个算法对象(nb_train),用于在在线处理模式中训练多项朴素贝叶斯模型。使用“nb_train”算法对象中的“input.set”成员方法设置两种输入片段-数据和标签。此外,使用“compute()”方法更新部分模型。创建模型后,对测试对象(nb_Test)进行了定义。分别使用 input.setTable()nbTest.input.setModel()方法将测试数据集和经过训练的模型传输至算法。使用“compute()”方法得到预测结果后,实验准确度和用时也通过计算得出。使用 Linux 中的“SkLearn”库和“time”命令进行上述计算。

使用 SkLearn 中的“多项朴素贝叶斯”再次实施相同的代码,以比较传统方法和 PyDAAL。

通过分析实验用时可以清楚地发现,相比其他方法,PyDAAL 拥有更好的时间性能。

  • AIMA 中的条件概率分布假设为
    • 由观察和计数示例形成的概率分布。
    • 如果 p 为该类别的实例,o 为观测值,共有 3 个主要运算:
      • p.add(o)增加 1 个观测数量。
      • p.sample()从分布中返回一个随机元素。
      • p[o]返回 o 的概率(与常规 ProbDist 相同)。
  • 高斯朴素贝叶斯中的条件概率分布假设为高斯/正态分布。
  • 多项朴素贝叶斯中的条件概率分布假设为多项分布。

简介

在测试中,使用英特尔® 至强® 金牌处理器运行 AIMA、SkLearn(高斯和多项)和 PyDAAL(多项)中的朴素贝叶斯。为了确定处理器的性能提升,我们比较所有相关场景的准确率。我们还计算出与其他方法相比,PyDAAL 的性能提升(比值)。朴素贝叶斯(高斯)不包含在计算中,假定它更适合比较 SkLearn 和 PyDAAL 的多项版本。

观察

英特尔® DAAL借助批量、在线和分布式处理的计算模式面向所有数据分析阶段(预处理、转换、分析、建模、验证和决策制定)提供高度优化的算法构建模块,有助于提升大数据分析的速度。

  • 帮助应用以更快的速度交付更好的预测
  • 使用相同的计算资源分析更大的数据集
  • 优化数据收集和算法计算,以实现最佳性能
  • 支持线下、流和分布式使用模式,以满足不同的应用需求
  • 提供优先支持-与英特尔工程师就技术问题进行私下沟通

准确度

我们运行了 AIMA 中的朴素贝叶斯学习方法,并发现 PyDAAL 和 SkLearn(多项)的准确率相同(请参阅测试与系统配置)。

图 1 显示了朴素贝叶斯的精度值。

graph of accuracy values
图 1.英特尔® 至强® 金牌 6128 处理器-精度值表。

Benchmark results were obtained prior to the implementation of recent software patches and firmware updates intended to address exploits referred to as "Spectre" and "Meltdown". Implementation of these updates may make these results inapplicable to your device or system.

Software and workloads used in performance tests may have been optimized for performance only on Intel microprocessors. Performance tests, such as SYSmark and MobileMark, are measured using specific computer systems, components, software, operations and functions. Any change to any of those factors may cause the results to vary. You should consult other information and performance tests to assist you in fully evaluating your contemplated purchases, including the performance of that product when combined with other products. For more information, see Performance Benchmark Test Disclosure.

配置:英特尔® 至强® 金牌 6128 处理器 3.40 GHz;系统:CentOS*(7.4.1708);内核数量:24 个;存储(RAM):92 GB;Python* 版本:3.6.2;PyDAAL 版本:2018.0.0.20170814。
性能指标评测资料来源:英特尔公司。更多注释和声明请参阅下方。1

性能提升

已计算出朴素贝叶斯(AIMA 和 PyDAAL,以及 SkLearn 和 PyDAAL)的时间性能提升(比值),通过观察发现,PyDAAL 上的性能(请参阅测试与系统配置)更高。

图 2 和图 3 提供了性能提升加速值。

AIMA versus PyDAAL
图 2.英特尔® 至强® 金牌 6128 处理器- AIMA 与 PyDAAL 性能提升对比图。

Benchmark results were obtained prior to the implementation of recent software patches and firmware updates intended to address exploits referred to as "Spectre" and "Meltdown". Implementation of these updates may make these results inapplicable to your device or system.

Software and workloads used in performance tests may have been optimized for performance only on Intel microprocessors. Performance tests, such as SYSmark and MobileMark, are measured using specific computer systems, components, software, operations and functions. Any change to any of those factors may cause the results to vary. You should consult other information and performance tests to assist you in fully evaluating your contemplated purchases, including the performance of that product when combined with other products. For more information, see Performance Benchmark Test Disclosure.

配置:英特尔® 至强® 金牌 6128 处理器 3.40 GHz;系统:CentOS*(7.4.1708);内核数量:24 个;存储(RAM):92 GB;Python* 版本:3.6.2;PyDAAL 版本:2018.0.0.20170814。
性能指标评测资料来源:英特尔公司。更多注释和声明请参阅下方。1

SkLearn versus PyDAAL
图 3.英特尔® 至强® 金牌 6128 处理器- SkLearn 与 PyDAAL 性能提升对比图。

Benchmark results were obtained prior to the implementation of recent software patches and firmware updates intended to address exploits referred to as "Spectre" and "Meltdown". Implementation of these updates may make these results inapplicable to your device or system.

Software and workloads used in performance tests may have been optimized for performance only on Intel microprocessors. Performance tests, such as SYSmark and MobileMark, are measured using specific computer systems, components, software, operations and functions. Any change to any of those factors may cause the results to vary. You should consult other information and performance tests to assist you in fully evaluating your contemplated purchases, including the performance of that product when combined with other products. For more information, see Performance Benchmark Test Disclosure.

配置:英特尔® 至强® 金牌 6128 处理器 3.40 GHz;系统:CentOS*(7.4.1708);内核数量:24 个;存储(RAM):92 GB;Python* 版本:3.6.2;PyDAAL 版本:2018.0.0.20170814。
性能指标评测资料来源:英特尔公司。更多注释和声明请参阅下方。1

总结

英特尔至强金牌处理器上的优化测试显示,相比 AIMA 和 SkLearn,PyDAAL 花费了更少的时间(见图 4),因此提供了更高的性能(请参阅测试与系统配置)。在本场景中,SkLearn(多项)和 PyDAAL 拥有相同的准确度。AIMA 中的条件概率分布假设是一个简单的距离测量。但是,SkLearn 和 PyDAAL 中的假设为高斯分布或多项,这就是我们观测到准确度差异的原因。

graph of performance time.
图 4.英特尔® 至强® 金牌 6128 处理器-性能时间图。

Benchmark results were obtained prior to the implementation of recent software patches and firmware updates intended to address exploits referred to as "Spectre" and "Meltdown". Implementation of these updates may make these results inapplicable to your device or system.

Software and workloads used in performance tests may have been optimized for performance only on Intel microprocessors. Performance tests, such as SYSmark and MobileMark, are measured using specific computer systems, components, software, operations and functions. Any change to any of those factors may cause the results to vary. You should consult other information and performance tests to assist you in fully evaluating your contemplated purchases, including the performance of that product when combined with other products. For more information, see Performance Benchmark Test Disclosure.

配置:英特尔® 至强® 金牌 6128 处理器 3.40 GHz;系统:CentOS*(7.4.1708);内核数量:24 个;存储(RAM):92 GB;Python* 版本:3.6.2;PyDAAL 版本:2018.0.0.20170814。
性能指标评测资料来源:英特尔公司。更多注释和声明请参阅下方。1

参考资料

  1. AIMA 代码:
    https://github.com/aimacode/aima-python
  2. AIMA 数据文件夹:
    https://github.com/aimacode/aima-python(单独下载)
  3. 书目:
    《人工智能:一种现代方法》(作者:Stuart Russell 和 Peter Norvig)

1性能测试中使用的软件和工作负荷可能仅在英特尔® 微处理器上进行了性能优化。诸如 SYSmark 和 MobileMark 等测试均系基于特定计算机系统、硬件、软件、操作系统及功能。上述任何要素的变动都有可能导致测试结果的变化。请参考其他信息及性能测试(包括结合其他产品使用时的运行性能)以对目标产品进行全面评估。更多信息,详见 www.intel.cn/content/www/cn/zh/benchmarks/benchmark.html

Intel’s compilers may or may not optimize to the same degree for non-Intel microprocessors for optimizations that are not unique to Intel microprocessors. These optimizations include SSE2, SSE3, and SSSE3 instruction sets and other optimizations. Intel does not guarantee the availability, functionality, or effectiveness of any optimization on microprocessors not manufactured by Intel. Microprocessor-dependent optimizations in this product are intended for use with Intel microprocessors. Certain optimizations not specific to Intel microarchitecture are reserved for Intel microprocessors. Please refer to the applicable product User and Reference Guides for more information regarding the specific instruction sets covered by this notice. Notice revision #20110804

使用持久内存开发工具包 (PMDK) 创建持久内存感知队列

$
0
0

简介

本文将介绍如何使用链表和 持久内存开发工具包 (PMDK) libpmemobj 库的 C++ 绑定实施持久内存 (PMEM) 感知队列。

队列是 一种先进先出 (FIFO) 数据结构,支持 推送弹出操作。在推动操作中,新元素将添加到队列的尾部。在弹出操作中,队列开头的元素被删除。

PMEM 感知队列与普通队列的不同之处在于,其数据结构永久驻留在持久内存中,程序或机器崩溃可能导致队列输入不完整和队列损坏。为了避免这种情况,队列操作必须是事务性的。这并不简单,但 PMDK 提供专门针对持久内存编程的这一操作和其他操作的支持。

我们将提供一个代码示例,介绍使用 libpmemobj 创建 PMEM 感知队列的核心概念和设计注意事项。您可以按照本文后面提供的 说明构建和运行代码示例。

如欲获取关于持久内存和 PMDK 的背景信息,请阅读 文章“借助英特尔持久内存进行编程的简介”并观看 持久内存编程视频系列

libpmemobj 中的 C++ 支持

libpmemobj 的 C++ 绑定的主要特性包括:

  • 事务
  • 基本类型的包装程序:在事务处理过程中自动创建数据快照
  • 持久指针

事务

事务是 libpmemobj 操作的核心。这是因为,就持久性而言,当前的 x86-64 CPU 只能保证 8 字节存储的原子性。实际应用以较大的数据块进行更新。以字符串为例,仅将 8 个相邻字节从一个一致的字符串状态更改为另一个状态几乎没有意义。为了支持对较大数据块中持久内存的原子更新,libpmemobj 实施了事务。

出于可见性原因,

Libpmemobj 将使用基于撤消日志的事务,而不是基于重做日志的事务。用户所作的变更立即可见。这可实现更自然的代码结构和执行流程,从而改进代码可维护性。这也意味着,如果事务处理过程中发生中断,对持久状态所做的所有更改都将被回滚。

事务具有类似 ACID(原子性、一致性、隔离性和耐久性)的属性。以下是这些属性与 PKDK 编程的关系:

原子性:事务在持久性方面是原子性的;当事务成功完成时,在事务中做出的所有更改都会被提交,或全部不提交。

一致性:PMDK 支持用户保持数据一致性。

隔离:PMDK 库提供持久内存驻留同步机制,使开发人员能够保持隔离。

耐用性:所有交易的锁都保留到交易完成,以确保耐用性。

事务是在每个线程的基础上完成的,所以调用将返回调用线程所执行的最后一个事务的状态。事务是断电安全的,但不是线程安全的。

< p > 属性

在事务中,撤销日志用于创建用户数据快照。<p>模板包装类是用于自动生成用户数据快照的基本构建模块,因此应用开发人员无需手动执行这一步骤(就像 libpmemobj 的 C 实施一样)。这一包装类只支持基本类型。它的实施基于赋值运算符,并且每次为这个包装类的变量分配一个新值时,系统都将为该变量的旧值创建快照。由于创建快照是一个计算密集型操作,因此不鼓励在堆栈变量中使用<p>属性。

持久指针

PMDK 中的库建立在内存映射文件的概念之上。由于文件可以映射到进程虚拟地址空间的不同地址,因此不能使用存储绝对地址的传统指针。PMDK 引入了一种新的指针类型,它有两个字段:一个池的 ID(用于从转换表访问当前的池虚拟地址)以及与池起点之间的偏移。持久指针是围绕这个基本 C 类型的 C ++ 包装。其理念与 std::shared_ptr相似。

libpmemobj 核心概念

根对象

通过 libpmemobj 创建具有 PMEM 感知能力的代码始终需要设计持久化数据对象的类型,这是第一步工作。第一种需要定义的类型是根对象。这一对象是强制性的,用于固定在持久内存池中创建的所有其他对象(将池视为 PMEM 设备中的文件)。

池是一个连续的 PMEM 区域,由用户提供的标识符(称为布局)进行识别。可以使用不同的布局字符串创建多个池。

使用 C++ 绑定实施队列

本示例中的队列被实施为一个单独链接列表,表头和表尾展示了如何使用 libpmemobj 的 C ++ 绑定。

设计决策

数据结构

我们首先需要一个数据结构来描述队列中的节点。每个条目都有一个值和一个指向下一个节点的链接。根据下图,这两个变量都是持久内存感知变量。

Data structure map
图 1.描述队列实施的数据结构。

代码走查

现在我们来深入了解一下该程序的主要功能。运行代码时,您需要提供三个参数。一个是池文件的绝对位置,第二个是需要执行的实际队列操作。支持的队列操作是 推送(插入元素)、弹出(返回和移除元素)以及 显示(返回元素)。

if (argc < 3) {
	std::cerr << "usage: "<< argv[0]
	<< " file-name [push [value]|pop|show]"<< std::endl;
	return 1;
}

在下面的代码片段中,我们检查池文件是否存在。如果存在,池将打开。如果不存在,则创建池。布局字符串标识我们请求打开的池。在这里,我们打开布局名称为“队列”的池,其由程序中的宏 LAYOUT定义。

const char *path = argv[1];
queue_op op = parse_queue_op(argv[2]);
pool<examples::pmem_queue> pop;

if (file_exists(path) != 0) {
	pop = pool<examples::pmem_queue>::create(
		path, LAYOUT, PMEMOBJ_MIN_POOL, CREATE_MODE_RW);
} else {
	pop = pool<examples::pmem_queue>::open(path, LAYOUT);
}

弹出是指向池的指针,我们可以从中访问根对象(examples::pmem_queue的实例)的指针,Create 函数创建一个examples::pmem_queue类型的新 pmemobj 池。根对象就像文件系统的根,因为它可以用来访问池中的所有其他对象(只要这些对象链接正确,并且不会由于编码错误而丢失指针)。

auto q = pop.get_root();

一旦获得指向队列对象的指针,程序就会检查第二个参数,以便确定队列应执行的操作类型;也就是推送弹出显示

switch (op) {
	case QUEUE_PUSH:
		q->push(pop, atoll(argv[3]));
		break;
	case QUEUE_POP:
		std::cout << q->pop(pop) << std::endl;
		break;
	case QUEUE_SHOW:
		q->show();
		break;
	default:
		throw std::invalid_argument("invalid queue operation");
}

队列操作

推送

我们来看看如何实施 push函数,使其具有持久编程感知能力。如下面的代码所示,事务代码被实施为一个封装在 C++ 闭包中的 lambda 函数(支持轻松读取和遵循代码)。如果发生断电,数据结构不会被损坏,因为所有更改都会回滚。如欲了解关于如何用 C++ 实施事务的更多信息,请阅读 pmem.io 上的 libpmemobj 的 C++ 绑定(第六部分) - 事务。

分配函数也是事务性的,它们使用事务逻辑来支持持久状态的分配/删除回滚;make_persistent()是构造函数,delete_persistent()是析构函数。

在事务内调用 make_persistent()会分配一个对象并返回一个持久对象指针。由于分配现在是事务的一部分,因此如果中止,分配将进行回滚,将内存分配恢复到其原始状态。

分配之后,n的值将被初始化为队列中的新值,并且下一个指针将被设置为 null。

void push(pool_base &pop, uint64_t value) {
	transaction::exec_tx(pop, [&] {
		auto n = make_persistent<pmem_entry>();

		n->value = value;
		n->next = nullptr;

		if (head == nullptr && tail == nullptr) {
			head = tail = n;
		} else {
			tail->next = n;
			tail = n;
		}
	});
}

Data structure map for push functionality
2.推送功能的数据结构。

弹出

推送类似,弹出函数如下所示。这里我们需要一个临时变量来存储队列中下一个 pmem_entry的指针。在使用 delete_persistent()删除队列头后,我们需要使用临时变量将队列头设为下一个 pmem_entry。由于这是使用事务完成的,因此具有持久感知能力。

uint64_t pop(pool_base &pop){
	uint64_t ret = 0;
	transaction::exec_tx(pop, [&] {
		if (head == nullptr)
			transaction::abort(EINVAL);

		ret = head->value;
		auto n = head->next;

		delete_persistent<pmem_entry>(head);
		head = n;

		if (head == nullptr)
			tail = nullptr;
	});

	return ret;
}

Data structure map for pop functionality.
图 3.弹出功能的数据结构。

构建指令

运行代码示例的说明

从 PMDK GitHub* 库下载源代码:

  1. Git 克隆https://github.com/pmem/pmdk.git

    command window with GitHub command
    图 4.从 GitHub* 库下载源代码。

  2. cd pmdk,在命令行上运行 make,如下所示。这将构建完整的源代码树。

    command window with code
    图 5.构建源代码。

  3. cd pmdk/src/examples/libpmemobj++/queue
  4. 查看队列程序的命令行选项:
    ./queue
  5. 推送命令:
    ./queue TESTFILE push 8

    Command window with code
    图 6.使用命令行的推送命令。

  6. 弹出命令:
    ./queue TESTFILE pop
  7. 显示命令:
    ./queue TESTFILE show

    Command window with code
    图 7.使用命令行的弹出命令。

总结

在本文中,我们使用 PMDK 库 libpmemobj 的 C++ 绑定展示了一个简单的 PMEM 感知队列实施。如欲了解更多关于借助 PMDK 进行持久内存编程的信息,请访问英特尔® 开发人员专区 (Intel® DZ) 持久内存编程板块。您可以在该板块中找到相关文章、视频以及面向 PMEM 开发人员的其他重要资源的链接。

关于作者

Praveen Kundurthy 是英特尔的开发人员宣传官,在应用开发、优化和移植到英特尔平台方面拥有超过 14 年的经验。在过去的几年里,他一直从事存储技术、游戏、虚拟现实和基于英特尔平台的 Android 等方面的工作。

如何使用英特尔® Inspector - Persistence Inspector 检测持久内存编程错误

$
0
0

概述

持久内存一种新兴的内存存储技术,在提高应用性能和可靠性方面具有很大潜力。但开发人员需要解决几个编程挑战,以获得性能最佳的代码。其中一个挑战是,由于缓存问题,存储到持久内存的内容无法立即实现持久化。数据只有在离开缓存层级结构且对内存系统可见时,才能持久存留。由于处理器无序执行和缓存,持久性的顺序可能与存储的顺序不同。

Intel® Inspector - Persistence Inspector是一种新的运行时工具,目前提供技术预览版。开发人员可以用它来检测持久内存程序中的这些编程错误。除了缓存刷新未命中之外,该工具还可以检测

  • 冗余缓存刷新和内存屏障
  • 无序持久内存存储
  • 持久内存开发工具包 (PMDK) 的撤销日志错误

本文将介绍英特尔英特尔 Inspector - Persistence Inspector 的功能并提供入门指南。

背景

持久内存设备使用英特尔和美光科技* 联合开发的新技术(如 3D Xpoint™ 介质),可直接连接到内存控制器。这样的设备通常被称为非易失性双列直插式内存模块 (NVDIMM)。NVDIMM 中的数据支持字节寻址,可在系统或程序崩溃后保留下来。NVDIMM 的访问延迟与 DRAM 相当。程序使用常规的 CPU 加载/存储指令读取和写入 NVDIMM。考虑下面的代码示例:

 

示例 1:将通讯录写入持久内存

#include <stdio.h>
#include <fcntl.h>
#include <sys/file.h>
#include <sys/mman.h>
#include <string.h>

struct address {
	char name[64];
	char address[64];
	int valid;
}

int main()
{
	struct address *head = NULL;
	int fd;
	fd = open("addressbook.pmem", O_CREAT|O_RDWR, 0666);
	posix_fallocate(fd, 0, sizeof(struct address));

	head = (struct address *)mmap(NULL, sizeof(struct address), PROT_READ|PROT_WRITE,    MAP_SHARED, fd, 0);
    close(fd);

    strcpy(head->name, "Clark Kent");
	strcpy(head->address, "344 Clinton St, Metropolis, DC 95308");
	head->valid = 1;

    munmap(head, sizeof(struct address));

	return 0;
}

在示例 1 中,持久内存作为 addressbook.pmem文件公开,并使用常规文件系统 API 映射到进程地址空间。一旦映射到持久内存,程序就会直接访问内存,并开始调用 strcpy。如果再调用 munmap之前断电,持久内存可能会发生下列情形之一:

  • head->namehead->addresshead->valid都没有进入内存系统并持久留存。
  • 它们全部都持久留存。
  • 其中一个或两个持久留存。

缓存效应给持久内存软件开发带来了挑战。为确保数据在断电或系统崩溃后可恢复并保持一致,开发人员需要确定何时何地将缓存层级结构的数据明确刷新到内存系统。

持久内存应用行为

在发生断电或系统崩溃等不幸事件后重启时,持久内存应用必须验证一致性并对内存中存储的数据进行恢复。通常情况下,不幸事件会将持久内存应用分为两个阶段:一个是不幸事件之前的阶段,另一个是不幸事件之后的阶段。这可能会导致数据损坏或不一致。在不幸事件之前的阶段,应用运行读取和写入持久内存的正常流程。在不幸事件之后的阶段,系统检查数据一致性并在应用恢复正常运行之前将不一致的数据恢复到一致性状态。

 

Map of  memory unfortunate event

如何使用英特尔 Inspector - Persistence Inspector

这是英特尔 Inspector - Persistence Inspector 的使用流程:

A flowchart, usage workflow of Intel® Inspector

设置环境

将工具文件安装到所选目录(例如/home/joe/pmeminsp)后,将英特尔 Inspector - Persistence Inspector 路径添加到 PATHLD_LIBRARY_PATH环境变量。例如:

$ export PATH=/home/joe/pmeminsp/bin64:$PATH

$ export LD_LIBRARY_PATH=/home/joe/pmeminsp/lib64:$LD_LIBRARY_PATH 要验证该工具是否正确安装和设置,可以键入“pmeminsp”。

$ pmeminp 键入 'pmeminsp help’ 以便使用。

准备应用

如前所述,持久内存应用通常由两个阶段组成。若要使用英特尔 Inspector -Persistence Inspector,您需要确定这两个阶段的代码。

  • 不幸事件之前的阶段。执行您希望工具检查的代码。
  • 不幸事件之后的阶段。执行您将在断电或系统崩溃后运行的代码。

如果您的应用使用 持久内存开发工具包 (PMDK)实现对事务的持久内存支持,则 PMDK 事务运行时支持负责事务块内的数据一致性和恢复。不幸事件后的代码驻留在 PMDK 事务运行时中。因此,无需使用英特尔 Inspector - Persistence Inspector 验证该特定阶段。

通知英特尔® Inspector - Persistence Inspector 不幸事件之后的阶段

一旦确定不幸事件之后的阶段,我们强烈建议您将不幸事件之后阶段的确切开始时间和结束时间通知工具,以大幅缩短分析时间。英特尔 Inspector - Persistence Inspector 提供一套面向应用的 API,用于在不幸事件之后阶段开始和结束时通知工具。

#define PMEMINSP_PHASE_AFTER_UNFORTUNATE_EVENT 0x2 void __pmeminsp_start(unsigned int phase); void __pmeminsp_pause(unsigned int phase); void __pmeminsp_resume(unsigned int phase); void __pmeminsp_stop(unsigned int phase);

若要将不幸事件之后阶段的开始时间通知工具,您只需在不幸事件之后阶段开始之前,在应用中调用 __pmeminsp_start (PMEMINSP_PHASE_AFTER_UNFORTUNATE_EVENT)。同样,若要将不幸事件之后阶段的结束时间通知工具,您只需在不幸事件之后阶段结束后,在应用中调用__pmeminsp_stop(PMEMINSP_PHASE_AFTER_UNFORTUNATE_EVENT)

__pmeminsp_pause(PMEMINSP_PHASE_AFTER_UNFORTUNATE_EVENT) and __pmeminsp_resume (PMEMINSP_PHASE_AFTER_UNFORTUNATE_EVENT)调用可让您更精细地控制分析开始之后和结束之前的分析暂停和恢复。

例如,如果不幸事件之后阶段是函数 recover()调用的持续时间,您只需在函数入口放置 __pmeminsp_start(PMEMINSP_PHASE_AFTER_UNFORTUNATE_ EVENT),并在函数出口放置 __pmeminsp_stop(PMEMINSP_PHASE_AFTER_ UNFORTUNATE_EVENT)即可。

示例 2:将不幸事件之后阶段的开始时间和结束时间通知英特尔 Inspector - Persistence Inspector。

#include “pmeminsp.h”

 …

 … …

void recover(void)
{
    __pmeminsp_start(PMEMINSP_PHASE_AFTER_UNFORTUNATE_EVENT);

    …

    __pmeminsp_stop((PMEMINSP_PHASE_AFTER_UNFORTUNATE_EVENT);
}

void main()
{

    …

    … = mmap(…..);

    __pmeminsp_stop((PMEMINSP_PHASE_AFTER_UNFORTUNATE_EVENT);

    …
    recover();
}

英特尔 Inspector - Persistence Inspector API 在libpmeminsp.so中定义。当您构建应用时,确保指定正确的选项。例如

-I /home/joe/pmeminsp/include –L /home/joe/pmeminsp/lib64 –lpmeminsp.

分析不幸事件之前的阶段

运行不幸事件之前分析阶段的命令为

pmeminsp check-before-unfortunate-event [options] --

如果应用使用系统 API 直接映射持久内存文件,您还需要使用 option -pmem-file指定该文件的路径(即使该文件在应用运行之前不存在)。例如:

pmeminsp check-before-unfortunate-event -pmem-file ./addressbook.pmem [options] --

如果应用创建了多个文件,应该可以使用这些文件指定文件夹的路径。从该位置映射的任何文件都将被解析为持久内存文件。

请注意,如果您的应用是基于 PMDK 的,那么这两个选项是多余的。英特尔 Inspector - Persistence Inspector 会自动跟踪由 PMDK 管理的所有持久内存文件,即使这些选项不存在。

分析不幸事件之后的阶段

运行不幸事件之后分析阶段的命令为

pmeminsp check-after-unfortunate-event [options] --

确保指定持久内存文件的位置,除非应用使用 PMDK 来运行持久内存。选项名称与 check-before-unfortunate-event命令类似。

检测到报告问题

要生成检测到的持久内存问题的报告,请运行

pmeminsp report [option] --

以下是英特尔 Inspector - Persistent Inspector 生成的一些诊断示例:

缺少缓存刷新

持久内存存储(第一个缓存)的缺失缓存刷新始终参照后面的持久内存存储(第二个存储)。其潜在的不利影响是,如果在第二个存储之后发生不幸事件,第二个存储将成为持久存储,但第一个存储不会成为持久存储。

第一个内存存储

in /home/joe/pmeminsp/addressbook/writeaddressbook!main at writeaddressbook.c:24 - 0x6ED, in /lib/x86_64-linux-gnu/libc.so.6!__libc_start_main, at: - 0x21F43 in /home/joe/pmeminsp/addressbook/writeaddressbook!_start at: - 0x594

,

未在第二个内存存储之前刷新

in /home/joe/pmeminsp/addressbook/writeaddressbook!main at: writeaddressbook.c:26 - 0x73F, in /lib/x86_64-linux-gnu/libc.so.6!__libc_start_main, at: - 0x21F43, in /home/joe/pmeminsp/addressbook/writeaddressbook!_start at: - 0x594

.

内存从第一个存储的位置加载

in /lib/x86_64-linux-gnu/libc.so.6!strlen at: - 0x889DA

取决于从第二个存储的位置加载的内存

in /home/joe/pmeminsp/addressbook/readaddressbook!main at readaddressbook.c:22 - 0x6B0.

冗余或不必要的缓存刷新

冗余或不必要的缓存刷新可以从分析的执行路径中删除,但不影响程序的正确性。尽管冗余或不必要的缓存刷新不会影响程序的正确性,但它可能会影响程序性能。

缓存刷新

in /home/joe/pmeminsp/tests/pmemdemo/src/pemmdemo!test_redundant_flush at main.cpp:134 - 0x1721, in /home/joe/pmeminsp/tests/pmemdemo/src/pemmdemo!create_data_file at main.cpp:52 - 0x151F,in /home/joe/pmeminsp/tests/pmemdemo/src/pemmdemo!create_data_file at main.cpp:48 - 0x14FD,in /home/joe/pmeminsp//tests/pmemdemo/src/pemmdemo!main at main.cpp:231 - 0x1C74,in /lib/x86_64-linux-gnu/libc.so.6!__libc_start_main, at: - 0x21F43,in /home/joe/pmeminsp/tests/pmemdemo/src/pemmdemo!_start at: - 0x12A4

在缓存刷新方面是冗余的

in /home/joe/pmeminsp/tests/pmemdemo/src/pemmdemo!test_redundant_flush at main.cpp:135 - 0x1732,in /home/joe/pmeminsp/tests/pmemdemo/src/pemmdemo!create_data_file at main.cpp:52 - 0x151F,in /home/joe/pmeminsp/tests/pmemdemo/src/pemmdemo!create_data_file at main.cpp:48 - 0x14FD,in /home/joe/pmeminsp/tests/pmemdemo/src/pemmdemo!main at main.cpp:231 - 0x1C7,in /lib/x86_64-linux-gnu/libc.so.6!__libc_start_main at: - 0x21F43,in /home/joe/pmeminsp/tests/pmemdemo/src/pemmdemo!_start at: - 0x12A4

内存存储

in /home/joe/pmeminsp/tests/pmemdemo/src/pemmdemo!test_redundant_flush at main.cpp:133 - 0x170F,in /home/joe/pmeminsp/tests/pmemdemo/src/pemmdemo!create_data_file at main.cpp:52 - 0x151F,in /home/joe/pmeminsp/tests/pmemdemo/src/pemmdemo!create_data_file at main.cpp:48 - 0x14FD,in /home/joe/pmeminsp/tests/pmemdemo/src/pemmdemo!main at main.cpp:231 - 0x1C74,in /lib/x86_64-linux-gnu/libc.so.6!__libc_start_main at: - 0x21F43,in /home/joe/pmeminsp/tests/pmemdemo/src/pemmdemo!_start at: - 0x12A4

无序持久内存存储

无序持久内存存储是无法执行正确持久顺序的两个存储。值得指出的是,显式高速缓存刷新不会执行正确的顺序。
无序持久内存存储可以通过 'report’ 命令中的查看无序存储 (check-out-of-order-store) 选项进行启用。

内存存储

in /home/joe/pmemcheck/mytest/writename6!writename at writename6.c:13 - 0x6E0, in /home/joe/pmemcheck/mytest/writename6!main at writename6.c:21 - 0x72D, in /home/joe/pmemcheck/mytest/writename6!main at writename6.c:20 - 0x721, in /lib/x86_64-linux-gnu/libc.so.6!__libc_start_main at: - 0x21F43,in /home/joe/pmemcheck/mytest/writename6!_start at: - 0x594

应在内存存储之后

in /home/joe/pmemcheck/mytest/writename6!writename at writename6.c:14 - 0x6EB,in /home/joe/pmemcheck/mytest/writename6!main at writename6.c:21 - 0x72D,in /home/joe/pmemcheck/mytest/writename6!main at writename6.c:20 - 0x721, in /lib/x86_64-linux-gnu/libc.so.6!__libc_start_main at: - 0x21F43,in /home/joe/pmemcheck/mytest/writename6!_start at: - 0x594

不撤销记录更新

开发人员负责在 PMDK 事务中更新内存位置之前撤消其日志记录。开发人员通常调用 PMDK 函数 pmemobj_tx_add_range()pmemobj_tx_add_range_direct(),,或使用宏 TX_ADD()撤消内存位置的日志记录。如果内存位置在更新之前未被撤消记录,那么如果未成功提交事务,则 PMDK 无法将变更回滚到内存位置。

有用户报告无撤消记录更新的问题,并提到内存存储和更新内存的事务。

内存存储

in /home/joe/pmeminsp/tests/pmemdemo/src/pemmdemo!test_tx_without_undo at main.cpp:190 - 0x1963,in /home/joe/pmeminsp/tests/pmemdemo/src/pemmdemo!run_tx_test at main.cpp:175 - 0x1877,in /home/joe/pmeminsp/tests/pmemdemo/src/pemmdemo!run_tx_test at main.cpp:163 - 0x180D,in /home/joe/pmeminsp/tests/pmemdemo/src/pemmdemo!main at main.cpp:245 - 0x1CF4,in /lib/x86_64-linux-gnu/libc.so.6!__libc_start_main at: - 0x21F43,in /home/joe/pmeminsp/tests/pmemdemo/src/pemmdemo!_start at: - 0x12A4

事务未撤销记录

in /home/joe/pmeminsp/tests/pmemdemo/src/pemmdemo!test_tx_without_undo at main.cpp:185 - 0x1921,in /home/joe/pmeminsp/tests/pmemdemo/src/pemmdemo!run_tx_test at main.cpp:175 - 0x1877,in /home/joe/pmeminsp/tests/pmemdemo/src/pemmdemo!run_tx_test at main.cpp:163 - 0x180D,in /home/joe/pmeminsp/tests/pmemdemo/src/pemmdemo!main at main.cpp:245 - 0x1CF4,in /lib/x86_64-linux-gnu/libc.so.6!__libc_start_main at: - 0x21F43, in /home/joe/pmeminsp/tests/pmemdemo/src/pemmdemo!_start at: - 0x12

无更新的日志撤销

开发人员负责在 PMDK 事务中更新内存位置之前撤消其日志记录。开发人员通常调用 PMDK 函数 pmemobj_tx_add_range()pmemobj_tx_add_range_direct(),或使用宏 TX_ADD()撤销内存位置的日志记录。如果内存位置被撤销记录,但从未在 PMDK 事务内更新,那么性能可能会降低。或者如果未成功提交事务,内存可能会回滚到 dirty/uncommitted/stale value

有用户报告无更新撤销记录的问题,并提到 PMDK 撤销记录调用和内存被撤销记录的交易。

Memory region is undo logged in /home/joe/pmeminsp/tests/pmemdemo/src/pemmdemo!test_tx_without_update  at main.cpp:190 - 0x1963, in /home/joe/pmeminsp/tests/pmemdemo/src/pemmdemo!run_tx_test at main.cpp:175 - 0x1877, in /home/joe/pmeminsp/tests/pmemdemo/src/pemmdemo!run_tx_test at main.cpp:163 - 0x180D, in /home/joe/pmeminsp/tests/pmemdemo/src/pemmdemo!main at main.cpp:245 - 0x1CF4, in /lib/x86_64-linux-gnu/libc.so.6!__libc_start_main at: - 0x21F43, in /home/joe/pmeminsp/tests/pmemdemo/src/pemmdemo!_start at: - 0x12A4

但未在事务中更新

in /home/joe/pmeminsp/tests/pmemdemo/src/pemmdemo! test_tx_without_update at main.cpp:185 - 0x1921, in /home/joe/pmeminsp/tests/pmemdemo/src/pemmdemo!run_tx_test at main.cpp:175 - 0x1877, in /home/joe/pmeminsp/tests/pmemdemo/src/pemmdemo!run_tx_test at main.cpp:163 - 0x180D, in /home/joe/pmeminsp/tests/pmemdemo/src/pemmdemo!main at main.cpp:245 - 0x1CF4, in /lib/x86_64-linux-gnu/libc.so.6!__libc_start_main at: - 0x21F43, in /home/joe/pmeminsp/tests/pmemdemo/src/pemmdemo!_start at: - 0x12A4.

结论

持久内存是一项令人兴奋的新技术。正如我们在本文中讨论的,这一技术带来了一些编程挑战。使用英特尔 Inspector - Persistence Inspector 等工具可以帮助应对这些挑战,让您在程序生命周期的早期发现问题,并获得非常高的投资回报。

后续步骤

若要试用英特尔® Inspector - Persistent Inspector 测试版,请访问测试版注册网站并下载技术预览版。

使用预测性维护提高效率,延长运行时间

$
0
0

对于当今的许多制造工厂,监控流程高度依赖人工。FOURDOTONE Teknoloji 分析来自传感器的数据,支持制造商即时响应问题,并预测机器何时会出现故障。

要点综述

停机可能会造成重大经济损失,在一条紧密耦合的生产线上,一台机器的问题可能对整个工厂产生影响。对于许多工厂,避免停机依靠的是运气,而不是科学:它们不经常进行机器检查,只能发现肉眼可见的问题。

FOURDOTONE Teknoloji 的 4.1 工业物联网平台(4.1 IIoTP)提高了制造商的响应速度和前瞻性,支持他们最大限度地减少停机时间。从机器中收集数据,并在工厂内分析,从而确保能够即时响应紧急情况或突发问题。在云中,使用机器学习算法分析来自所有机器的组合数据,通过这种方式可以预测出未来的维护要求。这有助于制造商制定维护计划(以避免停机)以及优化维护成本。

该技术为持续改进提供了基础,并帮助制造商降低意外停机的风险。

FOURDOTONE Teknoloji 于 2014 年在土耳其成立,专门处理工业 4.0 项目。该公司承接硬件独立项目,包括状态监控、大数据流程优化、预测性维护和数字工厂。该公司的服务对象包括土耳其、中东欧和中东的企业。

业务挑战:避免行业内的停机

停机可能会对制造工厂的业务造成巨大的影响。单台机器的故障可能会中断整条生产线。对于 24 小时运转的工厂,停工的时间是无法弥补的。产量的意外下降可能会导致等待交货的客户对企业感到失望,可能会使企业失去订单,导致产品无法销售,从而直接影响收益。

对于许多制造企业,意外停机是难以避免的。维护依然采取被动的方式。公司无法实时监控与分析它们的工厂,因此,在机器骤然停止前,它们对问题一无所知。由于缺乏基于以往表现的可靠数据,它们无法预测机器何时会发生故障。

工厂管理是一项劳动密集型工作,很容易忽略重要信号。工人可能穿梭于机器之间,用肉眼检查任何异常,并在写字板上记下数据。人工观察和通常并不频繁的检查使工人难以发现潜在问题,即使发现了问题也只是碰巧而已。如果检查没有在正确的时间和地点内实施,便无法发现可能已经影响到性能的问题,最终引发停机。

机器监控的挑战

Scattolini 的土耳其工厂也面临着这些挑战。该工厂生产商用车的浮子和翻转机。公司的总部设在意大利,位于 Valeggio sul Mincio 的工厂和全球其他 7 家工厂生产 200 多种设备。土耳其工厂生产厢式货车的部件。

运行时间对运营和盈利至关重要。公司想从被动维护转变为预测性维护:确保能在每次停机前进行干预。停产一天可能造成多达 35,000 欧元的损失,包括维修成本。

现有的机制基于手动检查,每月进行一次振动测量和被动维护。公司需要想办法:

  • 收集工厂内 30 多台机器的数据,无需到达现场。机器包括起重机、风扇和泵;
  • 监控液体化学成分池的级别;
  • 在问题出现时确定问题,立即采取措施,以最大限度地减少停机;
  • 模拟未来可能出现的故障,确保维护团队在停机前进行维修和替换。

解决方案价值:支持预测性维护

4.1 IIoTP 通过无线的方式收集与分析来自车间的数据。在 Scattolini 的土耳其工厂内,收集的数据包括轴向振动、发动机的表面温度、液压与气压系统的压力、罐和池的液位以及主能量线的状态。该解决方案支持工厂更及时地获取更多信息,这在之前是无法实现的。因此,公司可以更清楚地了解工厂的状态和维护要求。数据分析分为两个阶段:首先,如果传入数据出现了异常,则立即发出警报。将短信或电子邮件发送至预定义用户组。如果没有响应,或存在安全问题,可以将软件配置为自动关闭机器。

第二阶段的分析是在云中进行的。4.1 IIoTP 结合了来自所有机器的数据,用于预测未来可能发生的停机和维护要求。该方法使用机器学习技术来对比之前故障的已知信息和工厂与设备的当前数据。通过在潜在故障发生前替换部件,Scattolini 可以避免意外停机。

Scattolini 土耳其工厂的团队可以使用计算机、手机或平板电脑上的跨平台门户,实时监控工厂的状态。

通过减少对手动监控的使用以及将工厂转型为主动维护模式,Scattolini 估计已降低 15% 的维护运营成本。

解决方案架构:预测性维护

为了支持预测性维护,4.1 IIoTP 提供了一整套机制:从车间收集数据,如果要求即时响应,则立即分析数据,在云内执行深度分析,以生成预测性维护。

被监控的机器配备了电池或直流供电的无线工业传感器,专为实现高精度和在恶劣环境下运转而制造。

4.1 IIoTP 使用戴尔 Edge* Gateway 5000 中的英特尔® 物联网网关技术收集来自传感器的数据。支持有线和无线连接。坚固耐用的无风扇网关设备基于英特尔® 凌动™ 处理器,为网络边缘的设备提供了智能优势。边缘的计算能力支持快速是决策制定,这对重工业工厂、快速移动的生产线、化学物质存储设施等至关重要。

监督控制与数据收集(SCADA)工业控制系统通常显示当前数据。4.1 IIoTP 添加了查看历史数据和自动检测边缘异常和阈值违规的功能。可以通过电子邮件或短信发出警报,也可以由 4.1 IIoTP 直接干预,包括更改机器的配置或关闭机器。该功能由支持 4.1 IIoTP 控制可编程逻辑控制器(PLC)的库和框架提供。支持西门子、欧姆龙、三菱等厂家生产的主要 PLC。

此外,使用 256 位加密技术将数据发送至云。使用 4G/GPRS 移动宽带通信技术将数据发送至云,这是因为在工业环境中,该技术比 Wi-Fi 更稳定。在云中,将来自所有网关的数据收集到一个地方,然后使用机器学习算法对数据进行分析。该分析方法可用来预测可能产生的机器故障,发现提升效率和质量的其他机遇。可以通过机器学习创建面向边缘分析的新规则,以支持持续改进。


图 1. 4.1 IIoTP 借助机器学习和边缘分析,对紧急问题进行即时响应并在云中执行深度分析,以实现持续改进

云基础设施基于 Amazon Web Services(AWS*)。Amazon Kinesis 流* 专为连续捕捉大量数据而设计,4.1 IIoTP 使用该服务收集来自被监控设备的数据。这些数据还被添加至 Amazon Simple Storage Service*(Amazon S3*),发挥数据湖的作用,汇集来自不同类型被监控设备的数据。Amazon Elastic MapReduce*(Amazon EMR*)被用于设置 Spark* 集群,以及将 S3 对象映射至 EMR 文件系统,以便使用 S3 存储器中的数据执行批量分析。预测模型在 EMR 上运行,Kinesis 流也可能会消耗部分数据,以实时分析传感器数据。同时使用 Amazon Elastic Compute Cloud*(Amazon EC2*)在 AWS 上托管数据库、API 和 web 服务器。用户可以选择支持互联网的设备,通过可视化界面远程监控平台。

结论

制造商可以使用 4.1 IIoTP,并在机器中安装传感器,以深入了解其工厂设备当前与未来的健康状况。如果出现了紧急情况、断电或技术故障,边缘分析能提供即时响应。云中的机器学习算法可以分析所有机器在一定时间内生成的所有数据,以完善告警规则,并提供有关机器维护或替换的最佳时机的洞察。这种智能不仅能帮助制造商避免意外停机,降低手动监控机器的人力成本,优化部件和维护开销,还可以支持制造商提高其制造基础设施的可靠性和可预测性。

寻求适合贵公司的解决方案。联系您的英特尔代表或访问 www.intel.cn/content/www/cn/zh/industrial-automation/overview.html

英特尔解决方案架构师是技术方面的专家,他们与世界最大、最成功的公司合作设计业务解决方案,以应对严峻的业务挑战。在特定业务用例中成功测试、试运行和/或部署过这些解决方案的客户提供了宝贵的真实体验,为创建和改进这些解决方案奠定了良好的基础。封页上列出了撰写本解决方案简介的解决方案架构师和技术专家。

如欲了解更多信息,请访问:

英特尔与 ISV   工业自动化   重要的物联网网站

了解更多信息


使用全新的英特尔® System Studio 2018 加快系统和物联网设备应用开发速度

$
0
0

Intel System Studio Usages

简化系统启动,提升性能和可靠性

系统和物联网开发人员的开发工作变得更轻松了。英特尔刚刚推出的 英特尔® System Studio 2018是一款用于系统和物联网设备应用开发的全面的一体化、跨平台工具套件。 新版(对 2017 版的更新) 面向在基于英特尔® 处理器的平台上运行的智能系统和物联网设备应用提供了全新的工具、 库、代码示例 和功能,有助于缩短开发周期,帮助开发人员加快产品的上市速度,提升性能、功效可靠性

有关该版本新特性和 英特尔 System Studio常见用例、使用群体和版本的详细信息,请参阅下文。

立即下载英特尔® System Studio 2018
此外,开发人员可以获得免费的 90 天可更新商业许可(包括公共社区论坛支持)和付费许可产品,为用户提供了与英特尔工程师私下沟通技术问题的优先支持。

 

Intel System Studio英特尔 System Studio 2018 的新特性

  • 新 库和代码示例帮助缩短开发周期。通过使用英特尔® 数据分析加速库,开发人员可以提升边缘分析处理和机器学习的速度。
  • 全新的物联网连接工具,包括高级云接口和 400 多个传感器的访问支持。
  • 最新款英特尔® 处理器支持-使用英特尔® AVX-512 指令优化系统和代码性能。1
  • 调试功能和增强的工作流程,可简化目标设备的系统验证,支持自动跟踪,确保可靠的端到云数据交换等。
  • 免费的 90 天可更新商业许可,可以进行无限次更新,以使用最新版本
  • 自定义软件下载的新功能-只获取您需要的工具。

如欲接收产品更新,用户必须在 英特尔® 软件开发产品注册中心注册或开立账户。

新特性与功能详情

下文提供了关于新特性和功能的更多详情。 您可以在工具套件和单个组件工具的版本说明中查看更多信息。

面向英特尔 System Studio 2018 的 Eclipse* IDE

  • 创建了面向英特尔 System Studio 2018 的 Eclipse* IDE 英特尔版本
  • 创建了服务于英特尔 System Studio 产品的模块化 Eclipse IDE 结构
  • 将英特尔  System Studio IoT Edition 集成至英特尔  System Studio 2018
  • 改进远程 Linux* 操作系统目标支持 
    • 添加面向目标连接的 Eclipse 目标通信框架支持
    • 添加基本 Makefile 支持
  • 添加面向英特尔® C++ 编译器集成的向导
    • 添加面向 Linux 主机的本地编译器集成
    • 添加交叉编译集成,包括 Linux 和 Android* 操作系统目标支持
  • 改善普通用户的体验
    • 自定义视角
    • 实施以英特尔 System Studio 用例为重点的向导
    • 禁用不受支持的向导

如果您在首次创建交叉编译项目时需要帮助,请参阅 《交叉开发》一文

关于如何使用基于全新容器的工作流程创建项目的视频,请访问:示例入门页面

英特尔® C++ 编译器 18.0 

  • 控制流强制(Control-Flow Enforcement,缩写为 CET)技术支持
  • 新选项- Qimf-use-svml,用于加强短矢量数学库(SVML)
  • 面向 SVML 调用的编译时间调度
  • 所有 -o* 选项被替换为 -qo* 选项
  • 基于硬件的按配置优化(PGO)支持
  • OpenMP* TR4 版本 5.0 Preview 1
  • OpenMP* 4.0 或更高版本中更多的新特性支持
  • 全新的 C++17 特性支持
  • C++11 中引入的原子关键字支持
  • 新选项-定义 ZMM 寄存器使用级别的 qopt-zmm-usage

另请参阅

英特尔® 数学核心函数库 2018(英特尔® MKL)

  • BLAS 特性
    • 引入“紧凑型 GEMM”和“紧凑型 TRSM”函数,以处理矩阵群,并添加了服务功能,以支持全新的格式。
    • 引入经过优化的整数矩阵乘法例程,以处理面向所有架构的量化矩阵。
  • BLAS 优化
    • 使用 AVX512_4FMAPS 和 AVX512_4VNNIW 指令组支持优化了面向英特尔® 高级矢量扩展指令集 2(英特尔® AVX-2)和英特尔® AVX-512 的 GEMM_S8U8S32 和 GEMM_S16S16S32。
  • 深度神经网络
    • 添加了面向非方形池化内核的支持。
    • 优化了普通(nchw, nhwc)和内部数据布局之间的转换。
  • LAPACK
    • 添加了面向小型矩阵(N<16)的改进和优化。
    • 添加了面向细长型和短粗型矩阵的 ?gesvd、?geqr/?gemqr、?gelq/?gemlq 优化。
    • 添加了面向 ?pbtrsroutine 的优化。
    • 添加了面向英特尔® 线程构建模块(英特尔® TBB)层的 ?potrf 例程优化。
    • 添加了面向 CS 分解例程 :?dorcsd 和 ?orcsd2by1 的优化。
    • 引入因子分解,并解决基于 Aasen 的 :?sytrf_aa/?hetrf_aa, ?sytrs_aa/?hetrs_aa 算法的例程。
    • 引入面向对称不定(或埃尔米特不定)因子分解的全新(更快)的 _rk 例程,包含有界 Bunch-Kaufman(rook)旋转算法。
  • ScaLAPACK
    • 针对 JOBZ=’N’(仅限特征值)案例添加了面向 p?syevr/p?heevr 例程的优化(二阶段频段压缩)。
  • FFT
    • 引入面向 FFT 域的 Verbose 支持,允许用户捕捉面向英特尔® MKL 的 FFT 描述符信息。
    • 提升了面向英特尔® 至强® 处理器(支持英特尔 AVX-512)的 2D 实部输入转复数输出和复数输入转实部输出矩阵乘法性能。
    • 提升了面向英特尔 至强 处理器(支持英特尔 AVX-512)的 3D 复数输入转复数输出性能。
  • 英特尔优化型高性能共轭梯度性能指标评测
    • 采用英特尔 MKL API 的新版性能指标评测
  • 稀疏 BLAS
    • 引入对称高斯-塞得尔预处理器。
    • 引入对称高斯-塞得尔预处理器,对结果和初始阵列进行 ddot 计算。
    • 稀疏 Matvec 例程,对结果和初始阵列进行 ddot 计算。
    • 稀疏 Syrk 例程,包含 OpenMP 和英特尔® TBB 支持。
    • 提升了面向英特尔 AVX-512 指令集的稀疏 MM 和 MV 功能的性能。
  • 面向集群的直接稀疏解算器
    • 添加转置解算器支持
  • 矢量数学
    • 添加了 24 种功能,包括面向基于英特尔 AVX-512 的处理器优化。
  • 数据拟合
    • 在支持英特尔 AVX-512 的英特尔 至强 处理器上,对 ILP64 接口中基于三次样条的内插进行了多达 8 次优化。

另请参阅:

英特尔® 数据分析加速库(英特尔® DAAL)

  • 引入 API 修改,以简化库的使用,并支持不同功能的一致性。
  • 引入面向分类与回归的决策树支持。该特性包括计算面向分类的基尼指数和信息增益、适用于回归分割标准的均方误差(MSE)和减少的错误修剪。
  • 引入了面向分类与回归的决策森林支持。该特征包括计算面向分类的基尼系数、回归分割标准的方差、泛化误差和变量重要性测量方法,如平均不纯度减少和平均精度减少。
  • 在面向神经网络训练的随即梯度下降算法中引入对不同学习速度的支持。
  • 引入对数据来源进行过滤的支持,包括从 CSV 数据来源加载选定的特性/列和分类特征的的二进制表示
  • 通过添加元素智能加法层扩大神经网络层。
  • 引入新示例,支持使用 Spark* MLlib 轻松集成库
  • 引入支持线程锁定的服务方法;提升了支持英特尔AVX-512 的英特尔 至强 处理上各种算法的性能。

有关英特尔® DAAL 的更多信息,详见: 英特尔® DAAL 简介 

英特尔® 集成性能基元 2018(英特尔® IPP)

  • 面向 LZ4 数据压缩与解压的优化函数,一种快速压缩算法,适用于注重速度的应用,尤其是通信通道。
  • 面向 GraphicsMagick*(一种主流图像处理工具箱)的优化函数,使用该函数的客户可以提升性能使用 ;使用英特尔® IPP 函数的普适性优化。
  • 移除了主软件包中的加密代码关联组件。
  • 平台感知型 API 的扩展支持,自动检测图像向量和长度为 32 位还是 64 位,提供面向图像尺寸和向量长度的 64 位参数,并从用户处提取该参数。

另请参阅:使用英特尔® 集成性能基元构建更快速的 LZ4

英特尔® 线程构建模块 2018(英特尔® TBB)

  • this_task_arena::isolate() 函数目前是一个完全受支持的特性。 同时,对 this_task_arena::isolate() 函数和 task_arena::execute() 方法进行扩展,以传输已执行的函子返回的值(该特性需要 C++11)。对 task_arena::enqueue() 和 task_group::run() 方法进行了扩展,以接受仅移动函子。
  • 添加对 Android* NDK r15、r15b 的支持。
  • 添加面向 Universal Windows Platform* 的支持。

物联网连接工具:MRAA 与amp; UPM 库

  • 包括 400 多个传感器与制动器库,内置 GUI 可用于访问存储库
  • 库支持,包括对 Ubuntu*、Wind River Linux* 和 Wind River Pulsar* 的支持
  • 额外的示例,包括展示如何结合使用 MRAA、UPM 和各种云服务的示例。

另请参阅:使用英特尔® System Studio -传感器库进行开发 

英特尔® VTune™ Amplifier 2018 

  • 更轻松地分析远程 Linux 系统
    • 在远程 Linux 目标上自动安装英特尔® Vtune™ Amplifier 收集器。
  • 增强了 Python* 分析
    • 锁定并等待混合 Python*和本地代码的分析调试线程性能。
    • 预览版:内存使用分析。Python,C,C++。
  • 优化基于私有云的应用 
    • 在 Docker 与 Mesos 容器内进行分析。
    • 连接至运行的 Java 服务和后台程序。
  • 媒体开发人员:GPU 内核分析
    • 分析 GPU 内核执行,以发现内存延迟或效率低下的内核算法。
  • 使用英特尔 TBB 简化应用的线程优化
    • 高级线程分析扩展了高开销和旋转时间的分类。 
  • 最新的处理器
    • 全新英特尔® 处理器,包括英特尔 至强 可扩展处理器。
  • 面向所有受支持操作系统的跨操作系统分析
    • 下载所需的其他操作系统,如在 Linux 上收集数据,然后在 Windows*或 macOS* 上分析数据。

另请参阅:

能耗分析/英特尔® SoC 手表

  • 添加用于能耗分析的 Eclipse* 插件 [预览版]

另请参阅:英特尔® System Studio 2018 中的能耗分析

英特尔® Inspector 2018

  • C++17 std::shared_mutex 和 Windows SRW Lock 支持,支持对包含读/写同步基元的应用进行线程错误分析。
  • 支持针对所有许可类型的跨操作系统分析。面向其他操作系统的安装包可从 registrationcenter.intel.com 中下载。
  • Microsoft Visual Studio 2017* 集成与支持。

英特尔® 图形性能分析器

  • 多帧分析器特性包 1
  • 跟踪分析器 PA 更换
  • 第八代智能英特尔酷睿处理器(原 Kaby Lake Refresh)Windows 10 支持
  • Windows Redstone 3 支持

英特尔® 系统调试器 2018

  • 添加了用于连接目标系统的新方法,并将其命名为目标连接代理(Target Connection Agent)。
  • 面向 Windows 和 Linux 主机添加了英特尔凌动® 处理器 C3xxx 目标支持。
  • 面向 Windows 主机添加了英特尔至强 可扩展处理器/ 英特尔® C620 系列芯片组目标支持。
  • 面向 Windows 主机添加了 第八代智能英特尔®酷睿处理器/ 英特尔® 100 系列芯片组支持。
  • 面向 Windows 主机添加了第八代智能英特尔酷睿 处理器/ 英特尔®  Z370 系列芯片组目标支持。

另请参阅: 使用目标连接代理配置英特尔® 系统调试器

面向 WinDbg* 的英特尔® 调试扩展

  • WinDbg* 支持 Windows 驱动程序工具包(WDK)版本 1703。添加了面向全新 eXDI 回调(DBGENG_EXDI_IOCTL_V3_GET_NT_BASE_ADDRESS_VALUE)的支持,以分配 windows 密钥结构 KdVersionBlock。
  • 针对英特尔® 处理器跟踪插件扩展了面向 WinDbg* 的英特尔® 调试扩展,以支持 Windows public symbol 信息。
  • 针对英特尔 处理器跟踪插件扩展了面向 WinDbg* 的英特尔 调试扩展,以支持环 3 跟踪。
  • 针对英特尔 处理器跟踪插件扩展了面向 WinDbg* 的英特尔 调试扩展,以支持解码来自崩溃转储的英特尔® 处理器跟踪数据。

GNU* GDB 与来源

  • 在 Linux 中添加了面向 PKeys 硬件寄存器的可视化工具和 GS_base 与 FS_base 系统寄存器。
  • 添加了面向英特尔® 处理器跟踪的 Python* 回调。

如有疑问或需要技术支持,请访问 英特尔® 软件产品支持

关于英特尔 System Studio

 
英特尔 System Studio 包含 3 个版本
  • 编译器版
  • 专业版
  • 旗舰版

这套全面的工具套件帮助简化了开发流程,以便开发人员加快从原型设计到生产的过程。 该套件受到设备制造商、系统集成商以及嵌入式和 物联网应用开发人员的青睐,用于开发可从系统和物联网应用改进中受益的解决方案,包括工业与制造、医疗保健、零售、智慧城市/楼宇/家居、交通、办公室自动化等领域。了解更多信息。

 

 

1优化声明:英特尔编译器针对英特尔微处理器的优化程度可能与针对非英特尔微处理器的优化程度不同。这些优化包括 SSE2、SSE3 和 SSSE3 指令集和其他优化。对于非英特尔微处理器上的任何优化是否存在、其功能或效力,英特尔不做任何保证。本产品中依赖于微处理器的优化仅适用于英特尔微处理器。不具体针对英特尔微架构的特定优化为英特尔微处理器保留。请参考适用的产品用户与参考指南,获取有关本声明中具体指令集的更多信息。 声明版本 #20110804。

 

 

 
 
 
 
 
 

在 VR 中打造极具吸引力的用户体验

$
0
0

本文将介绍设计的基础条件,以及如何利用能够创建更直观的交互以增强互动沉浸感的现代 UX 技巧在虚拟现实 (VR) 中将设计转化为支持全沉浸式互动的用户体验 (UX)。随着 VR 设备越来越普及、时尚和主流,开发人员必须继续学习新的方法来研究 UX。从概念阶段开始,到设计、用户类型和控制、导航和安全,以及最后的实施,开发人员必须考虑周全,才能满足现实中影响力最大的用户需求。

什么是 UX?

如果没有真实可信的高质量体验,易于使用和理解的交互,很难实现真正的互动,UX 是指用户和他们对产品功能的满意度之间的关系。在 VR 中,沉浸感的关键是思想与移动、功能和质量的真实性是否契合。UX 的设计必须首先考虑用例、流程和预期用户。由于与用户界面 (UI) 交互的要素之间经常造成混淆,UX 必须区别对待,甚至视作一件好产品所不可或缺的组成部分。

探索和完善用户体验的工具有很多,下面列举一些例子。为了了解受众,用户角色将重点关注某一类用户如何感知和体验设计。流程图可帮助设计人员绘制用户体验某种系统的过程,并支持他们借助线框图和原型设计进行测试,以证明假设的真伪。也可以选择其他用于创建 VR UX 的工具,包括用户旅程图、访谈和研究。以用户为中心的设计会在产品开发中实施这些工具,从而打造对个人有重要影响的体验。

Image representing a filled out document of a generic person
用户角色

Image repesenting a generic diagram
流程图

Image repesenting generic wireframe
线框图和原型设计

如何探索和创建 VR 概念

开始开发 VR 概念时,许多人(特别是 VR 新手)倾向于认为 VR 凌驾于当前体验之上,比如电影、市场营销或视频。这些旧的流程通常不会因为一种新的媒介而受到质疑,但会引发出一些有趣的问题,比如给定脚本。如果仍然使用脚本设计,那么期望应该是一个灵活的想法,也就是说倘若用户是主要推动力,那么脚本中的内容可能不是体验的主要兴趣点。至于 VR 体验从想法转变为概念,诸如建模和概念艺术等基础支持则帮助引导该愿景的实现,但往往会忽略用户所需要的重要特性。还有许多其他的功能选项,比如Unity* EditorVR,它支持在引擎实验中开发具体化的体验功能概念;支持创建包含深度、维度和交互的快速物理原型的纸板或棕色纸箱;或使用“替身”录制的视频,所有这些都可以更好地呈现如何与环境进行交互。

Download Unity

在这个示例中,Underminer Studios 的 MR Configurator Tool,大家可以看到一些通过用户提示(比如按下按钮和熟悉的现代平板电脑界面)形成的 UX 技巧。这些都是开发过程的重要部分,但缺乏对用户的透彻理解,而这是准确预测和设计直观的体验所必不可少的。

为了认识和了解娱乐媒体的基础,我们从观众的角度来看看来源于戏剧的视频游戏和电影;而 VR 则来源于表演戏剧,每一位顾客都是戏剧本身的一部分。人们认为那些用在非 VR 媒体中的视觉技巧和电影魔术不仅非常廉价,而且在视觉上大煞风景。特别是在特效方面,用户对 VR 中特效的大小和位置更加反感。为了使视觉愉悦且空间准确的体验更类似于模拟,开发人员必须提高空间和感知方面的真实性,从而创造更真实的体验。例如,在用户驾驶赛车的体验中,必须以极其真实的方式满足他们同时操纵方向盘和换档的需求,这样才能使用户相信他们是坐在汽车里执行必要的驾驶任务。

在 2016 年游戏开发人员大会上,Jesse Schell 和 Shawn Patton 谈到了他们在 VR 设计方面所积累的经验,I Expect You To Die: New Puzzles,New Hands下面的示例清楚地说明了棕色纸箱是实现 VR 的一种有效的物理原型设计工具。观察用户与环境和道具之间的交互有助于理解“为何”进行交互。

 

Developer creating a cardboard mock-up

排印不可避免,而且目前仍然没有好的解决方案。目前,我们发现了很多屏幕抖动问题,导致视线被广告牌式的屏幕挡住,遮住了眼前的世界并降低了沉浸感。这里最基本的需求是沟通。所有 UI 和 UX 都应该来自环境本身的理想场景,但很难提供相应的提示。音频提示或图形符号式的指令似乎已经取代了旧的模式,使用手部追踪的新界面减少了语言的使用,使交互更加直观。

设计基础

好的设计需要考虑到不同用户的直观需求。没人知道设计本身是不是好;他们只知道它操作简单,不需要花费大量的时间和精力来学习如何操作。我们以儿童的玩具为例。一个设计良好的玩具不需要解释,而更多地是为用户扩展了一项功能。设计者希望用某一种方式来使用产品,但如果用例不同,用户会对功能感到沮丧。只有进行大量的试玩测试,才能避免出现这种问题。用户越多,测试和验证的功能范围就越广。

cartoon of two figures struggling with a door

在 VR 中,拙劣的设计会对身体产生不好的影响,比如恶心。之所以出现像感官冲突这样的问题,是因为强制的相机移动、低质量的动画、每秒帧数 (fps)、渲染世界的速率,但如果提前考虑 UX 设计,可以减少或消除这些问题。我们以现实生活的门为例。如果门的设计合理,使用平板来推门,使用把手或拉杆来拉门,那么我们就不需要使用推拉标志,也不会出现需要经常擦拭手印的表面,但即使是这种最基本的日常功能,也常常会在设计中被忽略。你能想象每次遇到设计拙劣的门所导致的种种不快吗?

在 VR 中,其中一个关键的元素实际上是戴上头盔。从这样的位置看,颜色、刻度和空间相比于看显示器有很大的不同。关于灯光纹理和视差,建造一个两平方米,相当于房间大小的空间,可极大地影响用户体验,这是打造深度感的关键因素。在实际物理空间范围内利用空间大小所构建的体验,意味着必须慎重选择在这个范围内的移动或运动。如果构建未考虑空间的糟糕体验,你会发现自己身处在一个昏暗的角落里,用户没有焦点,不断远距传动,对周围的空间完全不确定。环境线索,比如定向音频,灯光,清晰的路径,或发光的物体(也称为感应式设计),环境自身告诉你信息,提醒你注意场景中的重要部分。其他提供清晰提示的方法是使用细节级别 (LOD) 对比远处的对象,包括使外观颜色更浅。用户需要来自环境的反馈来感觉自己沉浸于环境之中;可以包括手部投来的阴影以及响应移动的声音。这些注意事项对所有体验都很重要,但更关键的是考虑用户及其规范。

Stylized image of a generic VR headset

面向用户设计

对于所有体验而言,教程非常重要,告诉他们需要了解的可在 VR 环境中发挥作用的独特模式,尤其是早期用户规范不一致的时候。为每种体验创建标准时,最理想的做法是在不能跳过的时候进行基于任务的实践,包括针对任何输入(包括控制器、手部、视觉和其他要素)的六个动作角度—偏航、倾斜、旋转和 x、y、z 轴。三种传统类型的用户可以组合在一起,而且为了让他们愉快地参与互动,设计者应该知道如何克服各自的障碍。

  • 一种用户类型是 T-Rex;这些人朝 45 度角的位置伸出双臂,不转身也不环顾四周。关键是用方向性的音频、视觉提示以及非常明显的提示(比如带定时器的箭头),来让他们放松。
  • 第二种用户类型是 Ultra-Enthusiastic;他们不阅读任何文本,只想玩游戏。他们通常习惯于玩视频游戏,也许体验过其他 VR,但他们希望能够在玩游戏的时候顺便学会。帮助这类用户最好的方法是通过强制性的趣味性游戏(比如教程)。
  • 第三种用户类型是 Precise,他们阅读所有内容并进行相应的操作。这类用户的缺点是没有直观的交互,如果指令不准确,他们可能会迷失。在某些情况下,这类用户能使用批判性思维克服某些缺点,但在其他情况下,如果没有进一步的解释,这类用户会卡住半路上。帮助这类用户最好的方法是使用非常清晰的提示和相同的模式。

通用设计涵盖了精神和身体上的相同点和不同点。创建持久模式时,考虑所有的学习和身体能力并规划解决方案是必不可少的。如果体验能被上述所有用户类型拥有,并通过适应所有身体限制的控制坐着来使用,那么这种体验将被更多的受众所接受。随着硬件越来越低调、直观和合用,我们可以真正开始为所有类型的用户打破这些障碍。

用户输入控制

功能控制可以是一只手或任意一种控制器。每种控制的功能各不相同,而且由于它们面世还不到两年的时间,没有形成既定的规范,用户可能不了解基本知识,包括如何拿握、按钮用法、UI 访问或与别人交互。因此必须使用象形图、信息图表、文本或动手演示,直到它们成为主流。

有几种个别系统特定的、构造符合人体工程学、耐用、熟悉的控制器,还有为了提高交互的真实性、更加专业的定制控制器,比如枪、剑、自行车或其他道具。有一些套件可以通过跟踪所有的运动,支持内置的运动捕捉体验。它们都有自己的功能和缺点。但它们的成本更高,有些人无法轻易获得,这是提高采用率的另一个障碍。以下为您列举了部分示例:使用跟踪控制器时另外需要注意的一点是,必须优化体验,以更好地进行跟踪,否则会破坏沉浸感。

VR Gaming tools

手部跟踪的限制要少得多,而且是与系统进行交互最直观、最自然的一种方式。手势控制是速度更快、更加真实的一种输入控制。嵌入至手部功能(比如翻手掌)的用户界面可帮助用户轻松学习其中涉及的相关知识。互动性越高,错误越少。经过每一次的迭代后,硬件本身比较新,也经过了大幅度的改进。

photo of developer using Underminer Studios VR data visualization tool

大家可以看到上图中的 Underminer Studios VR 数据可视化工具 ManuVRing Data,使用 Leap Motion 控制。我们在 Vive 上开发了这个项目,并有一个选项支持在控制器之间选择或添加手部跟踪功能。鉴于这是黑客马拉松中的一个快速原型设计,我们希望用户在系统内进行更直观的交互。比赛的评委没有太多的 VR 经验,因此最适合检验如何运用设计思维实现所有的技能水平。为了用不熟悉的控制器来对抗未知感,用双手夹捏、缩放、旋转、选择和操纵虚拟世界以支持直观的动作,而且几乎不需要解释体验中的模式。

导航

导航是获得真实体验必不可少的一部分。相机角度、帧速率、速度和运动是关键因素,而接受可能是有意或无意的;一个好的设计者,他的规划能同时满足基于方向和时间的接受标准。充分考虑上面提到的用户类型,可以帮助设计者做好充分准备应对常见问题。T-Rex 和 Precise 用户了解环境以及不遵守模式对体验带来的负面影响。虽然 Ultra-Enthusiastic 用户在寻求直观体验过程中更加放松,但他们通常不会联想到可能会造成现实生活中的身体伤害。充分考虑这一点,对导航和安全至关重要。

基于凝视的导航是指当视线成为选择工具时,不需要其他输入来改变内容。这种导航不适合快节奏环境,因为用户一次只能使用一种输入。对于基于转身或以策略为重点的信息、教育或视觉体验来说,这一工具非常实用。

远距传动闪烁是当用户从一个位置跳转到另一个位置时,视线会被屏幕短暂的变暗或闪烁遮住;如果选择白色,恶心的感觉会更加严重。这里,闪烁、位置和精神追赶之间的定时也是一个重要因素。位置和动画之间出现轻微的定时延迟,即三到五帧的停帧时间,使用户在心理上应对这种改变,有助于减低负面影响。一种最新的技术,是在不移除视觉的情况下闪烁;带图案式视图的轻微动态模糊和视觉变暗有利于 VR 体验中的人物拥有情景感知能力。

基于转身的导航是当你开始体验时,增加用户认为他们相对于视觉感知所正在执行的前进动作和转身关系的数量。基本上,用户向前走 50 步并从视觉上转 180 度,但实际上只移动了 90 度。这种设计可以最大限度地增加游戏空间。而这种的方法缺点是必须非常了解三维数学和每种头盔的局限性,包括系统每次的跟踪更新。这是一种比较巧妙的做法,而且必须精确执行才能限制不利的影响。

听觉提示是创造完美 VR 体验的核心。用户无法看到所有的地方,而且预计人们不能始终朝正确的方向看。因此,为了吸引他们的注意力,创造更多的机会和感性体验,利用音频设计和 360 度全方位声音添加用户能够自然遵循的提示。另一种方案是添加心里声学音频,它处于人类可感知的听觉范围上下,并产生自然而然的情绪反应;这可能导致用户追求甚至避免特定的交互。

用户安全和舒适度

Stylized image of a VR user in a warning sign

遗憾的是,这点通常事后才能考虑到。优秀的设计师会考虑 VR 中用户行为的长期影响,并计划尽可能避免或减降低这些影响。例如,在 VR 中人们自然不知道如何弯腰来观察自己的脚。如果不遵循预定路径,会引起恶心;当有人冲进一堵墙或从它对面冲出来,使用视觉提示在视觉障碍(而非物理障碍)(比如 clipping)处阻止用户,对 VR 来说并不是一个好的机制。最重要的安全机制是监护边界,一个警告游戏空间的物理界限以确保安全的网格。必须在教程中增加这些基本知识,这样用户就能知道在 VR 体验中如何对这些元素做出反应。

实施

虚拟现实是一个令人振奋的最新前沿领域,需要我们不断地进行探索。从心理学、建筑、声音,到物理、照明等等,这些都会对 3D 交互式体验的设计产生重要影响。越全面地看待设计影响,越好。为了成功创造有影响力的 VR 体验,设计者必须清楚地了解硬件的差异性和局限性,因为它们直接影响优化需求和塑造整体体验。由于我们处在虚拟现实生命周期的最初阶段,因此仍然需要进行大量的实验,才能形成最全面的最佳实践指南。当前,最基础的原则是坚持以用户为中心、漂亮且真实的体验,并进行优化以实现体验最大化。

VR Optimization Tips

关于作者

Alexandria Porter 是 Underminer Studios的首席执行官。她从最基础的角度阐述了如何运用中小企业经验、设计能力和管理技能在技术前沿让企业快速发展。作为一家以解方案为中心的公司,我们帮助客户利用技术突破极限,并改变他们解决实际问题的角度。凭借超过十年的丰富经验、强大稳定的行业关系以及开发独特产品的发散性思维,Underminer Studios 始终热衷于有效地运用技术塑造未来。

大型任务处理:为虚拟现实游戏施展混合现实魔法

$
0
0

面向 VR 的绿屏混合现实视频制作分步指南

虚拟现实 (VR) 可为佩戴头盔的玩家提供丰富多彩的游戏体验,但我们很难分享这种体验。第一人称玩家视角的视域非常有限,而且跳动比较突兀,导致不佩戴头盔的观众无论是现场观看游戏屏幕,还是观看预先录制的视频,都无法获得令人满意的观看体验。

绿屏混合现实视频是一种创新技巧,能够有效地带领外部观众走进 VR 世界。具体方法是展示涵盖玩家和游戏环境的第三人称游戏视角,创建极具沉浸感的 2D 视频解决方案。如果想展示 VR 体验,以在竞争越来越激烈的市场脱颖而出,VR 开发人员一定要使用要求严苛但效果惊艳的大型任务处理

Figure 1
图 1:Kert Gartner 为 Job Simulator* 制作的混合现实预告片截图。

开发人员或 YouTube 视频制作人员都可以使用这项技巧,它既能用于现场活动和在线流媒体播放,也能用于预告片和视频。去年,英特尔开发人员关系部门的 Josh Bancroft 和 Jerry Makare 在公司内部协作使用这项技巧进行了活动演示,不仅与现场其他人员建立了关系,而且锻炼了技能。本篇分步操作指南将和大家(VR 开发社区)分享有关知识,并帮助大家以最恰当的方式向观众展示令人惊叹的创意。

Figure 2
图 2:2016 年东京电玩展上使用绿屏混合现实技术沉浸在 Circle of Saviors* 中的角色扮演玩家。

本篇指南将重点介绍 Josh 和 Jerry 有着最丰富经验的工作流 — 如何处理在 Unity* for HTC Vive* 中构建的 VR 游戏,包括使用用于启用混合现实的 SteamVR* 插件;用于校准的 MixCast VR Studio*,以及用于色度键、合成和编码以便流传输和/或录制的 Open Broadcaster Software* (OBS)。重新创建该技巧的方法,以及用于重建流程各个阶段的其他硬件和工具有很多,本指南引用了其中几种。

本指南内容如下:

硬件:大致推荐处理大型任务所需的物理套件,包括 PC、VR 头盔、摄像机、镜头、视频捕捉、工作室空间、绿色屏幕和灯光。

软件:推荐所需的软件,包括支持 VR 应用本身、游戏和物理摄像机校准、捕捉与合成软件,以及面向流媒体传输和/或录制的编码软件。

分步:执行起来比较容易的阶段包括校准、合成、编码和录制阶段。

资源:本指南中还提供了有关其他信息和资源的链接,以便大家了解制作 VR 游戏混合现实视频的所有知识。

硬件步骤

重中之重:PC 硬件

执行混合现实视频大型任务时,最常见、最便捷的设置要求两台功能强大的 PC — 但使用一台基于英特尔®酷睿 X 系列处理器产品家族,以及 12 核以上的英特尔®酷睿 i9+ 处理器的 PC,效果相同或更好。更多的内核可为 PC 提供充足的处理能力来处理 VR 游戏、混合现实渲染和捕捉,以及面向高质量录制和/或流媒体播放的编码。

VR 应用必须在其他进程占用大量空间的情况下(后续详细解释)顺畅运行;也就是说,在游戏内生成第三人称视图,并执行现场视频捕捉与合成。为了避免延迟,建议在同一台设备上运行 VR 应用和执行捕捉与合成。

在双 PC 设置下,第二台设备接收第一台设备传来的合成视频信号、依次捕捉,并执行编码任务以支持流媒体播放和/或录制。从处理的角度来说,这项任务相对比较繁重,因此编码能力越高,结果质量越高。因此我们选择使用英特尔酷睿 X 系列处理器的其他内核。

实施进程的方式取决于可用的硬件,而且可以灵活管理负载平衡。但如果需要将工作分布于多个系统,我们发现最高效的方法是尽可能均匀地分布。

后台组件:VR 硬件

当然,关键的组件就是 VR 头盔以及随附的传感器。在本项目中,我们使用 HTC Vive 硬件,因为支持制作这种 VR 游戏混合现实所需的大量工作都内置于 SteamVR Unity 插件,而且许多内置于 Unity 的 Vive 游戏仅适用于这一进程。关于支持 HTC Vive 混合现实的实用资源,请访问 Steam 开发人员论坛

Oculus Rift* 新增了支持第三人称、混合现实捕捉,但本文撰写之际,开发人员需要编程以便该游戏支持混合现实。相关文档可从 Oculus 网站下载。

传感器非常关键 — 但同等重要,因为还需要在物理摄像机上安装一个用于混合现实流程的传感器。HTC Vive Tracker* 非常适合,也可以使用第三个 Vive 手持式控制器。

Figure 3
图 3:SteamVR* 必须看到第三个控制器(左下方)以便混合现实功能运行。

为此本指南中使用的是 HTC Vive 硬件。

实景真人:视频捕捉

混合现实流程涉及用摄像机拍摄玩家,生成的视频信号馈送至 PC 进行合成。至少按照符合最终视频要求的分辨率和帧速率捕捉视频。高质量视频最好为 1080p,每秒 60 帧。

捕捉现场视频时,高质量摄像机(比如数码单反相机 (DSLR) 或无反相机)的效果最好。可以使用譬如 Magewell、Elgato、Blackmagic Design 等公司的 USB 或外设组件互联 (PCI) 高清多媒体接口 (HDMI) 捕捉设备,插入 USB 3.0 端口或 PCI 插槽,还包含可插入摄像机的 HDMI 端口。这样外部摄像机就相当于合成软件的网络摄像机。

还有适用于 PCI 插槽(我们之前用过,包括 Blackmagic 等)的内部捕捉卡。不过,它们需要较多的驱动程序,还会占用 PC 空间和匹配时间。而且它们不适用于笔记本电脑。

支持最终视频要求的最低分辨率和帧速率并在所选的合成软件中显示的所有视频捕捉设备(包括常规网络摄像机)都适用 — 但高质量摄像机的效果更好。

其他要求使用的硬件为 4K 显示器。下文将进行介绍,台式机上渲染的窗口为四分窗口,每次只捕捉和录制四分之一。之后,这些四分之一将变成经合成以创建最终混合现实视频的层。为了制作分辨率为 1080p 的最终视频,台式机的全分辨率至少是四倍,即最低为 4K。可以使用分辨率较低的显示器,但记住,混合现实视频的最终输出分辨率为四分屏幕窗口渲染大小的四分之一。

在工作室中:

设置空间

工作室的空间必须大于仅用于玩 VR 的空间。Vive 所需的最小空间大约为两平方米,还需另外的空间方便物理摄像机自由移动。

必须在该空间中搭尽可能大的绿屏。从理论上来说,在玩家身后搭一块绿屏就行,但这会严重限制摄像机的拍摄角度。摄像机的拍摄角度不能超出绿屏,因为会显示屏幕之外的空间,还会破坏混合现实的幻想。

最理想的情况是,绿屏围住其中的三面墙,不过这取决于空间的大小以及所使用的屏幕支架。

我们使用了多种配置,有一种适用于多种演示的便携式解决方案是 Lastolite* 全景式背景,它可以打造 4 米宽、2.3 米高,包裹住三面墙的绿屏空间。

除了表面覆盖,另外需要注意的一点是,屏幕材质必须平整,避免导致出现阴影的褶皱,而且照明必须尽可能均匀(下文详细介绍),因为这些因素会影响色度键滤波器是否能够消除玩家背后的绿色。

良好的拍摄效果:摄像机规格

摄像机的使用比较灵活,取决于你能访问的设备,但必须尽可能使用质量较高的摄像机。摄像机必须有 HDMI 输出,并能按照你的分辨率和帧速率需求(最低 1080p,每秒 10 帧)进行拍摄。所有支持视频功能的优质摄像机都可以 — 比如 DSLR 或无反相机、便携式摄像机,或者最理想的专业摄像机。

我们使用的摄像机包括全帧型无反相机 Sony a7S*、专业 Sony FS700* 摄像机和 Panasonic GH5*,不过其他品牌和型号的摄像机性能也很好。但是,如果追求高品质拍摄效果,便宜的网络摄像头可能无法实现。

更好的拍摄效果:摄像机和跟踪器定位

在这一过程中,需要第三个 VR 控制器或跟踪器牢固地装在摄像机上,安装后它不会移动到摄像机对面,因为这样会断开校准。由于其形状和方便拿握的设计,安全连接控制器可能会出现一些问题。我们用了很多巧妙的方法将控制器固定在摄像机上,包括强力胶带、夹具以及能够从控制器中心孔穿过的定制装配工具,还使用了大垫圈、橡胶垫片和长螺栓来确保其稳定性。更新、更紧凑的Vive Tracker可以让这一流程更简单,随附的便宜靴架可以轻松、牢固地装在摄像机机身上。使用冷靴架还可将跟踪器和摄像机镜头的中线对齐,这样有利于校准。

Figure 4
图 4:Vive Tracker* 的下方有一个标准 ¼” 20 螺旋接头,方便安装。

开始校准阶段(后续介绍)之前,必须使安装在三脚架上的摄像机和连接的跟踪器保持水平。这里有一个实用的技巧,就是使用智能手机上的内置罗盘/气泡水平仪(如有)。将智能手机放在跟踪器或摄像机顶部,它可以告知摄像机各个方向的偏离度数,可以经过简单调节使相机保持水平。在校准阶段一开始保持摄像机和跟踪器的水平可减少后续对三个位置(X、Y 和 Z)和三个转动(rX、rY 和 rZ)轴的调节,同时可以使校准更简单、更准确。

校准过程完成,以及物理和虚拟摄像机进入锁步模式之后,物理摄像机可在绿色空间范围内自由移动,包括从三脚架上取下来。只要跟踪器和摄像机不彼此相向移动,就可以随意移动摄像机,而且将在 3D 空间中跟踪该摄像机,就像 VR 中的双手。想要获得专业的拍摄效果,建议手持摄像机时使用稳定器。如果没有稳定器,将摄像机放在三脚架上并依靠针对摄像机移动的水平和垂直规划,这样拍摄效果更好。记住,摄像头的视域必须始终在绿色屏幕范围内。

最好的拍摄效果:镜头和焦距

焦距和镜头是其他必须考虑的重要因素。16 毫米或 24 毫米广角镜头的焦距较短、视域较宽,意味着摄像机必须更靠近主体,因此会增加发生碰撞的风险,而且视域会意外滑出绿屏区域。

但是,焦距较长(比如 70 毫米变焦镜头)意味着视域较窄,因此需要距离玩家较远,以保证他们始终在画面中。如果空间有限,这样可能不太适用。使用最佳镜头和焦距取决于可用的空间、绿屏的范围、拍摄的玩家数量,以及拍摄方式(静态摄像机还是手持)。对于大多数情况来说 70 毫米镜头可能过长,而且可能你想坚持使用 24 毫米或 28 毫米镜头。

尤其当你使用焦距可以调节的变焦镜头,最重要的一点是,必须在校准完成后保持固定的焦距。改变焦距(放大或缩小)可能会断开校准,导致虚拟摄像机和物理摄像机不再对齐,你不得不重新开始。记住这点,使用焦距固定的镜头是最稳妥的做法。

聚光灯下:灯光

在灯光方面,目标是均匀地照射主体和绿屏,避免出现刺眼的阴影或色度键上显示的灯光出现明显不同。如果可能,设置三个灯光是最理想的:一个照射主体,另外两个照射主体背后的绿屏 — 一边一个,以最大限度地减少阴影。可以根据具体条件减少灯光的使用。发光二极管 (LED) 面板灯比较好 — 便携,不像钨丝灯或卤素灯那样产生过多热量,这样玩家会感觉更加舒适。

Figure 5
图 5:2017 年 5 月台北国际电脑展英特尔的设置(包括在框架上搭的绿屏(覆盖三面墙)、地板和两个荧光面板灯)。

务必检查空间中现有灯光的频率不会造成摄像机图像过度频闪,尤其是当你依靠环境光(而非自带灯光)的时候。有些 LED 灯光和其他频率一定的灯光会发生这种情况。唯一的办法是在空间中测试一下,才知道是否存在这种问题。

软件步骤

软选择:VR 应用

从软件的角度来说,第一个关键是游戏支持混合现实。也就是说,允许在游戏中实施和定位另一个第三人称摄像头,而且能够输出一个象限图(由在游戏中以第三人称视角提取的独立背景层和前景层所组成)。该象限图可使用物理摄像机进行合成和校准,支持整个混合现实流程。

内置于 Unreal Engine* 的游戏支持混合现实,而且也可以编码自己的工具,像 Croteam 对 Serious Engine* 的操作那样,该游戏支持整个混合现实流程,包括捕捉与合成。大家可以观看 此处 Croteam 的教程视频,了解他们如何在其 VR 游戏中创建混合现实视频。

不过我们指南的重点是介绍流程,因为它与使用 SteamVR 插件(可自动支持游戏创建混合现实视频)的(内置于 Unity for HTC Vive 的)游戏相关。

软操作:校准

要想混合现实正常运行,必须能够校准物理摄像机和虚拟摄像机之间的位置偏移。可以手动调整偏移值,并通过测试和纠错进行校准,但这一过程非常痛苦。我们主要使用 MixCast VR Studio对齐和校准游戏和物理摄像机,我们稍后将详细介绍这一关键过程。

软结果:合成与编码

我们使用 OBS Studio*进行合成与编码,以支持录制和/或流传输。OBS Studio 是一种拥有良好支持的开源软件,被广大视频创建人员和流传输人员所采用。也有其他的解决方案比如XSplit*,但 OBS Studio 能够实现本指南的目的。

分步操作

进行校准之前,我们首先总结一下之前的步骤,以及接下来的步骤。一台 PC 运行 VR 应用、视频捕捉与合成、另一台 PC 处理面向录制和/或流传输的编码(如果采用英特尔酷睿 X 系列处理器家族,仅一台 PC 就可完成所有任务)。在工作室内搭建了一个较大的绿屏,以及一台固定有跟踪器或控制器,且完全处理水平状态的摄像机。

我们所使用的 VR 应用支持混合现实(比如使用 SteamVR 插件内置于 Unity),实施另一台游戏内第三人称摄像机,并输出象限图(包括前景层和背景层)。然后运行捕捉与合成软件。

接下来是校准游戏内和物理第三人称摄像机,以便它们在合成阶段处于完全水平状态。这样有利于我们拍摄玩家,让游戏内摄像机精准地跟踪物理摄像机和玩家的手部运动(并延伸一下,跟踪玩家在游戏中手持的物品或武器)。这样我们可以准确组合游戏和真实世界的视频层,并创建令人信服的混合现实。

摄像机校准

开始校准之前,最好返回去仔细检查摄像机和固定的跟踪器是否处于完全水平状态。在校准过程中,玩家(或替身)站在摄像机前,手持控制器。这个人应该保持水平,也就是说应该笔直地站在摄像机前,抬头挺胸,以便对齐并处于摄像机视图中线的中央位置。

这一点很重要,因为你进行调整时,可以更改六个点:X、Y 和 Z 的位置以及 X、Y 和 Z 的旋转。调整对精准度要求很高,但如果你知道摄像机保持水平,以及玩家笔直地站在摄像机中线上,你可以将部分偏移值最小化使它们接近 0,之后就不再需要调整。这样有助于顺利地进行拍摄。

在对齐游戏内摄像机和物理摄像机时,我们使用 MixCast VR Studio 进行校准。启动软件,确保软件能够看到物理摄像机,并使用下拉菜单检查它是否知道哪台设备是跟踪物理摄像机的第三个控制器(固定在摄像机上的 Vive Tracker 或控制器)。开始之前,还需要一个人佩戴 VR 头盔站在游戏空间中,一手拿一个控制器。

快速设置

接下来启动 Quick Setup流程,它将带你完成整个校准过程。为你提供 VR 应用所需的 XYZ 位置、XYZ 旋转和虚拟摄像机的视域值,以便与物理摄像机对齐。

Figure 6
图 6:在 MixCast VR Studio* 中选择 Quick Setup,以开始校准过程。

第一步是拿起一个手持式控制器,将圆环放在物理摄像机的镜头上,使两者尽可能地靠近对齐。点击控制器上的侧键进行注册。

Figure 7
图 7:校准第一步是对齐物理控制器和物理摄像机。

接下来,该工具将十字准线投影在屏幕的角落中。将手持控制器移到圆环的位置以便它和十字准线的中心位置对齐,然后点击按钮。

Figure 8
图 8:设置流程最初在屏幕相对的角落提供两个十字准线用于对齐。

一开始要在屏幕中左上角和右下角两个位置的十字准线对齐。完成之后,可以点击 Plus 按钮增加十字准线,以提高精准度。

Figure 9
图 9:点击 Plus 按钮会出现更多十字准线以提高精准度。

我们发现最佳的数量是 4 个十字准线。两个不够,超过四个也趋向于 off。四个十字准线覆盖屏幕的四个角。

Figure 10
图 10:完成四角对齐操作。

这时已经完成了粗略的校准。大家会看到虚拟手持式控制器正在跟踪物理控制器的大体位置。之后使用屏幕下方的其他细化控制调整摄像机位置和旋转,让它们尽可能地靠近。

Figure 11
图 11:控制 XYZ 位置和 XYZ 旋转,微调摄像机的位置。

微调

微调手部对齐时,VR 中的人将手举在身体两侧,以便大家看到真实控制器顶部所绘制的虚拟控制器。如果虚拟控制器和物理控制器隔得较远,需要稍微回拉一下虚拟摄像机,以便将绘制的控制器拉近一点。为此,VR 中的人需要点击箭头调整摄像头位置。

Figure 12
图 12:显示 VR 绘制的手持式控制器与真实控制器未对齐。

Figure 13
图 13:调整之后绘制的手持式控制器与真实控制器结合得更加紧密。

每点击一次都会在想要的方法出现明显的微小移动。最好记录下点击数量,以免移动过量需要返回,否则需要撤销操作。接下来在空间中移动控制器,看上下是否对齐。

Figure 14
图 14:摄像机对齐后,点击选择目录确认设置。

可以拿着手持式控制器静止不动使其完全对齐,然后移动到别处看看对齐是否断开。可能出现一个位置对齐另一位置不对齐的情况,这意味着需要进行更多地微调。这是一个迭代过程 — Josh 将它描述成一个六维魔方,需要在拼好一面的同时不将其他的面打乱 — 但通过详细的试验和纠错,最终可以将它们完全对齐。

虚拟控制器和物理控制器对齐后,还需要检查游戏中使用的其他物体是否也对齐,包括武器、太阳镜和皇冠。操作这些物体确保所有物体都对齐。

Figure 15
图 15:MixCast VR Studio* 中用于对齐的所有物体。

Figure 16
图 16:手持鼓槌检查控制器是否对齐。

皇冠尤其适用于检查玩家佩戴 VR 的头部是否对齐。玩家将皇冠戴在头上,然后调节摄像机位置和旋转,直至皇冠位于最中间并保持水平。

Figure 17
图 17:使用皇冠检查摄像机对面的玩家头部的精确对齐和位置。

摄像机配置

校准完设置的对齐值并准备开始的时候,也可以用相同的方法将这些值用于任何 VR 应用。需要通过 MixCast VR Studio 将 XYZ 位置、XYZ 旋转值,以及视域值保存为 externalcamera.cfg 文件。

Figure 18
图 18:externalcamera.cfg 文件显示校准之后需要保存的 XYZ 位置、XYZ 旋转和视域值。

对于这种 Unity/SteamVR 混合现实方法,必须首先满足两个条件,才能触发合成混合现实视图所需的四分屏幕。第一个条件是第三个控制器或跟踪器必须插入并进行跟踪。第二个条件是所使用的 VR 应用的可执行文件的根目录中必须显示 externalcamera.cfg 文件。

Figure 19
图 19:确保 externalcamera.cfg 文件和 VR 应用可执行文件保存在同一个根文件夹中。

启动游戏

现在可以启动所使用的 VR 应用创建混合现实视频。如果是 Unity游戏,按住 Shift 键同时启动可执行文件,将弹出一个窗口供你选择启动时的分辨率,以及是在窗口启动还是全屏启动。

这时,需要指定应用必须以 4K(以便四分窗口的每一部分都是高清 1080p)的分辨率运行,而且必须全屏运行,而不是在窗口中运行(取消选中窗口方框,并从列表中选择 4K 分辨率)。然后当你启动应用时,它将打开以 4K 分辨率运行的四分桌面窗口视图。

Figure 20
图 20:Rick and Morty: Virtual Rick-ality* 四分桌面视图示例。

这一四分视图包含以下象限:游戏背景视图(左下);游戏前景视图(左上);游戏前景 alpha 遮罩(右上);和第一人称头盔视图(右下)。

合成

在本指南中,我们使用开源 OBS Studio软件进行合成。开始之前,确保屏幕上有一个四分视图。第一步是捕捉背景层,即左上方的象限。在 OBS 中添加一个“Window Capture”类型的新 Source。选择 VR 应用窗口。将该源重命名为 “Background”。接下来将 Crop Filter 添加至该源(右击 Background 源,Add Filter,Crop/Pad),将屏幕上不想捕捉的部分裁切掉。裁切值的单位是像素,表示从四个边裁切的窗口大小。因此,为了捕捉左下方的背景层象限,使用的值分别为顶部 1080 和右侧 1920(记住,在 4K 3840 x 2160 分辨率下,这相当于每个维度的一半)。

Figure 21
图 21:输入裁切值之前,裁切过滤器显示四分视图。

Figure 22
图 22:输入裁切值之后,裁切过滤器只显示左上方的象限(背景视图)。

应用裁剪过滤器后,它将出现在预览窗口中,但不会占据整个屏幕。右击该源,选择 Transform,并选择 Fit to screen(或使用 Ctrl+F 快捷键)。OBS 中合成图像的每一层都必须是全屏,因此每一层都这样操作。

色度键

有了背景之后,需要对物理摄像机视图进行同样的操作,并剔除绿色背景,留下可以叠加在捕捉的游戏背景上的玩家。

Figure 23
图 23:在 OBS* 中确定物理摄像机的源。

添加新的 Video Capture 源。选择摄像机捕捉设备,并将这一层命名为“Camera”。应该能看到摄像机视图。接下来右击该源,前往过滤器,并选择"Chroma Key"。

Figure 24
图 24:在 OBS* 中选择色度键过滤器。

Figure 25
图 25:选中 Chroma Key 后,绿色背景将消失,可使用滑动条微调。

可以在色度键设置下调整滑动条,直到人物在背景中清晰起来,绿色背景完全消失,但不会擦掉人物的任何部位。通常使用默认值就好,而且只需要稍作调整。这就是灯光均匀的好处。如果改动过大,色度键过滤器可能会搞砸,通常可以删除过滤器,重新添加重新打开。

Figure 26
图 26:临时测试合成,减去前景层,显示站在游戏背景顶部的人物。

如果看起来还不错,定位到预览窗口中的“Fit to Screen”,并确保在 Source 列表中 Camera layer 列在 Background layer 之前(表示摄像机层在顶部渲染)。这时大家应该可以看到 VR 背景下的摄像机视图。

前景

接下来按照和背景层相同的流程处理前景(左上象限)。添加一个新的窗口捕捉源,捕捉象限视图并应用裁切过滤器 — 这次按照右侧 1920 和底部 1080 的像素裁切,以隔离左上角象限。用“Fit to Screen”的 Ctrl+F 快捷键确定大小,确保其充满整个预览窗口。将该源命名为“Foreground”。

Figure 27
图 27:裁切前景视图。

前景层显示玩家和摄像机之间的物体(因此应在现场摄像机视图的顶部渲染)。需要屏蔽掉前景视图的黑色部分,以使背景层和摄像机层透过来。右击 Foreground 源,并应用“Color Key”过滤器。

Figure 28
图 28:选择 Color Key 过滤器删除前景视图中的黑色区域。

OBS 将询问该键使用哪种颜色,这里有一个自定义颜色选项。进入颜色选择器,选择黑色。当然,你还可以设定值,选择十六进制值 #000000 形成纯黑色。这样将使黑色部分变得透明,从而前景对象在其它层的前面。

Figure 29
图 29:选择色度键十六进制值 #000000 形成纯黑色。

Figure 30
图 30:应用色度键之后的前景层,黑色区域已消失。

右上方的象限为 alpha 遮罩,可用于前景层,但使用起来比较复杂。如果前景层包含你不想让其变成透明的纯黑色元素,可使用 alpha 遮罩来操作。alpha 遮罩的设置不在本指南介绍范围内,大家可以访问OBS 项目论坛了解相关信息。

合成图像

完成背景、摄像机和前景源过滤之后,按照正确的顺序(首先过滤前景,然后是摄像机,然后是背景),大家应该能够看到实时混合现实视图。

 

Figure 31
图 31:由游戏、背景、玩家和前景组成的最终合成图像。

有了混合现实源之后,大家可以在 OBS 中进行其他创作。例如,可以设置在混合现实视图和第一人称视图之间切换。这样有利于显示两种视图的差异,也可以用于流传输或录制阶段,更改观众所看到的内容。

还可以设置选择另一个源,以显示另一台摄像机,这种方式可以用于例如托管现场流传输,并想要转向指向主机的摄像机的情况。还可以根据需求在 OBS 工作流中添加不同的图形或流媒体插件,这也是为流媒体工作人员和 YouTube 视频制作人员提供的重要功能。

解决延迟问题

OBS 中可能会发生延迟问题,原因是不同视频源之间的每秒帧数不匹配。如果出现这个问题,首先确保桌面捕捉(视频下方的 OBS 设置中)设为每秒 60 帧。接下来检查摄像机捕捉上的帧速率是否设为每秒 60 帧。最后检查摄像机是否也设为了每秒 60 帧。

我们在演示中遇到过延迟问题,发现其中一台摄像机设的是每秒 24 帧。设为每秒 60 帧之后立刻解决了这一问题。可能你的设置无法处理超过每秒 30 帧的情况,没有关系;如果是这种情况,请注意所有的设置都必须是每秒 30 帧。

视频输出

最后一个阶段是提取混合现实视频信号并进行编码,以支持录制或流传输。如果是双 PC 设置,一台 PC 进行捕捉和合成,另一台 PC 处理录制或流传输编码。如果使用一台搭载多核英特尔酷睿 X 系统处理器家族的 PC,它应该能处理所有任务,包括高质量编码,无需输出至第二台 PC。

如果是双 PC 设置,如果要将信号从第一台 PC 发送至第二台 PC,OBS 有一项叫做 Full-screen Projector 的功能,支持全屏预览。右击预览窗口,可以选择输出至哪台显示器。然后将 DisplayPort* 或 HDMI 线缆插入图形处理单元 (GPU),以便运行 VR 与合成的计算机认为它是第二台显示器(还可以通过分离器运行,实际上还是在第二台显示器上)。

使用第二台具有相同 1080p/每秒 60 帧功能的 USB HDMI 捕捉设备(或 PCI 卡)将信号发送至第二台计算机。同样在第二台计算机上运行 OBS,并设置一个包含一台视频捕捉设备(USB HDMI 捕捉框)或 PCI 卡的简单场景。然后在第二个系统上设置所有录制和流传输质量设置。

大家同样可以在第二台计算机上为视频输出添加其他细节,比如用户告警或图形。切换场景时,在两台设备之间进行切换比较复杂。例如,你需要在第一台计算机上完成第一人称视图和第三人称视图之间的切换,同时管理在第二台计算机上运行的大量其他操作。

编码

OBS 输出设置默认情况下设为简单,即使用相同的编码器设置进行录制流传输。必须设为高级模式,才能分别调整流传输和录制的设置。

通常可以选择使用 GPU 或 CPU 进行编码。如果使用一台 PC 完成所有任务,我们建议使用 x264 CPU,因为 GPU 编码器会对 VR 体验的帧速率造成负面影响,不管是 VR 中的人物还是桌面前的操作人员。

使用 x264 CPU 编码器还可以充分利用 CPU 硬件的扩展功能,如果使用 12 核或 18 核英特尔酷睿 X 系列处理器家族,可以把质量设置调的特别高,因为它使用 CPU(而非 GPU)进行编码,这样能够提高最终视频的质量。

流传输

进行流传输时,码率取决于网络提供商的上游带宽以及流传输提供商支持的码率。例如,Twitch* 的码率限制在 6 MB 左右,因此在 OBS 中选择每秒 6,000 KB 的码率。最好使用流传输提供商和网络提供商能够处理的最大流传输码率。如果网络速度较慢,或者只想降低质量,可以将流传输码率降至每秒 4,000 或 2,500 KB。

Figure 32
图 32:OBS* 中的流传输设置。

我们还需要考虑关键帧间隔,将默认零 (0) 自动设置改为 2 秒。其他设置包括设为固定码率,以及为 CPU 用途预设选择中等。这样将支持你流传输高质量视频。

录制

录制编码器设置除了码率(调的更高)之外大体相同。如果在 Twitch 上流传输,需要每秒 6MB 的码率;如果是录制,最大可为每秒 20 -100 MB(取决于系统)。功能强大的系统(比如英特尔酷睿 X 系列处理器家族)的确能够显著提高视频质量 — 但如果码率设的太高,将很难管理和控制文件大小。

需要试验不同的码率值,以在质量和文件大小之间找到最佳的平衡点,同时不造成编码器过载。从较低的码率(大约 15 MB)开始,然后使用整个混合现实堆栈运行测试录制。CPU 的使用情况随具体情况的不同而有所差异,因此需要观察 OBS 上状态条和 Stats 窗口,看是否出现编码器过载帧数下降等警告。如果没有出现警告,系统可能能够处理更多。如果想要系统处理更多,可以暂停录制,将码率提高一点。

Figure 33

图 33:OBS* 中的录制设置。

精彩回顾

要想顺利完成 VR 绿屏混合现实视频的制作过程,必须进行大量的测试和纠错,一旦搞定了,结果将会非常令人满意。我们不可能在一篇指南中列出所有的可能性;但我们的目标是帮助大家掌握其中所涉及的相关知识,并能够尝试制作自己的混合现实视频。期待大家成功制作出混合现实视频。

更多信息

英特尔开发人员专区

使用 Feedback Solutions 和英特尔推出的智能实时占用解决方案降低能耗成本

$
0
0

控制供暖和制冷系统是一个行之有效的节能方法,但是如果您正管理一家会议中心、教育场所、酒店、医院和其他占用率不断变化的建筑,节约成本不会像下午 6 点关闭空调那么简单。对于经常被使用的建筑,针对实际占用率优化通风和空间利用是降低成本的最佳方式,但是基于 CO2 的传感器无法快速、准确地检测到占用率的变化,而基于视频的计数器会引发隐私和安全问题。

Feedback Solutions 提供了一种基于云的独一无二的解决方案,可与建筑管理系统无缝集成,并根据实时占用数据调整通风量。Feedback Solutions 使用热成像占用计数器和在英特尔® 物联网网关上运行的智能算法,帮助管理人员降低占用率不断变化的建筑的成本、提高其投资回报率,同时保护用户的身份安全。

“多伦多大学* 已将 Feedback Solutions ‘占用计数器’和‘占用控制器’纳入控制策略,以减少建筑的能耗……现在,在许多情况下,我们将风扇速度降至 40%,节省了大量的能源,并且不会对电机安全造成影响。我们轻松地超越了最初的节能预期。”

John Walker - 多伦多大学* 运营维护经理

实时信息与快速响应:占用率作为一个关键指标

Feedback Solutions 提供一款针对性解决方案,以实时占用作为建筑内风扇速度控制的关键指标。这款基于云的高度准确的解决方案能够基于占用实时、自动地提供通风,以维护室内空气质量和楼宇使用舒适度,并提升投资回报率。

  • 热成像占用控制器
    热成像占用控制器被置于入口上方,可提供准确、实时的建筑、空间和房间进出人口计数,无需记录标识性特征。
  • 占用计算
    Feedback Solutions 智能算法可实时计算占用率,并在用户定义的时间间隔内通过 BACnet* IP 传输数据,最终集成至建筑管理系统。
  • 自动调整
    使用占用数据控制风扇速度,满足通风要求。配置完成后,系统会自动调整风扇速度,无需人为干预。风扇速度每下降 20%,功耗约降低 50%。1
  • 英特尔® 物联网网关
    英特尔® 物联网网关支持传感器与建筑管理系统之间无缝、可靠、安全的数据流,实现了近乎实时的风扇速度调节。它还支持将数据传输至云,以进行存储与报告。
  • 存储与报告
    所有占用数据均被传输至安全的云平台,以进行存储与报告。收集的数据也可以用于了解与优化空间利用。

为什么选择 FEEDBACK SOLUTIONS?

  • 准确-降低成本,同时提高利用率。Feedback Solutions 热成像占用计数器可持续提供高于 95% 的准确性,您可以根据实际占用水平优化通风、清洁与维护和空间规划,延长 HVAC 组件的使用生命,并降低成本。
  • 非侵入式-在不侵犯任何人隐私的前提下捕捉占用数据。不同于视频传感器,热成像计数器捕捉用于空间利用规划和通风控制的占用数据,不会捕捉图像。
  • 实时-快速响应不断变化的占用率。使用 CO2 传感器会长时间延迟数据,在风扇速度调整之前真实的占用率可能已发生变化。Feedback Solutions 计数器数据和控制变量均以实时的方式运行,因此,您可以满足当前占用率的需求,而不是 30 分钟之前该房间的需求。
  • 低维护- Feedback Solutions 计数器拥有超过 25 年的平均故障间隔时间(MTBF),您几乎不需要重新配置或更换传感器。配置完成后,通风系统会根据占用率自动调整,无需人为干预。
  • 可扩展-您可以将传感器安装于某个建筑,甚至整个校园。英特尔® 物联网平台将占用数据集中于云,支持面向小型和大型装置的快速、可靠、安全的数据流。

探究:基于占用的能耗意味着真正的节约

能耗

多伦多大学* 使用 Feedback Solutions 产品节约了 40% 的能源 在多伦多大学*的多数大型建筑中,通风系统基于设计容量(或最大占用水平)进行供热或制冷,即便实际占用率远低于此。这导致过度使用风扇,降低了环境的舒适度,以及能源浪费。

Feedback Solutions 在两栋建筑的教室、讲堂、办公室和图书馆入口处安装了热成像占用计数器。此后,该大学使用实时占用值调节风扇速度,从而控制通风量。据第三方能源审计机构的测定,此举节省了 40% 的能源

投资回报率:多伦多大学* 将两栋建筑的能耗降低了 40%,每年将节省约 150 万美元,两年内便可获得投资回报。

空间利用

Feedback Solutions 通过优化空间利用,降低了阿尔伯塔大学* 的成本

阿尔伯塔大学* 一直在寻找一种优化空间利用的方法。 Feedback Solutions 在整个建筑中安装了占用计数器,以捕捉占用数据。该大学通过比较计数器提供的占用数据与课程、维护和清洁时间表,掌握了在一定时期内加强少数建筑的教室使用、关闭其他建筑的方法。他们还可以每小时、每天和每周创建关于每间教室和每节课堂的使用效率指标。

投资回报:阿尔伯塔大学* 通过改进空间利用,降低了维护和清洁成本。

“借助 Feedback Solutions 系统,阿尔伯塔大学能更好地为扩张提供理论支持,并使用数据形成未来的扩张策略。此外,阿尔伯塔大学开始使用数据确定使用率(每学期均会波动)的方差,并致力于更全面、更准确地了解实际的空间使用。”

Rob Pawliuk - 阿尔伯塔大学*运营部副主任

实时占用数据如何帮您节约成本?

了解更多信息,并申请免费的评估与路线图,以进行激活。

sales@feedbacksolutions.ca | 289 290 4365

加快向更智能建筑的转型
物联网正推动全新智能建筑的进步,以更好地解决业主和管理人员的当务之急。英特尔正借助物联网组件改进数据分析,简化与云的通信,增强安全性,提供深入的洞察,从而加速这一转型,提升效率与投资回报率

 

Intel and ISVs  关于智能建筑的更多信息

1.根据风扇相似定律-“风扇功率随着速度的立方而变化。”

如何安装面向 UE4 的 Vulkan* API

$
0
0
 

全新路径,全新可能

正如并行性和多线程编程为多核 CPU 的性能飞跃铺平了道路,Vulkan* API 也将有望为多线程、跨平台 GPU 编程和高性能渲染创造一个美好的未来(无论使用哪种目标设备)。

Vulkan* 逐渐取代 OpenGL*,支持开发人员更好地控制线程化和内存管理,并且更直接地访问 GPU(相比前代 API),这意味着将有更多的功能用于处理一系列目标平台。唯一的成本是使用相对最新的处理器,并投入较多的开发工作。

您需要的组件

在英特尔显卡 GPU 上开发 Vulkan* API,最低的要求是使用运行 64 位 Windows * 7、8.1 或10 的第六代英特尔®处理器家族的处理器(2015 年 8 月推出)。英特尔还为第六代、第七代或第八代处理器提供仅适用于 64 位 Windows®10 的驱动程序。Vulkan* 驱动程序目前包含在英特尔® 高清显卡驱动程序中,可帮助简化设置过程。

这些说明要求:

UE4 和 Vulkan* API 搭配使用时要求重新构建引擎,而且这一操作必须在 Vulkan* SDK 下载并安装之后进行。重建 Unreal Engine 需要使用引擎源代码,GitHub* 向通过 Epic Games链接至 git 帐户的注册用户免费提供这一代码。下文将详细介绍所有必要步骤。

这些说明适用于设置采用英特尔® 高清显卡的开发主机。

第一部分:下载英特尔® 显卡驱动程序

1.访问 英特尔下载中心

2.从“Select a Product”下拉菜单中选择“Graphics Drivers”。

3.根据开发主机选择所需的驱动程序。

4.下载 .ZIP 版驱动程序。

5.提取 ZIP 中的所有文件并创建可存储的目标文件夹。

第二部分:在 Windows* 中更新显卡

6.在 Device Manager 中展开“Display adapters”,右击 Intel® HD Graphics adapter 并选择“Update driver”。

7.在“Update Drivers”屏幕中选择“Browse my computer…”

8.选择“Let me pick…”

9.选择“Have Disk…”

10.导航至包含第 5 步中解压的文件的文件夹。

11.如果成功,将出现一条如下所示的消息:

第三部分:为 Vulkan* 配置 UE4

12.下载并安装 Vulkan* SDK

13.如果还未登录 GitHub*,请现在登录。然后打开 Unreal Engine Launcher 并点击“Get the source code on GitHub*”(通过 Epic Games 链接至GitHub*帐户的注册用户可免费下载 UE4 源代码,对此我们下文予以介绍)。

单击“Grab the source”链接进入 GitHub* 页面,如图所示:

13a.打开您的 Epic Games 仪表板并链接至您的 Epic 和 GitHub* 帐户:

14.链接至帐户后,您的电子邮件收件箱将立即收到一封确认邮件。返回至 Epic Games GitHub* 页面并寻找加入邀请:

点击“View invitation”进入如下所示页面。单击“Join Epic Games”。

15.此操作将带您返回至提供 UE4 代码库的 Epic Games GitHub* 页面。单击“UnrealEngine”继续。

16.通过“Branch:”按钮选择“master”分支。然后点击“Clone or download”按钮。

重要提示:

如果不打算将引擎变动归还至社区,请下载 .Zip 文件。如果有此打算,然后分开主分支,将其克隆至您的硬盘并从硬盘上开始操作。

将 .Zip 提取(或将代码库克隆)至硬盘上的合适位置。

17.文件下载完成后,打开新目录并运行“Setup.bat”,然后等待运行完成(需要等待一段时间)。

18.在同一个目录中运行“GenerateProjectFiles.bat”以创建“UE4.sln”项目:

注:在某些系统上可能需要激活某一 Visual Studio 特性才支持“GenerateProjectFiles”脚本完成这一任务。

如果首次失败,请打开 Visual Studio 并进行以下操作:

a. 选择 Tools> “Tools and Features… ”

b. 在特性列表中选中“Game development with C++”

c. 在右侧的面板中选中“Unreal Engine Installer”

d. 单击“Modify”保存更改

e. 再次运行“GenerateProjectFiles”

双击“UE4.sln”文件打开 Visual Studio 中的项目

20.在 VS Solution Explorer 中右击 UE4 项目并选择 Build。

21.构建过程完成后,设置 UE4 Editor 快捷键,通过以下步骤将其放入“Vulkan* mode”:

a. 前往 "C:\<installation_dir>\Engine\Binaries\Win64\ 

b. 创建适用于文件"UE4Editor.exe”的快捷键

c. 将快捷键 Target 设置为:"C:\<installation_dir>\Engine\Binaries\Win64\UE4Editor.exe" -vulkan

现在,当您从快捷键开始时,将可以使用 Vulkan* API 成功构建 UE4 项目。

VR电竞游戏在英特尔®架构上的用户体验优化

$
0
0

王文斓,英特尔公司

吴亚光,第一摩码娱乐科技有限公司

 

摘要

作为人与虚拟世界之间的新型交互方式,VR 能够让用户在模拟现实中获得身临其境的感受。但是,鉴于 VR 的帧预算为每帧 11.1ms (90fps),实现实时渲染并不容易,需要对整个场景渲染两次(一只眼睛一次)。在本文中,我们将重点探讨 VR 游戏的性能和用户体验优化,介绍我们在《Code51》这款VR电竞游戏中使用的缓解晕动症技术,延长用户的游玩时间,还将介绍《Code51》中的性能优化和差异化,提升玩家和观众在整个游戏中的用户体验。

 

引言

《Code51》是全球首款支持 Oculus Rift、HTC Vive、PSVR 和 Pico VR 的机甲竞技场 VR 游戏,它可支持全球玩家之间四对四对抗,专为 VR 电子竞技量身定制,采用可最大限度减少晕动症的游戏设计和内置的观察者模式。《Code51》已在中国 3,000 多个 VR线下店和体验中心上线(造梦和乐客),并且计划在 2018 年第二季度在 PlayStation Store、Oculus Store 和 Steam 上线。

英特尔与第一摩码紧密合作,致力于优化该游戏的用户体验和性能,并且在英特尔® 酷睿™ i7 处理器上增添了视觉和听觉增强效果,包括 3D 音效、物件破坏、更丰富的CPU粒子和额外的背景效果等。

我们将在本文介绍《Code51》中帮助提升 VR 游戏沉浸感和用户体验的 7 个设计要点。

 

沉浸感 VR 游戏的设计要点 

在 VR 中身临其境地移动

当前有 4 种在虚拟世界移动的方式,分别是:

  • 瞬移 + 6自由度 (DoF) 追踪(如《Robo Recall》)
  • 虚拟座舱(如《EVE:  Valkyrie》)
  • 硬件运动模拟器(如Virtuix Omni™)
  • 大空间追踪系统(如OptiTrack™)

所有这些解决方案各有优缺点。《Code51》采用了虚拟座舱的移动方式,原因如下:

  1. 能够連续移动是提升 VR 沉浸感的一种重要方法,因为它与我们的现实体验相匹配,虚拟座舱是唯一可支持在 VR 中連续移动且无需增加额外硬件和成本的方法。
  2. 鉴于目前高端 VR 头盔的销量并不高,可兼容 3自由度 VR 头盔的移动方式有助扩大用户基数并减少将游戏移植到这些设备上的工程量。
  3. 《Code51》是一款VR电竞游戏,玩家坐在机甲座舱内与其他人战斗,这种“坐式”体验与虚拟座舱的移动方式完美匹配,提升了沉浸感。
  4. 坐著使用游戏手柄玩没有站着使用动作控制器玩那么累人,用户可以更长时间地畅玩 VR 游戏[1]。图 1 展示了用户如何使用各种 VR 头盔畅玩《Code51》。

图 1.《Code51》支持各种 VR 头盔。左侧:HTC Vive (6 DoF)。右侧:Pico VR (3 DoF)。

 

 缓解 VR 中的晕动症

晕动症[2]是影响用户不能长时间体验 VR 的主要因素之一。引起这种症状的因素包括:

  • 辐辏调节冲突引起的视觉压力[3]
  • VR 场景没有方向线索或参照物(即不能获得当前移动方向的提示)
  • 较低的帧率或较高的 MTP 延迟
  • 眼睛所见和身体感受之间的加速度不匹配
  • 角速度
  • 镜头拉近或拉远
  • 低PPD (每度像素数)引起的模糊

《Code51》采用了几种方法来最大限度地减少晕动症

  • 用户界面设计
    • 将控制面板放到离用户 1 米以外以避免频繁改变用户的辐辏距离,减少辐辏距离相对于焦距的变化
  • 场景设计
    • 在场景中提供清晰的方向线索,以便用户始终知道正在走哪个方向,这能够让人的大脑提前做移动准备,减少眩晕。在实践中,避免添加可能会阻挡用户整个视野的纯纹理对象,因为它缺少视觉线索,让大脑无法感知目前的移动方向,等效增加了额外的加速度变化。
  • 渲染性能
    • 优化性能,让《Code51》以稳定 90fps 的速度渲染,最大限度地降低 MTP 延迟。
  • 减少加速运动
    • 尽可能地避免在《Code51》中添加加速度以减少眩晕,速度应该在不同程度之间作即时切换。例如仅在运动开始瞬间添加加速度,然后在跳跃或着陆期间保持恒定速度。
  • 降低角速度
    • 降低高速旋转(角速度)的能力,可帮助减低光流量。
  • 动态缩小 FOV
    • 动态缩小 VR 中的 FOV 有助于减轻晕动症[4][5]。《Code51》中采用了该方法,使用depth buffer(深度缓存)计算出屏幕四个角落的移动速度,然后在stencil buffer(模板缓存)中根据速度大小将顶点相应地向内弯曲,以减少运动期间的 FOV。正如图 2 中所示,移动的越快,FOV 越狭窄。它能够减少最终用户感知到的光流量。

图 2.《Code51》中采用动态 FOV 来消除屏幕边缘的光流,在实际测试中用户在 VR 裡并不太感觉出FOV的缩小,却能够帮助缓解晕动症。

 

最大限度降低网络延迟

对于 VR 应用,降低网络延迟也很重要,这样才能避免画面滞后,拥有流畅的游戏体验,否则容易引起晕眩。在《Code51》中,机甲本身的所有动作都是在本地预测,然后以较低的频率与服务器作同步和修正,避免网络不佳时本地出现频繁的修正动作,干扰游戏进行。如果同步失败,客户端会根据之前的轨迹作插值。

 

增强 VR 电竞游戏的观看体验

为了给 VR 电竞观众打造更好的观看体验,《Code51》内嵌了观察者模式。观察者模式在游戏中是一个独立的客户端,允许观众通过键盘、鼠标或手柄操控从任何视角和位置观看实时战斗画面,画面能够以双目或单目方式输出到 VR 头盔或者大型显示屏(如图 3 所示)。

图 3.《Code51》中的观察者模式。最终用户可以采用 VR 或非 VR 模式观看现场战斗。

 

保持渲染画面的锐利度

相对于传统显示器,当前 VR 头盔显示器的 PPD (每度像素数)仍然很低,因此 VR 应用需要尽可能地保证渲染画面的锐利度以减少画面模糊和因此引发的眩晕。当前 UE4 开发人员可使用的抗锯齿 (AA) 方案大致有以下三种

  • 页框交错抗锯齿 (TAA)
    • 在静态环境中,TAA 及其衍生AA算法拥有良好的抗锯齿质量,且计算成本适中。对于半透明物件的 TAA,必须在额外的步骤中计算,因为这些物件没有可用的深度信息。此外,VR 中使用的 TAA 最好采用保守的参数设置,以减少由于头部移动或动态场景中产生的 VR 模糊。
  • 多重采样抗锯齿 (MSAA)
    • MSAA 只能在正向渲染管线中使用,《Code51》采用了该方法来最大限度地减少 AA 所产生的 VR 模糊。VR 裡一般采用 4 倍 MSAA 就能达到不错的效果。
  • 屏幕空间抗锯齿 (SSAA)
    • SSAA 的计算成本较高,但是能够实现最佳的抗锯齿质量,《Code51》中还使用了 1.4 倍 SSAA。

 

CPU 性能优化

为了降低 UE4 DX11 VR 游戏的 CPU 瓶颈,需要减少渲染线程的工作负载[6],UE4 (4.20+) 中为 DX11 实现的 RHI线程可通过 D3D11 多线程渲染减轻主渲染线程的负载[7],所以尽可能启用 RHI 线程。

此外,UE4 中还有各种优化主渲染线程负载的方法[8],《Code51》中就采用了其中一些方法:

  • 减少绘制调用(根据我们在前向渲染管线上的分析经验,运行在高端 VR 头盔的应用最好将绘制调用数目限制在2,000以下来减少绘制调用的状态变换开销,避免产生渲染线程瓶颈。如果 GPU 优化得较好(每帧的 GPU 计算时间较低),每帧有较多的时间留给渲染线程,则绘制调用数目可以超出这数字而不掉帧)
  • 优化可见性剔除 (InitView)
    • 修改场景物件以减少动态遮挡剔除(硬件遮挡查询或层次Z-缓冲算法),因为计算一个只在屏幕上出现一小部分的大型物件的遮挡剔除是一件计算效率低下的事情。图 4 显示了该优化的示例
    • 使用预计算遮挡剔除[9]减少运行时需要使用动态遮挡剔除处理的基元 (primitives) 数量。这方法在《Code51》中效果相对没那么明显,因为游戏中的机甲能飞到空中,降低了预计算的效果。在《Code51》中,当启用预计算遮挡剔除时,可被预计算处理掉的静态遮挡的基元数量(大约 300 个基元)是总遮挡剔除基元数量的 20%
    • 屏蔽遮挡剔除(Github/论文)是采用 SIMD 加速的CPU 遮挡剔除算法(SSE、AVX 和 AVX-512),它可以取代层次Z-缓冲算法,能够在现代的多核 CPU 上高效地并行计算
  • 减少动态光源,关闭动态阴影投射,如果预算允许的话可使用胶囊阴影 (capsule shadows)
  • 尽可能地使用光照烘焙
  • 使用 LOD 和 HLOD,保证高端 VR 下三角形数目少于 250 万
  • 使用粒子 LOD,不要在 LOD0 中使用半透明粒子

图 4. 场景中一个物件从左图修改为右图的样子,以减少原图中该物件后面的遮挡剔除计算。

 

差异化和特效强化

最后,优化 VR 应用体验的一个重要方向是尽可能地利用硬件平台上的所有可用计算资源,在该平台上提供最佳体验。例如,使用英特尔® 酷睿™ i7-7700K 处理器的用户比仅使用英特尔® 酷睿™ i5-4590 处理器(未使用 ASW 的 Oculus Rift 的 CPU 最低要求规格)的用户有更高的主频和更多的 CPU 核心,这意味着可以添加额外的 CPU 计算来增强应用的视觉和听觉效果,提升体验的同时不影响帧率。

在《Code51》中,我们加入了一些主要用 CPU 作计算的强化特效来更好地用上高端 CPU 的计算资源,这些特性包括 3D 音效、物件破坏、CPU 粒子增强和炫酷的背景效果,如以下视频中所示:

其中大部分强化音效和画面表现的 CPU 计算(例如 PhysX™ 计算或Steam™ Audio 中利用光线跟踪算法的环境音效计算等)都是由 UE4 的工作线程或音效模拟线程处理。因为大部分计算都被分配到闲置的 CPU 核心,这些强化效果大大加强了沉浸感的同时最大程度避免了对帧率的负面影响。

图 5 展示了《Code51》的帧率数据。数据显示该游戏在开启强化效果的同时能够在英特尔® 酷睿™ i7-7700K 和英特尔® 酷睿™ i7-7820HK 上流畅运行,但在英特尔® 酷睿™i5-4590上开启强化效果则会导致帧率显著下降,这意味着 i5 CPU 在开启强化效果下无法在 11.1ms 内完成一帧渲染。但只要关掉所有强化效果(切换到无强化效果的低画质设置),使用 VR 最低 CPU 规格的用户仍可顺畅地游玩《Code51》。根据下列《Code51》的帧率数据,当所有 CPU 强化效果均打开时,英特尔® 酷睿™ i7-7700K 比英特尔® 酷睿™ i5-4590 性能高出 27%。

图 5.在不同 CPU (英特尔® 酷睿™ i5 和 i7 处理器)和画质设置下的《Code51》性能数据,“超高 Ultra High”是开启 CPU 强化效果的,“低 Low”是未开启 CPU 强化效果的。测试系统: 英特尔® 酷睿™ i5-4590,NVIDIA® GTX1080,2x4GB DDR3-1600,Windows™ 10 版本 1703;英特尔® 酷睿™ i7-7700K,NVIDIA® GTX1080,4x4GB DDR4-2400,Windows™ 10 版本 1703;英特尔® 酷睿™ i7-7820HK,NVIDIA® GTX1080,4x4GB DDR4-2400,Windows™ 10 版本 1703。

图 6 和图 7 展示了不同设置下《Code51》的 GPUView 屏幕截图,两图中均使用“超高” 画质设置和采用英特尔® 酷睿™ i7-6800K 处理器,并使用 msconfig 分别将 CPU 内核设置为 4C4T 和 6C12T,并以相同的 CPU 频率运行。从图表中可以明显看出,在 4C4T 配置下(模拟 4C4T 英特尔® 酷睿™ i5 处理器),一帧的渲染无法在 11.1ms 内完成,并且出现了帧率显著下降的情况,但在6C12T 配置下却能够流畅运行。

下列是在相同英特尔® 酷睿™ i7-6800K 上运行、两种配置下(6C12T 和 4C4T),用 Windows™ 性能分析器 (WPA) 观察到的《Code51》中不同线程上增加的 CPU 计算百分比:

总 CPU 工作负载:   43%↑

渲染线程:                44%↑

GPU驱动线程:        10%↑

逻辑线程:                13%↑

工作线程:                89%↑

在《Code51》中,大量 CPU 强化效果被移到工作线程计算,导致这些线程的计算增加率最大。

图 6.“超高Ultra High”设置下采用英特尔® 酷睿™ i7-6800K 处理器和 GTX1080 显卡的《Code51》的 GPUView 屏幕截图,在这里 CPU 内核用 msconfig 设置为 4C4T。测试系统: 英特尔® 酷睿™ i7-6800K,NVIDIA® GTX1080,4x4GB DDR4-2400,Windows™ 10 版本 1703。

图 7.“超高Ultra High”设置下采用英特尔® 酷睿™ i7-6800K 处理器和 GTX1080 显卡的《Code51》的 GPUView 屏幕截图,在这里 CPU 内核 (6C12T) 未做任何修改。测试系统: 英特尔® 酷睿™ i7-6800K,NVIDIA® GTX1080,4x4GB DDR4-2400,Windows™ 10 版本 1703。

 

结论

让用户感到舒适和减轻晕动症是 VR 电竞游戏成功的两大要素,只有能够让用户长时间沉迷其中的电竞游戏才能真正成功。《Code51》采用了文章中介绍的各种方法来减轻晕动症,同时内置了观察者模式帮助提升赛事观看体验。在性能优化方面,根据 GPUView 和 WPA 可以看出渲染线程通常是 UE4 DX11 VR 应用的主要 CPU 瓶颈,标淮的游戏优化方法以及将任务从主渲染线程移到其他线程能带来不少性能提升,此外,像《Code51》一样利用闲置的 CPU 资源添加更炫酷的画面和音效强化效果,可以让游戏从众多竞品中脱颖而出。

 

参考文献

[1] http://blog.leapmotion.com/taking-motion-control-ergonomics-beyond-minority-report/

[2] https://en.wikipedia.org/wiki/Virtual_reality_sickness

[3] Shibata、Takashi 等等“立体显示引起的视觉不适:观看距离的影响和视觉辐辏调节冲突的方向。”《立体显示及应用 XXII。第 7863 卷。国际光学工程学会,2011 年。

[4] http://engineering.columbia.edu/fighting-virtual-reality-sickness

[5] Fernandes、Ajoy S.和 Steven K. Feiner.。“通过精细的动态视野修改减轻 VR 眩晕。”3D 用户界面 (3DUI)2016 IEEE 座谈会。IEEE,2016 年。

[6] Finn Wong,“PC VR游戏的CPU性能分析与优化”,2016 年

[7] https://msdn.microsoft.com/en-us/library/windows/desktop/ff476891(v=vs.85).aspx

[8] Finn Wong,“Unreal* Engine 4 VR 应用的 CPU 性能优化和差异化”,2017 年。

[9] http://timhobsonue4.snappages.com/culling-precomputed-visibility-volumes

 

关于作者

王文斓是英特尔合作伙伴关系部 (DRD) 的一位高级软件工程师。自 2015 年以来,他一直负责 VR 内容支持和技术合作,帮助 VR 开发人员优化 CPU 性能和实现 CPU 内容差异化,从而为最终用户提供真正的沉浸式 VR 体验。王文斓曾被邀请在 CGDC、VRCORE、腾讯 GDOC、Unreal Circle、Unity Unite 和 Vision AR/VR 峰会等各种 VR 会议上做技术演讲。在此之前,王文斓在英特尔从事 H.264/H.265 和 RealSense 应用的性能优化工作,在视频编码、视频分析、计算机视觉、算法和性能优化领域拥有 10 多年的丰富经验,並发表了若干学术论文。王文斓拥有国立台湾大学电机工程学士学位和通信工程硕士学位。

吴亚光是第一摩码娱乐科技有限公司首席执行官兼联合创始人。他拥有多年深厚的游戏开发经验,从 2016 年开始领导了知名 VR 游戏《Code51》的开发工作。

使用Movidius神经计算棒(NCS)运行Caffe图片分类模型

$
0
0

作者:裴凡江

概要

Movidius神经计算棒(NCS – Neural Computing Stick)是Movidius推出的基于Myriad 2视觉处理单元(VPU – Visual Processing Unit)的神经计算棒,其最大的特性是可以在1瓦的功率下提供超过每秒1000亿次浮点运算的性能。这一特性使它可以被集成到低功耗的便携设备上,让这些低功耗设备也具有直接运行实时深度神经网络(DNN – Deep Neural Network)的能力,从而使得各种人工智能应用都能够离线部署。

通常来说,开发使用NCS的应用主要分为三个步骤:

  1. NN模型的训练:这一阶段的工作主要是训练出DNN模型,需要在运算能力强大的工作站或者服务器上进行。目前NCS支持Caffe和Tensorflow框架训练出来的模型。
  2. DNN模型的编译和调优:NCS的VPU是无法直接运行第一步骤中训练好的Caffe/TensorFlow模型的(比如Caffe框架训练出的caffemodel文件)。NCS SDK提供了专门的工具用于编译和优化Caffe/TensorFlow模型,最终将模型编译成NCS可以运行的专用模型文件(graph)。这一阶段的工作通常在运行Ubuntu的台式机或者笔记本上完成。
  3. 最后一步我们将在目标设备(集成NCS硬件的设备)上调用 NCS SDK API运行DNN模型。NCS SDK API提供了Python和C语言的支持。下文我们运行Caffe图片分类模型时将给出基于Python的实现。

使用NCS运行Caffe图片分类模型

准备工作

  1. 装备Ubuntu 16.04操作系统的Intel x86_64开发机;
  2. 将NCS通过USB 3.0接口连接开发机;
  3. 安装Movidius NCS SDK和Caffe到开发机,详细安装手册可以参考以下链接:https://software.intel.com/zh-cn/articles/how-to-deploy-tensorflow-and-caffe-for-intel-hardware-platform-into-movidius-ncs-sdk

训练Caffe图片分类的DNN模型

DNN网络的训练并不是此文的目的,这里我们直接借用以下文档中所训练出的Caffe模型和测试数据集。读者可以自行按照文档的步骤训练出完全一致的Caffe模型(文档提供基于Windows系统的操作指南,其在Ubuntu系统下同样适应)https://software.intel.com/zh-cn/articles/the-caffe-practice-on-windows-the-war-between-cat-and-dog

DNN模型的编译

如前文所述,Caffe训练出来的DNN模型是无法直接运行到NCS上的,我们需要使用NCS SDK所提供的编译工具mvNCCompile将Caffe模型编译成专用的graph文件:

mvNCCompile ./dogsvscats.prototxt -w stored_model_iter_10000.caffemodel -s 12 -o ./dogvscat_test.graph

相关参数说明:

   -w:需要编译的Caffe模型

   -s:最多使用多少个SHAVEs(最大值为12)

   -o:编译输出结果(graph文件名)

NCS上运行编译好的DNN模型

完成了DNN模型的编译工作之后,我们就可以调用NCS SDK提供的API,来运行我们的图片分类模型了。为此,我们新建一个python源文件(这里我们命名为image-classifier.py),然后开始我们的Python代码编写工作(注: 以下代码实现参考了Movidius官方提供的示例代码:https://github.com/movidius/ncappzoo/blob/master/apps/image-classifier/image-classifier.py

首先我们需要引用相关的python模块(这里的mvnc.mvncapi就是NCS SDK提供的Python API封装):

然后我们配置运行图片分类模型所需的参数,包括模型相关的参数,以及图片相关的参数:

参数的补充说明如下:

GRAPH_PATH:通过NCS SDK中的mvNCCompile工具编译好的graph文件

IMAGE_PATH:需要做推理的图片

LABELS_FILE_PATH:label文件

IMAGE_MEAN:图像均值(由训练DNN模型过程中计算得到)

IMAGE_STDDEV:图片缩放值

IMAGE_DIM:图片的维度(由训练DNN模型之前设置)

                 

接下来我们就可以分五个步骤来调用NCS进行推理工作了

  1. 获取系统可用NCS列表,并打开第一个可用的NCS
  2. 将graph文件加载到NCS
  3. 将需要推理的图片加载到NCS
  4. 获取并打印推理结果
  5. 释放相关资源

运行结果

将以上步骤中涉及到的代码(包括模块引用以及参数设置)保存到image-classifier.py文件并在终端中运行,将会看到类似这样的输出结果:

结语

文中介绍了如何利用单个NCS运行Caffe DNN模型,事实上如果我们将多个NCS同时集成到设备上,那么就可以获得近乎线性的性能提升。比如文章所介绍的图片分类模型,我们可以让不同的NCS实例同时对不同的图片做预测,以获得成倍的性能提升。这在真实应用场景下同样适应,例如我们对摄像头实时捕捉到的图像做物体识别时,同样可以将视频的不同帧按照时序分别交给不同的NCS实例进行识别,以获得加倍的FPS(每秒处理帧数)。

参考文献

NCS SDK快速上手:https://developer.movidius.com/start

NCS SDK与Caffe的集成:https://software.intel.com/zh-cn/articles/how-to-deploy-tensorflow-and-caffe-for-intel-hardware-platform-into-movidius-ncs-sdk

在Windows上的Caffe实战 – 猫狗大战:https://software.intel.com/zh-cn/articles/the-caffe-practice-on-windows-the-war-between-cat-and-dog

NCS SDK参考手册:https://movidius.github.io/ncsdk/

Intel Caffe:https://software.intel.com/zh-cn/ai-academy/frameworks/caffe

NCS 购买:https://developer.movidius.com/buy

关于作者

裴凡江是英特尔酷睿与视觉计算事业部的一名应用软件工程师,专注于在英特尔平台上与开发者的合作和业务拓展。力求将英特尔卓越的软硬件平台与开发者的软硬件产品完美结合,提供最优客户体验。


借助混合现实视频在虚拟现实领域脱颖而出

$
0
0

混合现实视频为何是宣传 VR 游戏的明智方法

很少有游戏开发人员不希望自己的创意成为热门游戏。虽然虚拟现实 (VR) 游戏世界可能尚未进入关键的轰动时刻,但在这一新兴(至少现在是)市场,VR 硬件所有者纷纷涌入开展激烈的竞争,只为分得一杯羹。

在 Steam 商城快速搜索,搜索结果显示超过 2000 种 PC VR 应用和游戏(大部分自 2016 年起开始推出)。在这种情况下,你一定希望自己的 VR 游戏不被忽略,并快速吸引潜在玩家的眼球。混合现实视频可以帮助实现这一目标。

Player using mixed-reality
图 1.HTC Vive* 在 2016 年的视频中所展示的绿屏混合现实。

VR 游戏的绿屏混合现实视频是一种 2D 视频制作技巧,为观众提供游戏世界中的第三人称玩家视角。使用此处介绍的软硬件堆栈,视频创建者可以记录或流传输沉浸在虚拟环境中玩家的现场游戏过程,观赏体验不再局限于第一人称头显视角。每一位 VR 开发人员都应该尝试一下这种技术。

“哇,太酷了!”几乎每一个人都这样说。

HTC 和 Valve* 在 2016 年初发布的 Vive 演示 视频中率先使用混合现实展示 VR,而且 Owlchemy Labs 的Job Simulator和 Northway Games 的Fantastic Contraption预告片都采用了这项技术,效果非常好。Croteam 的 Serious Sam开发人员也提倡采用这项技术,他们不仅在 2016 年 EXG 的早期演示中使用,还在所有 VR 游戏中实施了专有混合现实视频工具。

英特尔® 开发人员关系事业部的 Josh Bancroft 和 Jerry Makare 一年多来一直合作采用这项技术,在 2017 年 GDC、台北电脑展、E3 游戏展,以及最近的纽约英特尔假日展示会中都部署了现场演示。对他们来说,使用混合现实视频在 VR 领域脱颖而出是轻而易举的事。一听到现场的反应 —主要是上述副标题的变化 — 他们就知道混合现实有着第一人称视角无可比拟的优势,为没有佩戴头显的观众带来令人叹服的 VR 体验感。


图 2:英特尔团队在 2017 年台北电脑展上的现场绿屏混合现实演示,题为 Rick and Morty: Virtual Rick-ality

“VR 混合现实视频最大的优势是,可以让大家看到 VR 体验中的感觉到底如何,”Josh 说。“如果你作为第三方观看第一人称 VR 视频,会有一些你并没意识到的事情让你分心 — 当你处于 VR 情景之中,大脑缓和了你的头部移动,但如果你以第三人视角观看 VR,会出现很大的跳跃造成干扰。”第三人称混合现实视角巧妙地消除了这些干扰。

分享即是关心

对于许多游戏玩家来说,与朋友分享他们的体验是一件很自然的事,无论他们是在线玩家、本地多玩家,还是电视前的观众。使用 VR 时,玩家封闭在 VR 头显内,画面中只有第一人称视角,这样更难和其他人一起体验。混合现实可以改变这一点。

“使用第三人称视角可以提高社交性,使它更多地成为一种共享体验,”Jerry 说。"人们可以看到环境,知道你所反应的对象,进而产生各种好玩的事情,就像喜欢对司机指手画脚的人,他们提示你怎么玩游戏。”

VR game played in a social setting
图 3.2016 HTC Vive* 视频展示混合现实如何提高 VR 的社交性。

“目前 VR 市场竞争激烈,很难让自己脱颖而出,”Josh 说。“这是一个全新的领域,每个人都跃跃欲试,但没有人真正理解其中的奥秘、知道成功的秘诀,所以这其中有大把的机会。”使游戏支持混合现实技术可以帮助开发人员把握机会。

首先,相比标准第一人称视角,它支持创建包含完全沉浸于游戏体验的预告片、视频和现场演示。然后,可以在活动上使用这些预告片和视频展示游戏。Josh 表示:“使用第三人称混合现实视角可帮助你吸引更多的人观看预告片和购买游戏。”

第二,使游戏支持混合现实让内容创建者、流传输工作人员和 YouTube 视频创建者有机会为你的游戏创建混合现实视频。YouTube 视频创建者,包括 Get Good GamingDashieGames,在他们的视频中使用混合现实,达到了预期的效果。正如有关 DashieGames 视频的热评所述:“我可以好几个小时一直观看这类游戏,丝毫不会感到厌烦。”

Screenshot of user in a mixed-reality game
图 4:YouTube 视频制作者 Get Good Gaming 身穿角斗士服装使用混合现实技术玩 VR 角斗士题材游戏 Gorn

许多开发人员已经开始实现旨在帮助流传输工作人员和内容创建者的功能,例如优化 Twitch* 流传输和集成观众交互工具。说到 VR 游戏,混合现实支持必须处于列表中较高的位置。Northway Games 在 Fantastic Contraption中构建了一整套 content-creator 功能,包括混合现实支持。他们希望将其用在自己的流媒体中,但更重要的是,他们希望玩家也这样使用,从而达到以完美的内容吸引更多观众的目标。

Fantastic Contraption screenshot
图 5.Northway Games 的一种现场混合现实流媒体,标题为 VR 游戏 Fantastic Contraption。

“流传输工作者、YouTube 视频制作者和内容创建者在构建集成方面有很大的增长潜力,在游戏中添加混合现实支持刚好与这种增长相匹配,” Josh 说。“如果你证明自己的游戏能让流传输工作者和内容创造者为观众制作令人惊叹的混合现实视频,这将会为你的成功提供巨大的帮助。”

郑重承诺

过去几年,独立开发商 Croteam 一直履行着将 VR 融入所有最新游戏的使命,包括四款 Serious Sam游戏和 The Talos Principle。其核心是创新者,他们将不可避免地探索各种混合现实的可能性,从而推动 VR 领域的发展。“所有看起来非常炫酷的视频都采用了混合现实技术,所以这似乎是向人们解释 VR 体验的最好方法,” Croteam 资深程序员兼内部混合现实专家 Goran Adrinek 说。

他们曾获得机会在英国举办的 EGX 2016 上展示Serious Sam VR: The Last Hope*,有了展示混合现实能力的机会。Goran 表示:“我们计划通过实施某种实时混合现实,这样在展台上的我们会看起来很酷,还要捕捉一些宣传视频,展示物理迷你炮机枪控制器。”

Woman with gun playing VR
图 6.Croteam 的绿屏混合现实技术成功展示 Serious Sam* 迷你炮机枪。

从籍籍无名到一炮而红

但是他们面临一个问题:距离展会只有两周的时间,但游戏仍然无法支持混合现实。“那时我们仅仅了解混合现实是什么,我们试图像其他人一样: 渲染可以在开放直播软件中混合的游戏的几个画面,”Goran 说。

但他们是完美主义者,对结果不满意,所以决定在专有 Serious Engine 中构建自己的混合现实工具,在过去 20 年的大部分时间,他们一直在对该引擎进行更新迭代。他们选择将整个混合现实过程融入到引擎之中,包括捕捉现场视频,并在游戏中实时合成。

这一大胆的举动最终有所回报。“我们及时赶上了 EGX 2016,而且效果很好,”Goran 说。“观看别人在混合现实中玩游戏比观看第一人称视频要好得多,你可以看到它在展区里引起了很大的关注。”

Users playing mixed-reality games
图 7.Croteam 在 EGX 2016 现场展示的 Serious Sam VR: The Last Hope* 绿屏混合现实演示。

主场优势

除了只需一种软件(VR 游戏)来管理整个过程的优势,在游戏中创建混合现实视频还有增强效果的优势,因为他们将生成的玩家图像融入了游戏引擎。

将引擎中的所有内容进行编码,从视频捕捉和虚拟游戏第三人称摄像头,到绿屏色度键与合成,显然这些任务非常繁重,但看起来并没有难倒 Croteam。“很难说到底需要做多少工作,但也没有那么多,因为我们没有太多的时间,”Goran 说。

对 Croteam 来说,及时参加 EGX 只是支持混合现实的开端。“EGX 结束后,我们继续开发混合现实,直到解决所有困扰我们的问题,比如相机校准、控制器延迟和摄像头移动场景,”Goran 说。“最终,我们开发出了每个人都能轻松掌握的设置向导,它能帮助轻松完成设置。”

Mixed Reality Setup Wizard
图 8:Croteam 的所有 VR 游戏都包含混合现实设置向导。

他们还准备了更多,以便继续磨练技术和工具。“我们使用深度信息开展了一些试验,以便将玩家图像融入游戏时获得更好的视觉效果,这种尝试非常不错,”他说。“我们希望将来能够继续发展这种做法。”

混合差异

Croteam 和 Josh 与 Jerry 的想法一致,也看到了混合现实支持在展区和预告片之外的显著优势。Goran 表示:“它还能帮助玩家轻松地制作直接从游戏中捕捉的视频,并将视频传输或上传至 Twitch 或 YouTube* 等在线服务。我们希望有更多的玩家制作的材料,作为最直接的成果在网上宣传在游戏中支持混合现实是多么简单。”

尽管 EGX 举办的前两周取得了巨大成功,但 Goran 不希望让一切听起来容易。他说:“无论是在后期制作中进行混合,还是投资可简化整个过程的技术,都是一项巨大的工作。"但最终,一切投资都是值得的。Goran 继续说:“每个人都可以使用这种方法和工具,即使是最基本的形式,也能产生巨大的效果。”

Croteam 将整个混合现实的实施过程写在博客中,是每个希望了解这项技术的基本读物。

超越游戏

并不只有游戏开发商才能从创新混合现实应用中获益。乔希强调的另一个领域是房地产。Google 快速搜索显示,虚拟房产演练已经非常普及,支持潜在买家从自己舒适的家里或房地产经纪人的办公室参观房产。Josh 建议:“如果更进一步,可以生成房地产经纪人带领他们参观房产的混合现实视图。还可以给他们提供混合现实视频,他们可以通过视频看自己的参观路线,这样他们会更加喜欢。”

VR in real estate image
图 9.CNN Money* 以题为 "Virtual reality is the new open house” 的文章报道 VR 在房地产领域的使用。

从他作为视频创作者的角度来看,Jerry 设想混合现实可以作为视觉故事叙述的强大工具,可能与风靡伦敦、纽约和其他地方的沉浸式剧场体验相关。“人们都将有机会打造丰富、巧妙地 VR 混合现实体验,”Jerry 说。“这一切都是为了丰富社会环境,拓展范围,以便人们更好地了解事情的进展并相互分享体验。

随即而来的是 2017 年 6 月发布的 Spider-Man: Homecoming - Virtual Reality Experience游戏,Josh 希望看到更多的商业品牌打造 VR 和混合现实体验来作为营销工具。“想象一下,你可以在电影院或主题公园中设置一个环境,把自己插入到你最喜欢的电影或游戏体验中,和钢铁侠一起闲逛或者和蜘蛛侠一起荡秋千,”Josh 说。“不仅可以在 VR 头显中获得 VR 体验,还能借助混合现实,制作关于自己的 VR 体验视频,与人分享。”

Spiderman game screenshot
图 10:Sony Pictures Entertainment 发布的 The Spider-Man: Homecoming - Virtual Reality Experience* 预告片。

果断采取行动

但是,开始将我们与超级英雄面对面的混合现实视频发布到社交媒体之前,在游戏世界中有更多的工作要做,混合现实的视频还需要开发人员发挥更多的创意。

其中有一个创意进展令 Josh 印象深刻,就是 2016 年东京游戏展上的 Circle of Saviors* VR 游戏现场演示。比赛中玩家用一把巨大的剑砍龙,需要进入一种稳定的的怀疑暂停状态。为了完成这一效果,一个完全角色扮演(包括身披红斗篷)的玩家进行绿屏演示,最终合成的混合现实视频显示身处角色中的玩家完全沉浸在游戏世界中。

“太不可思议了!”Josh 说。“开发人员看到了混合现实的强大能力,并意识到可以用它来完成一些与众不同的事情,以创造性的眼光审视之前完全不可能的新方向。”

Cosplay user in a mixed-reality game
图 11.2016 年东京游戏展上角色扮演玩家以混合现实的方式展示 Circle of Saviors

而且这是关键 — 无论是否佩戴头显,都可以看到 VR 中可能发生的事情。目前混合现实视频很好地向观众传达了“这可能是你”的概念,而且 VR 开发人员认为自己以及自己的游戏有必要看看它可以做什么。

“使用混合现实展示完全沉浸在 3D 世界中的玩家是我们用 2D 媒体宣传 VR 游戏的最佳选择,”Croteam 说。“一定要试试。”

虽然确实存在技术挑战,但并非不可逾越,Croteam 和其他人证明了这一点。此外,还会有越来越多的资源可以提供帮助。“并不像看起来那么难,而且很奇怪会上瘾,”Josh 说。

“一旦你能够携带相机进入 VR 并展示第三人称视角的虚拟世界,它将激发你作为开发人员的创造力,你一定会开始想如何炫酷地发挥自己的创造力,”Josh 继续说。“绝对值得!”

未来计划提供更多关于 VR 混合现实技术的故事、教程、案例研究和其他相关材料。随时了解最新信息,请加入英特尔®开发人员计划:https://software.intel.com/gamedev

使用持久内存提升 C++ 应用的性能-一个简单的 grep 示例

$
0
0

下载示例代码

简介

本文展示了如何转换简单的 C++ 程序,选取了著名的 UNIX 命令行实用程序的简化版本 grep 作为示例,以利用持久内存(PMEM)。本文提供了详细的代码,首先描述了易失性版 grep 程序的作用。然后讨论了如何将 grep 添加至搜索结果的持久高速缓存中,以改进 grep。高速缓存通过1添加容错(FT)功能和2加速对已发现搜索模式的查询改进了 grep。本文使用 libpmemobj 的 C++ 绑定继续描述 grep 的持久版本,libpmemobj 是 持久内存开发套件(PMDK)集中的一个核心库。最后,使用线程和 PMEM 感知型同步添加了并行处理(在文件粒度中)。

易失性 Grep

描述

如果您熟悉任何类似于 UNIX* 的操作系统,如 GNU/Linux*,可能也比较了解命令行实用程序 grep(代表以正规表示法进行全域查找并打印)。实质上,grep 接收两种参数(其余是选项):采用正则表达式形式的模式和输入文件(包括标准输入)。grep 的目标是逐行扫描输入,然后输入匹配给定模式的行。更多详情敬请参阅 grep 手册页面(在终端输入手册 grep 或查看 Linux 手册页面,以在线了解 grep)。

我的简化版 grep 只使用了上述两种参数(模式输入),输入应为单个文件或目录。如果提供了目录,目录内容将被扫描,以查找输入文件(通常以递归的形式扫描子目录)。为了查看实际的运行方式,我们将它的源代码用作输入,将“int”用作模式,并运行该程序。

代码可从 GitHub* 中下载。为了从 pmdk-examples 存储库的根编译代码,输入 make simple-grep。 libpmemobj和 C++ 编译器必须安装在您的系统。为了兼容 Windows* 操作系统,代码不会调用任何针对 Linux 的函数。相反,使用 Boost C++ 库集合(主要用于处理文件系统输入/输出)。如果您使用 Linux,可能已面向您最喜爱的分发版配备了 Boost C++。例如,在 Ubuntu* 16.04 中,您可以通过以下方式安装这些库:

# sudo apt-get install libboost-all-dev

如果程序得以正确编译,我们可以按照以下方式运行程序:

$ ./grep int grep.cpp
FILE = grep.cpp
44: int
54:     int ret = 0;
77: int
100: int
115: int
135: int
136: main (int argc, char *argv[])

如您所见,grep 共找到 7 行包含“int”的代码(第 44、54、77、100、115、135 和 136 行)。作为一项合理性检查,我们可以使用系统提供的 grep 运行相同的查询:

$ grep int –n grep.cpp
44:int
54:     int ret = 0;
77:int
100:int
115:int
135:int
136:main (int argc, char *argv[])

截至目前,我们已经得到了预期的输出。代码如下表所示(注:以上代码片段中的行数与下表不匹配,因为代码格式不同于初始源文件):

#include <boost/filesystem.hpp>
#include <boost/foreach.hpp>
#include <fstream>
#include <iostream>
#include <regex>
#include <string.h>
#include <string>
#include <vector>

using namespace std;
using namespace boost::filesystem;
/* auxiliary functions */
int
process_reg_file (const char *pattern, const char *filename)
{
        ifstream fd (filename);
        string line;
        string patternstr ("(.*)(");
        patternstr += string (pattern) + string (")(.*)");
        regex exp (patternstr);

        int ret = 0;
        if (fd.is_open ()) {
                size_t linenum = 0;
                bool first_line = true;
                while (getline (fd, line)) {
                        ++linenum;
                        if (regex_match (line, exp)) {
                                if (first_line) {
                                        cout << "FILE = "<< string (filename);
                                        cout << endl << flush;
                                        first_line = false;
                                }
                                cout << linenum << ": "<< line << endl;
                                cout << flush;
                        }
                }
        } else {
                cout << "unable to open file " + string (filename) << endl;
                ret = -1;
        }
        return ret;
}

int
process_directory_recursive (const char *dirname, vector<string> &files)
{
        path dir_path (dirname);
        directory_iterator it (dir_path), eod;

        BOOST_FOREACH (path const &pa, make_pair (it, eod)) {
                /* full path name */
                string fpname = pa.string ();
                if (is_regular_file (pa)) {
                        files.push_back (fpname);
                } else if (is_directory (pa) && pa.filename () != "."&& pa.filename () != ".."){
                        if (process_directory_recursive (fpname.c_str (), files)
                            < 0)
                                return -1;
                }
        }
        return 0;
}

int
process_directory (const char *pattern, const char *dirname)
{
        vector<string> files;
        if (process_directory_recursive (dirname, files) < 0)
                return -1;
        for (vector<string>::iterator it = files.begin (); it != files.end ();
             ++it) {
                if (process_reg_file (pattern, it->c_str ()) < 0)
                        cout << "problems processing file "<< *it << endl;
        }
        return 0;
}

int
process_input (const char *pattern, const char *input)
{
        /* check input type */
        path pa (input);
        if (is_regular_file (pa))
                return process_reg_file (pattern, input);
        else if (is_directory (pa))
                return process_directory (pattern, input);
        else {
                cout << string (input);
                cout << " is not a valid input"<< endl;
        }
        return -1;
}

/* MAIN */
int
main (int argc, char *argv[])
{
        /* reading params */
        if (argc < 3) {
                cout << "USE "<< string (argv[0]) << " pattern input ";
                cout << endl << flush;
                return 1;
        }
        return process_input (argv[1], argv[2]);
}

我知道代码很长,但是相信我,代码不难运行。我只需要在 process_input()中检查输入是一个文件,还是一个目录。如果是前一种情况,将会在 process_reg_file()中直接处理文件。如果是后一种情况,将会在 process_directory_recursive()中扫描目录下的文件,然后通过调用每个文件上的 process_reg_file(),在 process_directory()中逐一处理被扫描的文件。处理文件时,检查每行是否匹配模式。如果匹配,将该行打印为标准输出。

持久内存

现在我们得到了一个正常运行的 grep,我们看一下如何对它进行改进。首先,我们发现 grep 不会保存任何状态。完成了对输入的分析并生成输出后,程序随即停止。假设我们计划每周都对一个大型目录(拥有几十万份文件)进行扫描,以查找相关的特定模式。假设目录中的文件可能会不断变化(尽管所有文件不可能同时改变),也可能会添加新的文件。如果我们使用经典的 grep 执行该任务,可能会重复扫描某些文件,浪费了宝贵的 CPU 周期。这个限制可以通过添加一个高速缓存来克服:如果已经针对特定模式对文件进行了扫描(而且上次扫描之后内容没有发生变化),grep 将返回缓存的结果,而不是重新扫描文件。

可以通过多种方式实施高速缓存。例如,一种方法是创建一个特定的数据库(DB),以存储每个被扫描文件和模式的结果(还会添加一个时间戳,以检测文件修改)。虽然该方法行之有效,但是,不要求安装与运行 DB 引擎的解决方案将是一个更好的选择,况且该方法需要在每次分析文件时,执行 DB 查询(将产生网络与输入/输出开销)。另一种方法是将该缓存存储为常规文件。在开始时,将缓存加载至易失性内存,在执行结束时或每次分析新文件时,对其进行更新。这种方法看似好用,但是我们不得不创建两个数据模型,一个用于易失性 RAM,另一个用于二级持久存储(文件),并且需要写入代码,以便在两个模型之间反复转化。如果能够避免这种额外的编码工作,那就最好不过。

持久 Grep

设计注意事项

使用 libpmemobj 编写 PEME 感知型代码的第一步通常是设计需要持久存储的数据对象类型。根对象是需要定义的第一类对象。该对象具有强制性,用于固定 PMEM 池中创建的所有其他对象(将池视为 PMEM 设备中的文件)。我的 grep 示例使用了以下持久数据结构:


图 1.PMEM 感知型 grep 的数据结构。

通过创建从根类中挂起的模式链表来整理高速缓存数据。每当搜索新模式时,将会创建一个新的类模式对象。如果当前搜索的模式在过去被搜索过,便无需创建对象(模式字符串被存储于 patternstr)。我们从类模式中挂起被扫描文件的链表。文件包括名称(在本示例中,名称和文件系统路径相同)、修改时间(用于检查文件是否已被修改)和匹配该模式的行的矢量。我们只针对未被扫描的文件创建新的类文件对象。

首先需要注意的是特殊类p<>(面向基础类型)和 persistent_ptr<>(面向复杂类型的指针)。这些类被用于通知库在交易时注意这些内存区(发生故障时,记录与回滚对象的更改)。得益于虚拟内存的性质,persistent_ptr<>将始终适用于 PMEM 中的指针。当池被程序打开,并且映射至虚拟内存地址空间时,池的位置可能与同一程序(或者访问同一个池的其他程序)使用的先前位置不同。在 PMDK 的示例中,持久指针被实施为胖指针;它们包含一个池 ID(用于从转换表中访问当前的池虚拟地址)+偏置(在池开始时)。有关 PMDK 中指针的更多信息,请参阅libpmemobj 中类型安全的宏面向 libpmemobj 的 C++ 绑定(第 2 部分)-持久智能指针

您可能想知道为什么行的矢量(std::vector)不被声明为持久指针。原因是没有必要这样做。表示矢量和行的对象一经创建(类文件对象的构建过程中),便不会改变。因此,无需在交易过程中追踪对象。尽管如此,矢量本身也会在内部分配(与删除)对象。因此,我们不能单纯依靠来自 std::vector的默认分配器(该分配器只了解易失性内存,分配堆中的所有对象);我们需要传输 libpmemobj 提供的定制化分配器(了解 PMEM)。该分配器为 pmem::obj::allocator<line>。我们以这种方式声明矢量后,便可以按照任意正常易失性代码中的使用方式使用它。事实上,您可以以这种方式使用任何标准容器类。

代码修改

现在,我们跳至代码部分。为了避免重复,只列出新代码(完整代码可在 pmemgrep/pmemgrep.cpp 中获取)。我们从定义(新的标头、宏、命名空间、全局变量和类)着手:

...
#include <libpmemobj++/allocator.hpp>
#include <libpmemobj++/make_persistent.hpp>
#include <libpmemobj++/make_persistent_array.hpp>
#include <libpmemobj++/persistent_ptr.hpp>
#include <libpmemobj++/transaction.hpp>
...
#define POOLSIZE ((size_t) (1024 * 1024 * 256)) /* 256 MB */
...
using namespace pmem;
using namespace pmem::obj;

/* globals */
class root;
pool<root> pop;

/* persistent data structures */
struct line {
	persistent_ptr<char[]> linestr;
	p<size_t> linenum;
};

class file
{
	private:

	persistent_ptr<file> next;
	persistent_ptr<char[]> name;
	p<time_t> mtime;
	vector<line, pmem::obj::allocator<line>> lines;

	public:

	file (const char *filename)
	{
		name = make_persistent<char[]> (strlen (filename) + 1);
		strcpy (name.get (), filename);
		mtime = 0;
	}

	char * get_name (void) { return name.get (); }

	size_t get_nlines (void) { return lines.size (); /* nlines; */ }

	struct line * get_line (size_t index) { return &(lines[index]); }

	persistent_ptr<file> get_next (void) { return next; }

	void set_next (persistent_ptr<file> n) { next = n; }

	time_t	get_mtime (void) { return mtime; }

	void set_mtime (time_t mt) { mtime = mt; }

	void
	create_new_line (string linestr, size_t linenum)
	{
		transaction::exec_tx (pop, [&] {
			struct line new_line;
			/* creating new line */
			new_line.linestr
			= make_persistent<char[]> (linestr.length () + 1);
			strcpy (new_line.linestr.get (), linestr.c_str ());
			new_line.linenum = linenum;
			lines.insert (lines.cbegin (), new_line);
		});
	}

	int
	process_pattern (const char *str)
	{
		ifstream fd (name.get ());
		string line;
		string patternstr ("(.*)(");
		patternstr += string (str) + string (")(.*)");
		regex exp (patternstr);
		int ret = 0;
		transaction::exec_tx (
		pop, [&] { /* dont leave a file processed half way through */
		      if (fd.is_open ()) {
			      size_t linenum = 0;
			      while (getline (fd, line)) {
				      ++linenum;
				      if (regex_match (line, exp))
					      /* adding this line...*/
					      create_new_line (line, linenum);
			      }
		      } else {
			      cout
			      << "unable to open file " + string (name.get ())
			      << endl;
			      ret = -1;
		      }
		});
		return ret;
	}

	void remove_lines () { lines.clear (); }
};

class pattern
{
	private:

	persistent_ptr<pattern> next;
	persistent_ptr<char[]> patternstr;
	persistent_ptr<file> files;
	p<size_t> nfiles;

	public:

	pattern (const char *str)
	{
		patternstr = make_persistent<char[]> (strlen (str) + 1);
		strcpy (patternstr.get (), str);
		files = nullptr;
		nfiles = 0;
	}

	file *
	get_file (size_t index)
	{
		persistent_ptr<file> ptr = files;
		size_t i = 0;
		while (i < index && ptr != nullptr) {
			ptr = ptr->get_next ();
			i++;
		}
		return ptr.get ();
	}

	persistent_ptr<pattern> get_next (void)	{ return next; }

	void set_next (persistent_ptr<pattern> n) { next = n; }

	char * get_str (void) { return patternstr.get (); }

	file *
       find_file (const char *filename) {
		persistent_ptr<file> ptr = files;
		while (ptr != nullptr) {
			if (strcmp (filename, ptr->get_name ()) == 0)
				return ptr.get ();
			ptr = ptr->get_next ();
		}
		return nullptr;
	}

	file *
       create_new_file (const char *filename) {
		file *new_file;
		transaction::exec_tx (pop, [&] {
			/* allocating new files head */
			persistent_ptr<file> new_files
			= make_persistent<file> (filename);
			/* making the new allocation the actual head */
			new_files->set_next (files);
			files = new_files;
			nfiles = nfiles + 1;
			new_file = files.get ();
		});
		return new_file;
	}

	void
	print (void)
	{
		cout << "PATTERN = "<< patternstr.get () << endl;
		cout << "\tpattern present in "<< nfiles;
		cout << " files"<< endl;
		for (size_t i = 0; i < nfiles; i++) {
			file *f = get_file (i);
			cout << "###############"<< endl;
			cout << "FILE = "<< f->get_name () << endl;
			cout << "###############"<< endl;
			cout << "*** pattern present in "<< f->get_nlines ();
			cout << " lines ***"<< endl;
			for (size_t j = f->get_nlines (); j > 0; j--) {
				cout << f->get_line (j - 1)->linenum << ": ";
				cout
				<< string (f->get_line (j - 1)->linestr.get ());
				cout << endl;
			}
		}
	}
};

class root
{
	private:

	p<size_t> npatterns;
	persistent_ptr<pattern> patterns;

	public:

	pattern *
	get_pattern (size_t index)
	{
		persistent_ptr<pattern> ptr = patterns;
		size_t i = 0;
		while (i < index && ptr != nullptr) {
			ptr = ptr->get_next ();
			i++;
		}
		return ptr.get ();
	}

	pattern *
	find_pattern (const char *patternstr)
	{
		persistent_ptr<pattern> ptr = patterns;
		while (ptr != nullptr) {
			if (strcmp (patternstr, ptr->get_str ()) == 0)
				return ptr.get ();
			ptr = ptr->get_next ();
		}
		return nullptr;
	}

	pattern *
	create_new_pattern (const char *patternstr)
	{
		pattern *new_pattern;
		transaction::exec_tx (pop, [&] {
			/* allocating new patterns arrray */
			persistent_ptr<pattern> new_patterns
			= make_persistent<pattern> (patternstr);
			/* making the new allocation the actual head */
			new_patterns->set_next (patterns);
			patterns = new_patterns;
			npatterns = npatterns + 1;
			new_pattern = patterns.get ();
		});
		return new_pattern;
	}

	void
	print_patterns (void)
	{
		cout << npatterns << " PATTERNS PROCESSED"<< endl;
		for (size_t i = 0; i < npatterns; i++)
			cout << string (get_pattern (i)->get_str ()) << endl;
	}
}
...

此处显示的是图 1 中图表的代码示例。您也可以看到 libpmemobj 的标头、定义池大小的宏(POOLSIZE)和用于存储开源池的全局变量(pop,您可以将 pop 视作特殊的文件描述符)。请注意如何使用交易保护 root::create_new_pattern()pattern::create_new_file()file::create_new_line()中的所有数据结构修改。在 libpmemobj 的 C++ 绑定中,使用 lambda 函数便捷地实施了交易(使用 lambdas 要求您的编译器至少兼容 C++11)。如果您因为某些原因不喜欢 lambda,可以尝试另一种方法

请注意如何通过 make_persistent<>()(而非常规 malloc()或 C++“新”结构)对所有内存进行分配。

旧版 process_reg_file()的功能被迁移至 file::process_pattern()方法。新版 process_reg_file()实施了用于检查当前文件是否已经进行模式扫描的逻辑(查看文件是否处于当前模式下,并且自上次后并未被修改):

int
process_reg_file (pattern *p, const char *filename, const time_t mtime)
{
        file *f = p->find_file (filename);
        if (f != nullptr && difftime (mtime, f->get_mtime ()) == 0) /* file exists */
                return 0;
        if (f == nullptr) /* file does not exist */
                f = p->create_new_file (filename);
        else /* file exists but it has an old timestamp (modification) */
                f->remove_lines ();
        if (f->process_pattern (p->get_str ()) < 0) {
                cout << "problems processing file "<< filename << endl;
                return -1;
        }
        f->set_mtime (mtime);
        return 0;
}

对其他函数仅实施了一项更改-添加修改时间。例如,process_directory_recursive()现在返回 tuple<string, time_t>的矢量(而不单单返回vector<string>):

int
process_directory_recursive (const char *dirname,
                             vector<tuple<string, time_t>> &files)
{
        path dir_path (dirname);
        directory_iterator it (dir_path), eod;
        BOOST_FOREACH (path const &pa, make_pair (it, eod)) {
                /* full path name */
                string fpname = pa.string ();
                if (is_regular_file (pa)) {
                        files.push_back (
                        tuple<string, time_t> (fpname, last_write_time (pa)));
                } else if (is_directory (pa) && pa.filename () != "."&& pa.filename () != ".."){
                        if (process_directory_recursive (fpname.c_str (), files)
                            < 0)
                                return -1;
                }
        }
        return 0;
}

运行示例

接下来,我们使用“int”和“void”两种模式运行该代码。假设 PMEM 设备(真实设备或 使用 RAM 模拟的设备 )安装在 /mnt/mem:

$ ./pmemgrep /mnt/mem/grep.pool int pmemgrep.cpp
$ ./pmemgrep /mnt/mem/grep.pool void pmemgrep.cpp
$

如果运行没有参数的函数,我们将得到高速缓存模式:

$ ./pmemgrep /mnt/mem/grep.pool
2 PATTERNS PROCESSED
void
int

传输模式时,我们将得到实际的高速缓存结果:

$ ./pmemgrep /mnt/mem/grep.pool void
PATTERN = void
        1 file(s) scanned
###############
FILE = pmemgrep.cpp
###############
*** pattern present in 15 lines ***
80:     get_name (void)
86:     get_nlines (void)
98:     get_next (void)
103:    void
110:    get_mtime (void)
115:    void
121:    void
170:    void
207:    get_next (void)
212:    void
219:    get_str (void)
254:    void
255:    print (void)
326:    void
327:    print_patterns (void)
$
$ ./pmemgrep /mnt/mem/grep.pool int
PATTERN = int
        1 file(s) scanned
###############
FILE = pmemgrep.cpp
###############
*** pattern present in 14 lines ***
137:    int
147:            int ret = 0;
255:    print (void)
327:    print_patterns (void)
337: int
356: int
381: int
395: int
416: int
417: main (int argc, char *argv[])
436:    if (argc == 2) /* No pattern is provided.Print stored patterns and exit
438:            proot->print_patterns ();
444:            if (argc == 3) /* No input is provided.Print data and exit */
445:                    p->print ();
$

当然,我们可以继续将文件添加至现有的模式:

$ ./pmemgrep /mnt/mem/grep.pool void Makefile
$ ./pmemgrep /mnt/mem/grep.pool void
PATTERN = void
        2 file(s) scanned
###############
FILE = Makefile
###############
*** pattern present in 0 lines ***
###############
FILE = pmemgrep.cpp
###############
*** pattern present in 15 lines ***
80:     get_name (void)
86:     get_nlines (void)
98:     get_next (void)
103:    void
110:    get_mtime (void)
115:    void
121:    void
170:    void
207:    get_next (void)
212:    void
219:    get_str (void)
254:    void
255:    print (void)
326:    void
327:    print_patterns (void)

并行持久 Grep

既然我们已经讲到了这里,不添加多线程支持未免太可惜了,尤其是该支持只需添加少量的代码(完整代码可从 pmemgrep_thx/pmemgrep.cpp中获取)。

首先需要添加面向 pthread 和持久互斥体(稍后将详细介绍)的相应标头:

...
#include <libpmemobj++/mutex.hpp>
...
#include <thread>

添加了全新的全局变量,以设置程序中的线程数量,现在接收用于设置线程数量(-nt=number_of_threads)的命令行选项。如果没有明确设置 -nt,将默认使用一个线程:

int num_threads = 1;

接下来,将持久互斥体添加至模式类。使用互斥体同步文件链表的写入(在文件粒度中完成并行化):

class pattern
{
        private:

        persistent_ptr<pattern> next;
        persistent_ptr<char[]> patternstr;
        persistent_ptr<file> files;
        p<size_t> nfiles;
        pmem::obj::mutex pmutex;
        ...

您可能想知道为什么需要互斥体的 pmem::obj版本(为什么不使用 C++ 标准版)。这是因为互斥体存储于 PMEM 中,并且 libpmemobj 需要能在崩溃时重置它。如果不能得到正确恢复,损坏的互斥体将创建一个永久的死锁;您可以参阅使用 libpmemobj 进行同步一文,以了解更多信息。 

虽然将互斥体存储于 PMEM 对关联互斥体和特定的持久数据对象有所帮助,但并不是在所有情况下都有此强制性要求。事实上,在本示例中,易失性内存中的单个标准互斥体变量已足够(因为所有线程一次只能处理一个模式)。我使用持久互斥体是为了显示它的存在。

一旦拥有了互斥体,无论持久与否,我们可以将互斥体传输至 transaction::exec_tx()(最后一个参数),以同步 pattern::create_new_file()中的写入:

transaction::exec_tx (pop,
                             [&] { /* LOCKED TRANSACTION */
                                   /* allocating new files head */
                                   persistent_ptr<file> new_files
                                   = make_persistent<file> (filename);
                                   /* making the new allocation the
                                    * actual head */
                                   new_files->set_next (files);
                                   files = new_files;
                                   nfiles = nfiles + 1;
                                   new_file = files.get ();
                             },
                             pmutex); /* END LOCKED TRANSACTION */

最后一步是调整 process_directory(),以创建与连接线程。已面向线程逻辑创建了一个新函数 process_directory_thread()—(该函数根据线程 ID 拆分任务):

void
process_directory_thread (int id, pattern *p,
                          const vector<tuple<string, time_t>> &files)
{
        size_t files_len = files.size ();
        size_t start = id * (files_len / num_threads);
        size_t end = start + (files_len / num_threads);
        if (id == num_threads - 1)
                end = files_len;
        for (size_t i = start; i < end; i++)
                process_reg_file (p, get<0> (files[i]).c_str (),
                                  get<1> (files[i]));
}

int
process_directory (pattern *p, const char *dirname)
{
        vector<tuple<string, time_t>> files;
        if (process_directory_recursive (dirname, files) < 0)
                return -1;
        /* start threads to split the work */
        thread threads[num_threads];
        for (int i = 0; i < num_threads; i++)
                threads[i] = thread (process_directory_thread, i, p, files);
        /* join threads */
        for (int i = 0; i < num_threads; i++)
                threads[i].join ();
        return 0;
}

总结

本文展示了如何转换简单的 C++ 程序,选取了著名的 UNIX 命令行实用程序的简化版本 grep 作为示例,以利用 PMEM。本文提供了详细的代码,首先描述了易失性版 grep 程序的作用。

然后,使用 libpmemobj(PMDK 中的一款核心库)的 C++ 添加了一个 PMEM 高速缓存,对程序进行了改进。最后,使用线程和 PMEM 感知型同步添加了并行处理(在文件粒度中)。

关于作者

Eduardo Berrocal 于 2017 年 7 月加入英特尔,担任云软件工程师。此前,他在伊利诺斯州芝加哥市的伊利诺理工大学(IIT)获得了计算机科学博士学位。他的博士研究方向主要为(但不限于)数据分析和面向高性能计算的容错。他曾担任过贝尔实验室(诺基亚)的暑期实习生、阿贡国家实验室的研究助理,芝加哥大学的科学程序员和 web 开发人员以及西班牙 CESVIMA 实验室的实习生。

资料来源

  1. 持久内存开发套件(PMDK),http://pmem.io/pmdk/
  2. grep 命令手册页面,https://linux.die.net/man/1/grep
  3. Boost C++ 库集合,http://www.boost.org/
  4. libpmemobj 中类型安全的宏,http://pmem.io/2015/06/11/type-safety-macros.html
  5. 面向 libpmemobj 的 C++ 绑定(第 2 部分)-持久智能指针,http://pmem.io/2016/01/12/cpp-03.html
  6. 面向 libpmemobj 的 C++ 绑定(第 6 部分)-交易,http://pmem.io/2016/05/25/cpp-07.html
  7. 如何模拟持久内存,http://pmem.io/2016/02/22/pm-emulation.html
  8. GitHub 中的示例代码链接

探索专门用于英特尔® 架构的 Unity Technologies ML-Agents*

$
0
0

摘要

本文介绍了如何在仅限 CPU 的环境中安装并运行 Unity Technologies ML-Agents*。展示了如何:

  • 不使用 CUDA* 和 cuDNN* 的情况下,在 Windows* 上训练与运行 ML-Agents Balance Balls示例。
  • 在专为英特尔® 高级矢量扩展指令集 2(英特尔® AVX2)优化的 Windows 上执行 TensorFlow* CMake 构建。
  • 不使用 CUDA 和 cuDNN 的情况下,从零创建一个简单的 Amazon Web Services*(AWS)Ubuntu* Amazon Machine Image* 环境,构建并在 AWS 上训练面向 Linux* 的“无头”版 Balance Balls

简介

Unity Technologies于 2017 年 9 月发布了测试版 机器学习代理*(ML-Agents*),并借助 3D 游戏引擎提供了激动人心的 强化学习简介。根据 Unity 博客的介绍,这款开源 SDK 能够为学术研究人员、对“机器人、自动驾驶汽车和其他工业应用的训练机制”感兴趣的行业研究人员和游戏开发人员带来显著的优势。

Unity ML-Agents SDK 将 TensorFlow* 用作使用近端策略优化(PPO)算法训练代理的机器学习框架。GitHub* 下载页面提供了若干示例项目、入门示例和关于安装与使用这款 SDK 的文档。

对于某些开发人员,设置与运行 ML-Agents 环境对 CUDA* 和 cuDNN* 的深度依赖是这款 SDK 的一个缺点。事实证明,我们不仅可以只在 CPU 上探索 ML-Agents,还可以在 Windows® 10 电脑上执行 TensorFlow 自定义构建,以添加面向英特尔® 架构的优化。

本文将向您展示如何:

  • 不使用 CUDA 和 cuDNN 的情况下,在 Windows 上训练与运行 ML-Agents Balance Balls(见图 1)示例。
  • 在面向英特尔® 高级矢量扩展指令集 2(英特尔® AVX2)优化的 Windows* 上执行 TensorFlow CMake 构建。
  • 不使用 CUDA 和 cuDNN 的情况下,从零创建一个简单的 Amazon Web Services*(AWS)Ubuntu* Amazon Machine Image*(AMI)环境,构建并在 AWS 上训练面向 Linux* 的“无头”版 Balance Balls


图 1.在 Unity* 软件上运行的经过训练的 Balance Balls 模型。

 

目标受众

本文面向接触过 TensorFlow、Unity 软件、Python*、AWS 和机器学习概念的开发人员。

系统配置

在准备本文的过程中使用了以下系统配置:

Windows 工作站

  • 英特尔® 至强® 处理器 E3-1240 v5
  • Microsoft Windows 10,1709 版本

Linux 服务器(训练)

  • 英特尔® 至强® 铂金 8180 处理器 @ 2.50 GHz
  • Ubuntu 服务器 16.04 LTS

AWS 云(训练)

  • 英特尔® 至强® 处理器
  • Ubuntu 服务器 16.04 LTS AMI

在“在云中训练 ML-Agents”部分,我们使用了免费的 Ubuntu 服务器 16.04 AMI 套餐。

安装通用 Windows 组件

本部分描述了通用软件组件的安装步骤,该组件是设置与运行 ML-Agents 环境所必需的。Unity ML-Agents 文档包含了安装与设置流程,用户可以通过链接访问 CUDA 和 cuDNN 安装指导网页。如果您的系统已经配置了兼容 CUDA的图形处理单元(GPU)卡,并且您不介意付出额外的努力,这样也可以,但不对此做要求。无论采用何种方式,我们建议您在进行下一步前查看 Unity ML-Agents 文档。 

安装通用软件组件需要 3 个步骤:

  1. 从软件包(参见此处)中下载并安装 Unity 2017.1 或更高版本。
  2. GitHub下载 ML-Agents SDK。解压文件并将它们移动到选定的项目文件夹(如 C:\ml-agents)。
  3. 点击此处,下载并安装面向 Windows 的 Python 3.6 版本的 Anaconda* 分发版。

安装预构建 TensorFlow*

本部分遵循了在仅限 CPU 支持的 Windows 上安装 TensorFlow 的指南。根据 TensorFlow 网站,“该 TensorFlow 版本通常更容易安装(一般需要 5 到 10 分钟),因此,即便您配备了 NVIDIA* GPU,我们建议您首先安装该版本。”执行以下步骤,在您的 Windows 10 系统上安装预构建 TensorFlow:

  1. 在开始菜单中,单击 Anaconda Prompt图标(见图 2),以打开新的终端。


    图 2. Windows* 开始菜单。

  2. 在提示符后输入以下命令:

    > conda create -n tensorflow-cpu python=3.5
    > activate tensorflow-cpu
    > pip install --ignore-installed --upgrade tensorflow

  3. 按照 TensorFlow 文档的说明,打开 Python 并输入以下命令,以确保正确执行安装:

    > python
    >>> import tensorflow as tf
    >>> hello = tf.constant('Hello')
    >>> sess = tf.Session()
    >>> print (sess.run(hello))

  4. 如果所有步骤都正确,“Hello”应在终端打印,如图 3 所示。


    图 3.Python* 测试输出。

    您可能会注意到类似图 3 所示的一条消息,即“您的 CPU 支持指令:该 TensorFlow 二进制未经过编译,无法使用 AVX AVX2(“Your CPU supports instructions that this TensorFlow binary was not compiled to use:AVX AVX2)。”该消息可能会因系统中英特尔® 处理器的不同而有所差异;它表示如果您从源代码构建 TensorFlow,它可以更快速地在电脑上运行,我们将在下一部分展开讨论。

  5. 收到提示后按下 CTRL+Z,以关闭 Python。
     
  6. 导航至之前下载的 ML-Agents 存储库的 python 子目录,然后运行以下命令,以安装所需的其他关联组件:

    > pip install.

  7. 请参阅《Balance Ball 示例入门》教程的构建 Unity 环境章节,以完成对 ML-Agents 教程的学习。

从源代码安装 TensorFlow

本部分描述了如何在 Windows 10 系统上构建一个优化版 TensorFlow。

TensorFlow 网站指出“我们不会正式支持在 Windows 上构建 TensorFlow;但是,如果您不介意在 Windows 或 TensorFlow CMake 构建中使用试验性很强的 Bazel,可以尝试在 Windows 上构建 TensorFlow。”但是,不要因为这则声明而气馁。在本部分,我们提供了关于如何在 Windows 系统上执行 CMake 构建的说明。

以下 TensorFlow 构建指南是对 GitHub上显示的分步 Windows 构建说明的补充。建议您在继续操作之前,查看 GitHub 文档,以全面了解构建过程。 

  1. 安装 Microsoft Visual Studio* 2015。请务必查看图 4 显示的编程选项。


    图 4. Visual Studio* 编程选项。

  2. 此处下载与安装 Git。接受面向安装的所有默认设置。
     
  3. 此处下载与提取 swigwin。将文件夹改为 C:\swigwin-3.0.12(请注意,您系统上的版本号可能有所不同)。
     
  4. 此处下载与安装 CMake 版本 3.6。在安装过程中,请务必检查 面向所有用户将 CMake 添加至系统路径(Add CMake to the system path for all users)选项。  
  5. 在开始菜单中,单击 Anaconda Prompt图标(见图 2),以打开新的终端。在提示符后输入以下命令:

    > conda create -n tensorflow-custom36 python=3.6
    > activate tensorflow-custom36

  6. 运行以下命令,以设置环境:

    > "C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat"

    (注:如果未发现 vcvarsall.bat,尝试此处提供的以下指令。)
     
  7. 复制 TensorFlow 存储库并为您的构建创建一个工作目录:

    cd /
    > git clone https://github.com/tensorflow/tensorflow.git
    > cd tensorflow\tensorflow\contrib\cmake
    > mkdir build
    > cd build

  8. 输入下列命令(注:请务必检查系统中的下列路径和库版本,因为它们可能会有所不同):

    > cmake ..-A x64 -DCMAKE_BUILD_TYPE=Release ^
    -DSWIG_EXECUTABLE=C:\swigwin-3.0.12/swig.exe ^
    -DPYTHON_EXECUTABLE=C:/Users/%USERNAME%/Anaconda3/python.exe ^
    -DPYTHON_LIBRARIES=C:/Users/%USERNAME%/Anaconda3/libs/python36.lib ^
    -Dtensorflow_WIN_CPU_SIMD_OPTIONS=/arch:AVX2

  9. 构建 pip 程序包,它将被创建为 .\tf_python\dist 目录中的 .whl 文件(如 C:\tensorflow\tensorflow\contrib\cmake\build\tf_python\dist\tensorflow-1.4.0-cp36-cp36m-win_amd64.whl)。

    > C:\Windows\Microsoft.NET\Framework64\v4.0.30319\MSBuild /p:Configuration=Release tf_python_build_pip_package.vcxproj

    (注:请务必检查系统中的 MSBuild 路径,因为它们可能会有所不同。)
     
  10. 输入以下命令,安装新创建的 TensorFlow 构建:

    pip install C:\tensorflow\tensorflow\contrib\cmake\build\tf_python\dist\tensorflow-1.4.0-cp36-cp36m-win_amd64.whl

  11. 按照 TensorFlow 文档的说明,打开 Python 并输入以下命令,以确保正确执行安装:

    > python
    >>> import tensorflow as tf
    >>> hello = tf.constant('Hello')
    >>> sess = tf.Session()
    >>> print (sess.run(hello))

  12. 如果所有步骤都正确,“Hello”应在终端打印。我们不会看到上文中出现的任何构建优化警告(见图 5)。


    图 5. Python* 测试输出。

  13. 收到提示后按下 CTRL+Z,以关闭 Python。
     
  14. 导航至之前下载的 ML-Agents 存储库的 python 子目录,然后运行以下命令,以安装所需的其他关联组件:

    > pip install .

  15. 请参阅《Balance Ball 示例入门》教程的构建 Unity 环境章节,以完成对 ML-Agents 教程的学习。

在云中训练 ML-Agents

ML-Agents 文档提供了名为《在 Amazon–Web Service 上进行训练》的指南,包括用于在 AWS 上设置 EC2 实例(以训练 ML-Agents)的说明。虽然该指南声明“您需要一个包含最新版 Nvidia* 驱动程序、CUDA8 和 cuDNN 的 EC2 实例”,但是基于云的训练可以采取更简单的方式,同时不产生 GPU 开销。

在本部分,我们执行以下步骤:

  • 创建一个 Ubuntu 服务器 16.04 AMI(免费套餐)。
  • 在 Windows 上安装先决组件应用,以便与云服务器进行交互。
  • 在 AMI 上安装 Python 和 TensorFlow。
  • 在 Windows 上构建无头 Linux 版 Balance Balls应用。
  • 导出 PPO.ipynb Jupyter 笔记本* 中的 Python 代码,使其在 Linux 环境中作为独立脚本运行。
  • python目录从 Windows 复制至 Linux AMI。
  • 在 AWS 上运行面向 ML-Agents Balance Balls应用的训练。
  1. 如果您没有账户,需要在 AWS 上创建一个账户。您可以按照本部分展示的步骤创建一个 AWS 免费套餐账户;但是,我们不会具体介绍如何创建账户与配置 AMI,因为该网站包含详细的操作信息。
  2. 创建一个 Ubuntu 服务器 16.04 AMI。图 6 显示了我们准备本文所使用的机器实例。


    图 6. Linux* 服务器 16.04 LTS Amazon Machine Image*。

  3. 在您的 Windows 工作站上安装 PuTTY* 和 WinSCP*。AWS 网站提供了关于如何安装这些组件、如何使用 PuTTY 从 Windows 连接至 Linux 实例以及如何使用 WinSCP 将文件传输至 Linux 实例的详细说明和链接。
     
  4. 使用 PuTTY 登录 Linux 服务器 AMI,然后输入以下命令,以安装 Python 和 TensorFlow:

    > sudo apt-get update
    > sudo apt-get install python3-pip python3-dev
    > pip3 install tensorflow
    > pip3 install image

    注:以下步骤假设您已完成 ML-Agents《Balance Ball 示例入门》教程的学习。如果您未完成该教程,在进行下一步之前,请务必输入完整的指令,并验证您能否在本地 Windows 工作站上成功训练与运行模型。
     
  5. 确保您的 Unity 软件安装包括 Linux 构建支持。您需要在安装时明确指定该选项,或者您可以通过运行 Unity Download Assistant 将它添加至现有的安装(如图 7 所示)。


    图 7.Unity* 软件 Linux* 构建支持。

  6. 在 Unity 软件中,打开 File – Build Settings并选择以下选项:
    • 目标平台:Linux
    • 架构:x86_64
    • 无头模式:已检查
  7. 设置如图 8 所示。


    图 8.面向无头 Linux 操作的 Unity* 软件构建设置。

  8. 点击 Build后,为应用创建一个独特的名称,并将其保存至存储库的 python文件夹内(见图 9)。在我们的示例中,我们将它命名为 Ball3DHeadless.x86_64,并在文章的后续部分沿用该名称。


    图 9.构建 Linux* 应用。

  9. 为了在 Linux AMI 上运行完整的训练流程,我们将导出 PPO.ipynb Jupyter笔记本中的 Python 代码,使其在 Linux 环境中作为独立脚本运行。为此,执行以下步骤:

    - 在开始菜单中,单击 Anaconda Prompt图标(图 2),以打开新的终端。
    - 导航至 python文件夹,然后在命令行输入 Jupyter笔记本。
    - 打开 PPO.ipynb笔记本,然后单击 File – Download As – Python (.py)。将在 Windows 电脑的 Downloads 文件夹内保存一个名为“ppo.py”的新文件。
    - 将文件名改为“ppo-test.py”,然后将它复制到 ML-Agents 存储库中的 python文件夹内。
    -在文本编辑器中打开 ppo-test.py,然后将 env_name变量改为“Ball3DHeadless”:
    - env_name = “Ball3DHeadless” # Name of the training environment file.
    - Save ppo-test.py, and then continue to the next step.

  10. 面向 Linux 环境构建了应用,并生成了测试脚本后,使用 WinSCP 将 python文件夹从 ML-Agents 存储库复制到 Ubuntu AMI。(关于如何使用 WinSCP 将文件传输至 Linux 实例的详细信息,请参阅 AWS 网站。)
  11. 在 PuTTY 控制台,导航至 python文件夹并运行以下命令:

    > cd python
    > chmod +x Ball3DHeadless.x86_64
    > python3 ppo-test.py
    If everything went well you should see the training session start up as shown in Figure 10.


    图 10.在 Amazon Web Services* Linux* 实例上运行的训练过程。

总结

根据图 10 显示的输出,我们需要注意每次保存模型后,时间(精确到秒)被输出至控制台。在本文中,代码被添加至 ppo-test.py脚本,以粗略测量模型保存之间的训练时间。

为了插入代码,我们对 Python 脚本进行了以下修改:

import numpy as np
import os
import tensorflow as tf
import time # New Code
.
.
.
trainer = Trainer(ppo_model, sess, info, is_continuous, use_observations, use_states)
timer_start = time.clock() # New Code
.
.
.
Save_model(sess, model_path=model_path, steps=steps, saver=saver)
print(“ %s seconds “ % (time.clock() – timer_start)) # New Code
timer_start = time.clock() # New Code
.
.
.

通过使用非正式的性能指标,我们发现预构建 TensorFlow GPU 二进制和 Windows 工作站上仅限 CPU 的预构建二进制在平均训练时间方面的差异十分微小。相比 Windows 工作站上仅限 CPU 的预构建二进制,仅限 CPU 的自定义 TensorFlow 构建的训练速度领先了约 19%。在云内执行训练时,相比 Windows 上的自定义 TensorFlow 构建,AWS Ubuntu Server AMI 的执行速度提升了约 29%。

增强用户体验:Krita 应用充分利用英特尔? 架构平台的多个内核

$
0
0

作者:Fredrick Odhiambo 和 Dmitry Kazakov

Krita是一款免费、专业的开源绘画程序,开发这款程序的美术师希望所有人都能使用物美价廉的美术工具。

要点综述

“现在我可以用大两倍甚至三倍的画笔来绘图!实际上就相当于性能提高了大约 5-10 倍。”Krita* 美术师 Rad (Igor Wect) 说。

计算机处理需求不断增长,而且更复杂、更密集。软件应用要求使用更多的计算资源,从而出现软件性能瓶颈和不可靠的软件服务,用户体验差,最终导致用户完全拒绝使用应用。多核指封装在单个集成电路板上的单颗物理处理器包含两个以上的处理单元来同时处理多个任务,从而增强性能并降低能耗。最新英特尔酷睿? 博锐? 处理器英特尔酷睿? 处理器家族、英特尔酷睿? m 处理器家族和英特尔? 至强? 处理器家族支持的英特尔? 超线程技术(英特尔? HT 技术)支持单颗微处理器像两颗单独的处理器一样运行操作系统和应用程序。本白皮书将重点介绍 Krita 应用如何结合多核和英特尔 HT 技术来提升应用性能和整体 Krita 用户体验。Krita 软件开发人员使用 VTune? Amplifier 分析了用户之前认为需要提升性能的 Krita 用例。VTune Amplifier 支持快速识别并发性。通过多线程化和线程同步技巧可大体上解决这些问题。他们构建了进阶版 Krita,并重新测试了上述用例以提升性能。收集的结果显示,不同的系统配置可将性能提升高达 2 倍。系统配置信息可参考下文。

软件开发人员价值定位

多核和英特尔 HT 技术支持软件开发人员编写能够充分利用这些技术的软件应用,并提供卓越的用户体验,从而在让最终用户接受这些应用时拥有极大的竞争优势。此外,通过这些应用,软件开发人员现在能够:

  • 在运行多个要求苛严的应用程序的同时保持系统响应能力。
  • 在确保系统处于保护中、高效和易于管理的同时把对工作效率的影响降至最低。
  • 为未来业务增长和新的解决方案能力提供充足的扩展空间。
  • 降低应用能耗,因为相比单个内核,多个内核的运行频率较低,可大幅降低散热。
  • 增加可同时处理的交易数量。
  • 充分利用现有的 32 位应用技术,同时为未来的 64 位应用技术做好准备。
  • 多媒体应用可以在运行后台功能的同时创建、渲染、编辑和编码图形密集型文件,丝毫不影响应用的整体性能。

Krita* 案例研究

方法

Krita 在 https://bugs.kde.org/上使用 Bugzilla 收集关于应用使用方面的抱怨和问题。在线论坛上,美术师和其他用户报告就用户体验而言,使用 Krita 时所遇到的挑战和难题。其中笔刷速度慢最突出,Krita 向问题作者提出其他问题,比如系统设置支持更准确地定义和界定问题。他们从提出的多个问题中优先确定了两种用例:笔刷和动画渲染。

使用 VTune Amplifier 对受影响用例的并发性和热点进行分析,以深入了解性能问题。然后通过重新编写代码、更改代码库、融入英特尔? 高级矢量扩展指令集(英特尔? AVX)以生成融合乘加指令,以及尽量实现用例的多线程话,解决了这些问题。因此开发出了 Krita 版本 4.0.0,充分利用多核和英特尔 HT 技术显著提高应用性能。

Krita 情况

使用 VTune Amplifier 分析 Krita 应用性能。检查深度并发性和基础热点分析,确定两种重要用例的性能问题(刷涂和动画渲染)。确定了以下问题。

渲染用例

几乎每一个线程都按顺序渲染每一帧。经过 VTune Amplifier 初步分析(Concurrency 和 Waits 基础评测),找到了两个有关互斥体使用的问题:

问题 1:方块 (tile) 散列表。Krita 中的所有图像数据都以 256 × 256 像素方块的形式呈现,这些方块保存在散列表中。每个像素层都有一个散列表,包含所有专用于该层的方块,而且每个散列表都有自己的读写锁 (QReadWriteLock) 加以保护。根据 VTune Amplifier 分析结果,长达 40% 的时间都用于等待这些读写锁。

问题 2:Krita 应用内部调度程序也存在瓶颈。调度程序中的任务表示为在 QThreadPool 池上运行的 Qrunnable 对象。基于 Qrunnable 的任务太小,线程极有可能在一个任务完成之后下一任务开始之前进入睡眠状态,因此将任务合成几堆。现在,每个基于 Qrunnable 的任务都可以从调度程序 pull额外任务,从而避免进入睡眠状态。

这两个问题的解决大大改进了 Krita 的渲染引擎,并展现了接近线性的提速效果,多达 6 个物理内核。增加内核数量后,互斥体开始重新出现。为确保 Krita 在增加 CPU 内核数后顺利扩展,有两个选择 — 以无锁的方式重新编写散列表或使用更高级别的多线程方法。他们选择采用第二种方法。

解决方法

在散列表中通过双相锁定 (KisTileHashTableTraits<T>::getTileLazy()) 解决了方块散列表问题。第一种尝试是提取不带任何锁的方块 (KisTileHashTableTraits<T>::getTileMinefieldWalk()),并且只在流程失败的时候提取锁并重新运行流程。这种方法可确保线程安全,原因有两点:散列表中使用线程安全功能指示器(以便在最坏的情况下仅读取过期数据),高级别调度程序确保两个线程永远不尝试读写像素相同的图像(这意味着方块不被删除,同时其他线程仍然可访问)。这种双相锁定大大减少了锁争用现象。

保存动画视频剪辑时,所有帧都需要渲染并保存至单独的文件。从技术上来说,每帧代表同一张图像的不同版本,但包含自己的像素数据。因此无法同时在同一张图像上渲染不同的帧。但可以生成图像副本。如果每个副本传递至自己的线程,渲染过程将完全独立!

实施了制作图像浅副本的可能性。创建浅副本时,大部分像素数据可以在两张图像之间共享。除了用于渲染的临时“投影”外,不需要额外内存。一位专业动画师在大型动画文件上进行了测试,全加载时占用大约 7.2 GB RAM。生成该图像的浅副本仅占用 200MB 额外 RAM。常规用户的文件所需的额外内存更少。即使浅副本不会占用过多额外内存,仍然其创建需要耗费大量时间。考虑折衷:不为每个内核创建专用副本,仅创建少量副本并将每个副本分配给多个内核。例如,如果现在有 6+6 个内核,那么可创建 4 个浅副本并将每个副本分配给 5 个线程。这样可通过生成 12 个线程使 CPU 利用率高达 100% 并独立运行。

注意:进行这种内存/效率折衷时,应特别注意用户所拥有的内存量。经过对真实用户的测试,确定即使用户可以在设置中调整“克隆”数量,但用户不愿意考虑这点。因此实施了自动克隆限制。克隆图像之前,计算投影即将占用的实际内存量,并对比用户允许 Krita 使用的内存量。

刷涂用例

刷涂从本质上来说属于顺序算法。刷笔本身是一系列小圆图(称为 dab),以极小的偏差沿着笔画一个一个涂。但实际情况更为复杂,因为部分 dab 之间存在依赖性:两个 dab 可以使用相同的 base dab,但运用不同的后期处理。

解决方法

这种问题的解决方法包含两个部分:

  1. dab本身通过主渲染线程以并行、异步的形式准备。这里巧妙的地方在于 — 当 dab 调度至并行处理时,可能已经重新排序且部分新的dab旧的完成之前做好准备。实施特殊队列 (KisDabRenderingQueue) 后解决了这些问题,将 dab收集在序列堆中,并将它们进一步传递至管线。
  2. dab渲染过程每隔 20-100 毫秒暂停一次,然后所有准备好的 dab 渲染在画布上。这种渲染过程可以并行实施:工作区分为多个分配给不同线程的小区域。

通过这些渲染内核优化,现在可以确保笔刷顺利扩展至多达 6 个物理内核。

响应最快的笔刷 (Basic_tip_default) 出现了一个有趣的现象,它利用英特尔 AVX 技术进行矢量化计算。所有优化完成后,性能显著提升几乎达到了内存吞吐量的顶点(根据 VTune Amplifier,在 5k 画布上用 1000 px 笔刷涂画)。

Krita 未来优化目标

  • 可以确定的是,采用英特尔 AVX 技术进行优化后,渲染速度比未优化的提高了 4-6 倍。笔刷优化的下一步是实施其他笔刷的矢量化版本。
  • 充分利用英特尔? 高级矢量扩展指令集 2(英特尔? AVX2)中的特定整数指令进行合成,还可将渲染速度提高 2 倍。
  • 实施无锁方块散列表还可为使用超过 6 个物理内核的系统提供部分优势。

结果

实施上述解决方案之后,他们收集了基准性能评测结果,评测的两种系统分别采用以下系统配置:

系统 1:

处理器:英特尔? 酷睿? i7-8550U @ 1.80GHz,四核

物理内存:8GB DDR4 2400MHz

操作系统:Windows 10? Pro(64 位)

系统 2:

处理器:英特尔? 酷睿? i7-8550U @ 3.2GHz,六核

物理内存:32GB DDR4 2400MHz

操作系统:Windows 10? Pro(64 位)

用例 1:动画渲染

以下数据显示了不同内核生成帧(动画渲染准备)的时间(毫秒)。增加设备的内核数可以实现这一目标。帧生成时长决定着使用 Krita 渲染动画的时间。

注:用于测试的动画文件来源于 Wolthera Van Hovell(顶级 Krita 美术师),https://yadi.sk/d/n9a6YRrP3Lc3Ts。 

图 1测试动画截图

图 2系统 2 的动画渲染结果

注:上图中的内核指物理内核 + 逻辑内核(例如 12 个内核= 6 个物理内核 + 6 个逻辑内核)

用例 2:笔刷

关于可靠性,在上述两种系统不同数量的内核上开发和执行自动化脚本,脚本记录了笔刷速度。下表显示了使用直径为 1000px 的高斯笔刷渲染 16 个 5000-px 长笔画所用的时间。随着内核数量的增强,渲染时间逐步降低(笔刷速度提高)。

图 3系统 2 的高斯笔刷速度结果

注:上图中的内核指物理内核 + 逻辑内核(例如 12 个内核= 6 个物理内核 + 6 个逻辑内核)

图 4系统 1 的高斯笔刷速度结果

注:上图中的内核指物理内核 + 逻辑内核(例如 8 个内核= 4 个物理内核 + 4 个逻辑内核)

结论和建议

通过上述两种用例我们可以得知,多核和英特尔 HT 技术可显著提高处理器密集型计算的性能。相比在一个内核上渲染动画,使用 6Core 系统(启用英特尔 HT 技术)中的全部内核渲染动画,可将性能提升 4.37 倍。这种可衡量的性能显著提升意味着可大大缩短 Krita 应用用户的等待时间,进而提高总体效率。

英特尔提供技术和硬件平台满足软件应用提出的越来越多的计算机处理需求。通过编写多线程化软件,独立软件开发商可充分利用这些功能为应用用户提供无与伦比的用户体验。英特尔为软件开发商提供的各种工具可用来确定软件应用性能瓶颈、热点和并发性问题。例如本案例研究中所使用的英特尔 VTune Amplifier

加快 3D 资产管道

$
0
0

Image of 3D Asset Pipeline

随着计算机硬件技术持续发展以及相应软件的配套,我们已经进入了(甚至以入门级水平)轻松创建优秀数字媒体的时代。然而,好的设计不仅仅在于外观。对于用户以及后续负责创建和设置功能的人员来说,它还必须能够正常运行。必须有固定的创意管道支持资产创建,才能节省时间、减少烦恼和开发交互性更强、更好玩的资产。

3D 资产创建流程包含四个部分:预先规划和研究、第一通道、实施和测试以及最终通道。前期工作可能看似多余,但可以识别和解决创建过程早期的问题,节省时间。

下面我们详细了解可增强创意过程的这四个步骤。

付诸行动之前了解实际需求

Novice 数字艺术家经常犯一个错误,不花时间充分了解具体目标,而直接进入项目选择和开始创建的阶段。问题是如果创建的是游戏资产或商用资产,必须考虑的最有可能是一定水平的功能性和交互性。一开始花时间了解手中资产的预期功能和作用,可以将其分解成多个组件,然后将组件合成一个整体。否则可能面临将已经完成的资产分解然后重新组装,或者更糟糕的重新开始的风险。

下面几点提示可帮助你了解正式开始创建之前需要做些什么:

  • 首先进行大量的参考和研究。显然,许多数字艺术家并没有花足够的时间查找可以对其设计产生重要影响的参考。我经常要求客户如果可以的话提供参考,以便我们更好地了解对方的想法。我还为自己留出了足够的时间来寻找和学习参考,以对主题有更好的了解。不管您是否相信,Pinterest * 非常适用于查找参考并根据不同项目创建参考主板。
  • 概念:数量高于质量。通常大家会持反对意见 — 质量高于数量 — 的确如此,但在概念阶段,必须有多个创意供你选择最好的一个,而不是仅仅处理一个还算不错的创意。确保手头上有参考可用。比较好的做法是,花 25 分钟至一个小时,每个概念至少 5 分钟,但不要超过 10 分钟。在这个过程中,一定要逐步缩小范围并专注于整体形状,不要过分注重细节,细节部分可以在检查完前几轮概念之后进行。

Different versions of the main 3D figure

观看概念视频

  • 好的模型表删除了许多猜测工作。标准模型表包括计划资产的正面、侧面和顶部视图,可作为指导建模过程的指南。为了充分发挥模型表的作用,预先规划所有功能部件,看它们在所有视图中运行时的机械性和美观性。这样你可以同时查看各个功能部件,更好地了解资产所需的部件数量。如果出现错误,你可以在建模过程之前解决问题,从而节省时间,减少挫败感。

Graphics of the main 3D figure

花足够多的时间预先规划可帮助打造出更加成熟产品,为自己和队友节省时间。

现在,我们了解了自己的目标以及可作为指南的模型表,下面我们来构建模型。

第一通道建立第一印象

第一通道类似于概念阶段,在这个阶段我们重点关注主要的形状和功能。如前所述,创建 3D 资产过程中,使其外观良好比较简单,因此在这一阶段,我们想以确保资产的交互性为中心并进行相应的拓展。再次提醒,在这一阶段保持简单可为我们留有较多的回旋余地,以便在测试完成后解决相应的问题。之后我们可以轻松地增加细节,但围绕这些细节工作可能会出现问题,让人沮丧。

以下是关于加快建模过程以及优化的一些提示:

  • 建模之前设置场景比例。公布项目选择后,就可以开始创建,但我们必须确保项目范围与规格相符。尽管事后也可以扩大或缩小比例,但如果不需要这一步骤,事情会简单更多。
  • 并非所有资产都必须从单个网格开始。相比想方设法扩建单个网格,将网格组合起来并清理松散的端部会简单得多。对于功能部件来说这点尤其重要,因为如果有一个单独的网格可以在不影响其他网格的情况下自行更改,处理起来更加简单。
  • 注意边缘流和密度。光滑的网格的确吸引人,但在这个阶段,多边形密度较大可以增加我们必须维持的细节数量,有时甚至是最小的变化。现在保持简单,因为我们可以在得到满意的整体形状之后增加额外细分部分。

3D figure set to Merge Center

  • 关于对称部分,将焦点放在一侧,然后镜像相应的轴。这种方法可以确保网格保持对称,节省大量的检查和整理时间。预计在开发网格时会多次采用这种方法,因为这样可以更好地了解整个对象。如果镜像时接缝处出现间隙,可以将 Merge To设为 Center连接各个顶点,也可以选择接缝上的所有顶点,将枢纽点移到中心起点,然后朝彼此缩放。
  • 如果复制网格,首选 UV。UV 解包属于时间密集型任务,因此执行一次并延伸至其他时需要花费额外的时间。尽管可以通过复制粘贴 VU 来复制网格,但偶尔会出现不好的结果,需要额外的时间来进行修复。

当网格出现大体形状后,建议用简单的颜色设置一些基础材质来模拟细节。如果边缘流管理得足够好,我们应该有大致的基础纹理层区域以供我们根据表面来运用。这样做节省了很多时间,因为我们不致力于完善 UV 解包的艰巨任务,解包工作将在润色阶段进行。现场测试时,我们还可以更好地控制以材质为基础的颜色更新。

Colored base materials to mock up the details

现在我们需要做的就是导出资产,在引擎中进行测试,以确保它拥有预期的外观和功能。不过,首先我们需要进行一些重要的准备,以避免多次重新导出和导入。

务必在导出之前解决以下问题:

  • 层次结构是否简明,网格是否正确命名?功能部件各不相同的资产,很可能需要处理多个网格。花点时间打造简明的层次结构,可帮助理解哪些网格交互或独立,而正确命名可避免混淆层次结构中的各个部分。

Example of properly named meshes

  • 交互点的起点已相应设置还是已锁定?并非每个创建的网格起始点都为0,0,0。处理多个网格并根据层次结构移动它们时尤其如此。因此,我们希望确保将枢纽点设在重要位置,并在我们希望的位置冻结转换。这样可以在场景中轻松操作资产的任何部分。

Steps to set Freeze Transformations

  • 材质设置和管理是否妥当?尽量避免使用默认材质和着色器,因为会通过不同位置的重复材质加重项目负担,需要在编辑器中分配材质时会造成混淆。处理可能遗漏并留作默认材质的表面时,建议前往 Hypershade 菜单,按住右击键选择所有包含该材质的表面。如果没有,我们就离开这一菜单。如果有,选中它们,并将它们分配给相应的对象。

观看导出设置视频

资产设置和准备就绪后,我们可以在不做任何大的改动情况下将其导出,以及重新导出。使用 Maya* 时,建议使用 Game Exporter,因为很容易理解和调整它的导出工具设置。还可以将 exporter 设为 Export Selection,以便更好地控制导出的内容,避免出现杂散的网格、材质、摄像头等。导出资产后,现在我们可以进行现场测试,看它的运行和外观如何。

Steps to export selection

考验你的勇气,看看结果如何

在进入建模和制作注重功能(而非次要细节)的粗略模型版本之前,我们做了所有的准备工作确保有固定的设计,下面我们来看看结果如何。首先将导出的模型添加至相应文件夹中的项目。元数据文件将自动创建,包含我们分配的材质的文件夹也是如此。由于我们为了节省时间选择制作多种材质(而非一种 UV 布局/材质),没有过多注重细节,所以我们有了最终版本的资产之后需要清空文件夹。

 

现在我们将资产拖放至场景中进行检查和测试,以确保它的功能和外观符合我们的预期。

下面是我们希望看到的结果:

  • 与场景中的其他资产是否协调?这是最明显的一点,如果比例失调,资产会最突出和鲜艳。如果比例失调,千万不要缩放场景内的资产。这样不仅对基础资产没有任何作用,之后还会导致出现令人头疼的问题,因为每次把资产放入场景中时都必须手动缩放资产。要想在引擎内调整缩放比例,我们想通过 Scale Factor下方资产的 Import Settings进行。不过,基于资产制作最终通道然后重新导出时,建议注意缩放差异并进行调整,因为缩放系数的更新都会保存在元数据文件中,重新导入时会更改,而不保留我们对缩放所做的更改。
  • 网格区域是否未按预期读取?即使在建模之前有一流的模型表来处理奇怪的细节,有时一旦网格在场景中,为了在其他资产之间进行缩放,某些区域和特性可能无法像在环境中那样读取。我们不在 Scene View中检查资产,而是通过 Game View查看资产,以更好地了解它的外观,避免成为过分关键,但其实在使用过程中根本不会注意到的区域。我们的目标就是实现整体可读性。如果资产没有达到预期,我们需要了解其中的原因并想办法纠正以支持最终通道。
  • 枢纽点是否在相应的位置并已归零?导出之前,我们花点时间确保枢纽点在相应的位置并已锁定,因此现在我们希望确保其相应地转入引擎之中。思考可能出错的地方之前,检查确保 Tool Handle模式已设为 Pivot,而非 Center。双击资产层级结构中的各个网格时,我们还想确认转换是否同样已归零。这样如果有部件意外移至其他地方,任何人都可以归零以便该部件回到原有位置。

检查这种次要细节可以确保首次运行时一切正常,并加快进程以完成其他任务。也就是说,我们不能首次尝试时就期待完美,因此必须首先制作粗略的版本,然后在进展过程中进行改进。

在理想的情况下,我们考虑了所有的注意事项,做好充足的准备,以确保高质量,在检查之后进行最少的必要修改,这样的话就能启动最终通道,打造美观、炫酷的最终产品。

润色和包装

这是我们为之努力的时刻。首先,需要解决所有现场检查时所发现的缺点,充分考虑我们在第一通道中所使用的技巧。修改基础形状后,可以开始整理网格并添加我们在一开始想添加的装饰细节。

以下是整理和润色网格时需要注意的地方:

  • 有选择性地添加细分部分。磨平网格的时候,最好不要在整张网格上只点击 Smooth Tool,因为这样会使多边形的数量增加一倍。看起来的确比较美观和平滑,但在长期运行过程中会产生性能成本。相反我更愿意有选择性地为某些区域添加细分部分。首先从较大的区域开始磨平边缘,看看是否奏效。如果没有效果,选择需要磨平的区域面,然后使用磨平工具,因为这样可以更好地控制多边形数量。
  • 斜硬边可以使网格看起来不会过于尖锐。这与为表面添加细分部分相似,不过添加的是边。如果使边缘柔和时效果并不是很理想(通常为不到 135 度的表面的边),这样会使它们看起来更柔和。而且焙烤环境遮挡时效果也很好。磨平表面时,我们想有选择性地选择哪些为斜边,以避免过多地增加多边形数量。

Example of beveled hard edges

  • 注意边缘流和密度大型平面上不需要有多个环边。只要不破坏边缘流,可以尽可能降低多边形密度。这样还可以使 UV 解包变得更加简单。

Example of minimized poly density

  • 不要害怕三角形。人们对技能各不相同的 3D 建模程序普遍存在误解,即网格主要包含的必须是四边形。尽管我们不想要高于四边形的图形,但完全不必担心三角形。事实上,大多数游戏引擎(比如 Unity3D* 和 Unreal*)都会将四边形转化为三角形以进行优化。

Example of mesh made with triangles

  • 不定期删除变形记录。整理网格次数越多,保存的变形记录越多,这样会造成属性编辑器拥堵,还会可能影响其他变形工具的使用。使用变形工具(比如 bevel 和 smooth)获得满意的效果后,我们可以选择网格然后前往 Edit>Delete All by Type>History或按下 Alt+Shft+D键,删除变形记录。这样可以清空属性编辑器,防止后续其他变形工具无法正常运行。

Steps to delete deformer history

尽管我们一般希望资产中的多边形数量较少,但创建虚拟现实 (VR) 资产时这种情况是可遇不可求的。记住这点,因为用户可以在 VR 环境中近距离接触许多资产,硬边会令人生畏,因此要求多边形数量稍多一些。

对网格进行整理和润色之后,现在可以进入 3D 建模过程中稍微繁琐的部分:UV 解包。下面是关于如何充分利用 UV 的部分提示:

  • 从平面映射开始。自动 UV 解包似乎不错,对简单网格来说可行,但处理复杂网格时会将 UV 切成过小的组件,你必须将它们重新粘起来。另一方面,平面映射将 UV 投射于可将网格剪影成顶部、侧面或正面视图的平面上,并制作一个简单 shell,以便分解成所选择的的组件。我发现,进行平面映射时,最好选择表面区域最大的平面。

Steps to set planar mapping

  • 将 shell 切割成可管理的部分。平面映射完成后,通过选择边缘和使用 Cut Uv工具,创建接缝以将 shell 分解成更小的部分。这样可以简化管理部分的过程,而不必试图展开较大的 shell 进行细微调整。事后可以将 shell 缝合起来以减少接缝,节省时间,减少麻烦。
  • 利用 UV 图像和纹理工具避免产生混淆。UV 有时会造成混淆,因为 shell 可能看起来正常但之后会翻转,产生不好的结果。为了确保你知道 UV 所面对的方向,可启用 Shade Uvs工具(蓝色 = 正常,红色 = 翻转)。另外一个可以启用的工具是 Texture Borders切换。它可以明确 UV 编辑器中以及主场景中的网格上 UV shell 的边缘,以便轻松查看 UV 接缝的位置。

Example of Shade UVs tool usage

Example of Texture Borders tool enabled

  • 后续需要增加细节的区域应该有较大的 UV 空间。尽管让所有 UV 彼此缩放的效果比较好,但那经常是我们后续想增加更多细节的区域。利用较多的 UV 空间来拥有要求增加细节的区域,可以帮助我们确保这些部分的整洁。
  • 将 UV 解包视作拼拼图,让过程看起来更像挑战,而不是繁琐任务。

UV 布局完成并尽可能减少材质后,可以(通过与粗略阶段相同的准备指南)将网格导出至 Substance Painter*。

Substance Painter 非常好用,因为它提供与许多 3D 建模项目类似的 3D 导航工具、支持数字艺术项目的图层和笔刷定制化,并能在 UV 布局或网格上绘图。建议从所选材质的填充层开始,重新创建粗略阶段的基础材质。我们可以使用图层蒙版,快速添加或删除按照 UV 或 UV shell 所选的材质。包含高度设置的自定义笔刷可以添加比如泥土、划痕、纤维等可以焙烤成法线贴图的细节,因此通过少量简单笔画就可让资产更加生动。

导出纹理和材质之前,需要进行一些准备工作,以充分发挥 Substance Painter 的特性和功能:

  • 提高或降低分辨率。Substance Painter 的其中一个优势是,能够提高或降低分辨率并在不影响细节的情况下返回。并非所有资产都需要处于高分辨率。如果资产能够在分辨率较低时读取,几乎或完全没有噪点、画面停格或失真,这时可以更改分辨率。借助 Substance Painter,可以经常返回调整分辨率,不会减少细节原来的数量。如果资产用于 VR 环境,最好提高分辨率,确保所有细节清晰生动。

Steps to increase resolution

  • 焙烤贴图。将它们焙烤成法线贴图可以充分利用所有高度细节,而且环境遮挡贴图可添加模糊阴影,为资产提供一点额外的记号以增加它们的可读性。焙烤贴图时,我通常将它们的分辨率从材质和纹理贴图降一级,因为这样更模糊一些。

Steps to bake maps

  • 根据分配以供使用的引擎选择导出设置。Substance Painter 的另外一个特性是基于各种可用引擎的导出预设。这样可以确保在引擎中将贴图添加至资产时不会受到任何奇怪的影响。

Options to choose export settings

观看在绘图工具中导出的视频

我们成功了!我们花时间对资产进行最充分的规划、在考虑功能的情况下粗略确定主要形状、在引擎中测试资产,并在最终通道中为资产添加细节并进行整理。现在我们可以充满信心的交出作品,我们的资产不仅外观优美,而且经过设置可以高效运行同时易于理解,方便管道下游的工作人员提高资产的交互性和可玩性。经过节省时间和避免挫折,在接下来的项目中我们可以保持高水平的创造性。

Final image of the 3D figure

Viewing all 583 articles
Browse latest View live


<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>