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

原生x86支持的Unity*游戏提升JumpStart的School of Dragons*(驯龙学院)的性能

$
0
0

作者:Cristiano Ferriera 和 Joseph Wells

图 1Dreamworks 的 ”School of Dragons(驯龙学院)* “现可原生支持 X86

Unity* 与英特尔® 开展了紧密合作,并通过安卓* x86 优化为移动设备游戏玩家带来更多丰厚奖励。Jumpstart* 等游戏公司希望在尽可能在更多的平台上轻松发布软件,同时实现显著的性能提升。Unity 的 4.6 版本可提供原生 x86 支持,从而实现这些目标。Jumpstart 将全新 Unity 4.5.4f1 版本应用于游戏 ”School of Dragons (驯龙学院)”后,通过启用原生 x86 支持,成功将帧速率提升了 146%,并将 CPU 占用率降低了 87.6%1。

JumpStart “School of Dragons(驯龙学院)”中非原生与原生 x86 支持的横向对比

相比之前的非原生支持,Unity 软件的原生安卓 x86 支持提供了这些卓越的潜在优势:

  • 加载时间大幅缩减
  • 帧速率 (FPS) 性能提升
  • 能耗降低 

基准

在 Unity 4.6 版本之前,安卓系统中所有基于 Unity 的游戏均以非原生 x86 模式运行。如下图 2 非原生栏所示,JumpStart 的 “School of Dragons (驯龙学院)”在帧速率和 CPU 占用率方面均表现欠佳。

启用 x86 支持

Jumpstart 的首席开发人员 Thomas Su 很快开始使用 Unity 发布的限量版 alpha  4.5.4f1 版本。启用原生 x86 支持后,“School of Dragons(驯龙学院)”的 FPS、加载速度和能效均实现了显著提升,如图 2 所示。

借助全新版本的 Unity 4 和 5 产品,现可自动内置 x86 支持。除了游戏代码之外,Jumpstart 还拥有多种主要插件,例如 Facebook* SDK、Supersonic* 以及需包含的 Prime31* 选项。  Su 了解完整构建的简易性:“这对大家来说非常简单。我们只需用包含 x86 支持 Unity 的新版 Unity 打开并重新构建安卓项目即可.”

Jumpstart 开发团队非常高兴得出了下列结果:“性能非常出色。基于原生支持构建的 FPS 提高了一倍。在测试中,从 图 2 可知,从人物选择界面到游戏播放的测量时间反映,“School of Dragons(驯龙学院)”的加载时间缩短了几秒钟。

图 2.Jumpstart “School of Dragons(驯龙学院)* ”的 FPS 和加载时间均提升了超过 20%。

总结

Jumpstart 通过启用原生 x86 支持显著提升了性能。Unity 软件的最新 4.6 版本可帮助安卓开发人员在基于英特尔处理器的移动设备上同样实现卓越的成效。

相关内容

在 Unity 上启用 x86 原生支持文章链接

在 Unity 上启用 x86 原生支持视频链接

 

Cristiano Ferreira 是英特尔开发人员关系部门的软件工程师,专门负责移动游戏和图形 ;Cristiano 帮助游戏开发人员为客户提供英特尔硬件所能实现的最佳体验。

Joseph Wells 拥有北亚利桑那大学技术写作专业的硕士学位,以及计算机科学专业的理学学士学位。他将此作为毕生的技术职业,并凭借其敏锐的洞察力和出色的指导,为其他人提供支持并进一步激发他们的兴趣。 


[1] 性能测试中使用的软件和工作负载可能仅在英特尔微处理器上针对性能进行了优化。SYSmark* 和 MobileMark* 等性能测试均使用特定的计算机系统、组件、软件、操作和功能进行测量。上述任何要素的变动都有可能导致测试结果的变化。请参考其它信息及性能测试(包括结合其它产品使用时的运行性能)以对目标产品进行全面评估。

本文测试所采用的系统配置:英特尔参考设计平板电脑、Android 4.4 和 Unity 4.5.4f1。英特尔® 凌动™ 处理器 Z3775(2M 高速缓存、4 核、高达 2.39 GHz,以及英特尔® 高清显卡(基础频率为 311mhz),2 GB 内存,16GB 固态盘。如欲了解更多信息,请访问 http://www.intel.com/performance


64位安卓* 和安卓运行库

$
0
0

简介

移动市场最新的关注点都集中在安卓 64 位系统。2013 年 9 月,Apple 发布了采用板载 64 位 A7 处理器的 iPhone* 5,进而掀起了新一轮移动技术竞争。

结果表明,基于安卓的内核 GNU/Linux* 在长时间内为带有 64 位寄存器的处理器提供了出色的支持。Ubuntu 是 "GNU/Linux",而安卓是 "Dalvik/Linux"。Dalvik 是 Google's安卓操作系统中的程序虚拟机 (VM),专门执行为安卓系统编写的应用。Dalvik 因此成为了安卓软件堆栈不可或缺的一部分,该系统常见于手机和平板电脑等移动设备,最近还在智能电视和可穿戴设备领域有着广泛的应用。然而,所有使用 NDK 的开发人员都不得不根据最新架构重新构建程序,其难易程度取决于 Google 提供的工具。此外,Google 应提供向后兼容性,例如,NDK 32 位应用应能够在安卓 64 位中运行。

首款面向移动设备的英特尔 64 位处理器于 2013 年第 3季度创建,并随即成为移动和台式设备中功能强大的的全新多核系统芯片 (SoC)。全新 SoC 系列包括面向平板电脑和 2 合 1 设备的英特尔® 凌动TM处理器、英特尔® 赛扬® 处理器,以及面向 2 合 1 设备、笔记本电脑、台式机和一体机的英特尔® 奔腾®处理器。

2014 年 10 月,Google 发布了 64 位安卓 L(面向开发人员)的预览仿真器映像。它可支持开发人员在发布操作系统之前测试程序并重新编写代码(如有必要)。开发人员在 Google+ blog>Google+博客 中表示,完全使用 Java* 创建的程序无需移植。他们按照 L- 版仿真器原来的样子运行这些程序,以支持 64 位架构。而使用其他语言(特别是 C 和 C++)的开发人员则必须执行几个步骤,以按照新的安卓 NDK 创建程序。目前市场可提供几种采用 64 位处理器并且基于安卓系统设备的老旧版本。但制造商必须快速更新这些设备,否则将无法为用户提供充足的软件应用。

安卓 64 位 L 仿真器

2014 年 6 月,Google 宣布安卓将在即将发布的 L 版本中支持 64 位。对于希望发挥设备和应用最佳性能的用户来说,这绝对是一个利好消息。Google 在本次更新过程中所重点强调的优势包括支持更多寄存器、增加可寻址内存空间,并支持全新指令集。

安卓仿真器可支持移动设备中的诸多硬件特性,包括:

  • ARM* v5 CPU 和相应的内存管理单元 (MMU)
  • 16 位 LCD 显示器
  • 一个或多个键盘(基于 Qwerty 的键盘和相关 Dpad/Phone 按钮)
  • 具备输入输出功能的声卡芯片
  • 闪存分区(通过开发环境中的磁盘映射文件仿真)
  • GSM 调制解调器,包括仿真的 SIM 卡
  • 摄像头,使用连接开发计算机的网络摄像头
  • 加速计等传感器,使用连接 USB 的安卓设备所传输的数据

对于构建钟爱的设备和应用来说,这是我们所取得的巨大进展。但遗憾的是,在享用这些全新的性能提升优势之前,我们必须等待Android L 的终止。Android L 发布几周后,原生开发套件 (NDK) 版本 10 应公布其对三种 64 位架构(arm64-v8a、x86_64 和 mips64)的支持,从而能够运行新版安卓系统。如果您已使用 Java 构建应用,您的代码将基于全新 x86 64 位架构自动实现性能提升。Google 已将 NDK 更新至版本 10b,并添加了仿真器映像,开发人员可使用该插件在内置了英特尔 64 位芯片的设备上运行其应用。

请记住,NDK 仅用于原生应用,而无法支持在常规安卓 SDK 上使用 Java 构建的应用。如果您希望应用基于 64 位运行,或者需要更新至最新版本的 NDK,请访问开发人员门户网站下载。

x86_64 安卓 NDK 助力开发

原生开发套件 (NDK) 是一套工具集,可支持您使用诸如 C 和 C++ 等原生代码语言实施部分应用。对于某些应用来说,这可能会有所帮助,因为您可以重复利用通过这些语言编写的现有代码库,但大多数应用无需使用安卓 NDK。使用 NDK 时,您需要平衡其优势与劣势。值得注意的是,使用安卓的原生代码通常不仅无法实现显著的性能提升,而且还会提高应用的复杂性。请在应用确实需要的时候使用 NDK,而不要仅仅出于使用 C/C++ 编程的目的。

您可通过以下网址下载最新版安卓 NDK:https://developer.android.com/tools/sdk/ndk/index.html

在本章节中,我将简单回顾如何使用安卓 NDK 编译示例应用。

我们将使用位于安卓 NDK 示例目录的示例应用 san-angeles

$ANDROID_NDK/samples/san-angeles

原生代码位于 jni/目录:

$ANDROID_NDK/samples/san-angeles/jni

针对指定 CPU 架构编译原生代码。安卓应用可能在一个 apk 文件中包含面向多个架构的库。

您需要在 jni/目录中创建Application.mk文件来设定目标架构。下列行可编译面向所有支持架构的原生库:

APP_ABI := all

通常,最佳的方法是指定目标架构列表。下行命令可编译面向 x86 和 ARM 架构的库:

APP_ABI := x86 armeabi armeabi-v7a

由于我们要构建 64 位应用,因此我们需要编译面向 x86_64 架构的库:

APP_ABI := x86_64

运行示例目录中的以下命令来构建库:

cd $ANDROID_NDK/samples/san-angeles

完成构建后,打开 Eclipse* 中作为安卓应用的示例,并点击“运行”。选择仿真器或连接的安卓设备,以在这些设备中运行应用。

为支持所有可用设备,您需要编译面向所有架构的应用。如果带有库(面向所有架构)的 apk 文件太大,请考虑使用 Google Play 多个 APK 支持中的指令,为各平台准备单独的 apk 文件。

查看支持的架构

您可以使用该命令查看 apk 文件中所包含的架构:

aapt dump badging file.apk

下面的命令列出了所有架构:

native-code:'armeabi', 'armeabi-v7a', 'x86', 'x86_64'

还有一种方法是打开压缩的 apk 文件并查看 lib/目录中的子目录。

64 位程序优化

降低应用消耗的内存

当以 64 位模式编译程序时,其消耗的内存高于 32 位版本。人们通常忽视这种较高的内存消耗,但 64 位应用的内存消耗量有时是 32 位应用的三倍。内存消耗量由下列因素决定:

  • 指示器等部分对象,要求更大的内存
  • 数据对齐和数据结构填充
  • 增加的堆栈内存消耗量

相比于 32 位系统,64 位系统通常为用户应用提供更多的内存。因此,如果在 2GB 内存的 32 位系统中,程序的内存消耗量为 300MB,而在 8GB 内存的 64 位系统中,该程序则需消耗 400MB,那么在相关单元中,该程序在 64 位系统中所消耗的内存可降低 3 倍。它的劣势在于性能损失。尽管 64 位程序的速度更快,但从内存中提取更多数据会抵消所有优势,甚至会降低性能。内存和微处理器(高速缓存)之间的数据传输也代价高昂。

可降低内存消耗量的另一种方法是优化数据结构。还有一种方法是使用内存节约型数据类型。例如,如果我们需要保存大量整数,并知道其数值不会超过 UINT_MAX,我们可以使用 "unsigned"类型替换 "size t"类型(将在下一章节中讨论)。

使用地址运算中的 memsize-type

使用地址运算中的 ptrdiff_tsize_t类型不仅能够额外提升性能,还可确保代码安全。例如,将大小不同于指示器容量的 int类型用作指数,可增加二进制代码的数据转换命令。我们可能有 64 位代码,且指示器的大小为 64 位,但 int类型的大小保持相同 - 32 位。

我们无法通过简单的例子来说明size_t优于 unsigned。为公平起见,我们使用编译器的优化功能。但优化后代码的两个变量通常大不相同,以致于无法有效证明这种巨大的差异性。我们在尝试 6 次之后终于成功创建了简单示例。但示例还很不理想,因为它仅表示编译器能够使用 size_t构建更高效的代码,而不是包含了上述不必要数据类别转换的代码。思考一下这个代码,它能够以倒序排列数组项:

<code1.txt>
unsigned arraySize;
...

for (unsigned i = 0; i < arraySize / 2; i++)
{
  float value = array[i];
  array[i] = array[arraySize - i - 1];
  array[arraySize - i - 1] = value;
}

示例中的变量 "arraySize"和 "i"含有 unsigned类型。您可以轻松地用 size_t替换它,并如表 1 所示对比一小段汇编代码。

表 1 - 使用 unsigned 类型和 size_t类型对比 64 位汇编代码片段

array [arraySize - I - 1] = value;

arraySize, i : unsigned

arraySize, i : size_t

mov eax, DWORD PTR arraySize$[rsp]

sub eax, r11d

sub r11d, 1

add eax, -1

movss DWORD PTR [rbp + rax*4], xmm0

mov rax, QWORD PTR arraySize$[rsp]

sub rax, r11

add r11, 1

 

movss DWORD PTR [rdi + rax*4 - 4], xmm0

使用 64 位寄存器时,编译器成功构建了更加简洁的代码。我们不想说使用 unsigned类型(栏 1)创建的代码比使用 size_t类型(栏 2)创建的代码慢。难以比较现代处理器的代码执行速度。但您可从本例中看出来,使用 64 位类型时编译器构建的代码更简短、更快。

现在让我们来看看这些示例,它们的ptrdiff_tsize_t类型在性能提升方面具备优势。为便于展示,我们选取一种计算最小路径长度的简单算法。

函数 FindMinPath32由带有 unsigned类型的经典 32 位方式编写而成。函数 FindMinPath64与之唯一的不同之处是其中所有的 unsigned类型均被 size_t 类型替换掉。除此之外没有其他不同之处。现在我们来对比两种函数的执行速度(表 2)。

表 2 - 函数 FindMinPath32 和 FindMinPath64 的执行时间对比

 型号和函数函数执行时间
132 位编译模式。函数 FindMinPath321
232 位编译模式。函数 FindMinPath641.002
364 位编译模式。函数 FindMinPath320.93
464 位编译模式。函数 FindMinPath640.85

如表 2 所示,基于 32 位系统的函数 FindMinPath32缩短了与执行速度相关的时间。该表格的目的在于清楚地进行对比。第一行中

FindMinPath32函数的运行时间为 1(基于 32 位系统)。这是我们的基准测量单位。

第二行显示 FindMinPath64函数的运行时间也为 1(基于 32 位系统)。因为 32 位系统上的 unsigned类型与 size_t类型一致,所以 FindMinPath32FindMinPath64函数的运行时间也相同。细微误差 (1.002) 仅仅表明测量过程中出现了细小错误。

我们在第三行中发现性能提升了 7%。我们希望是重新编译面向 64 位系统的代码实现了性能提升。

第四行是我们最感兴趣的地方。性能提升了 15%。仅用 size_t类型替换 unsigned类型后,编译器所构建的代码便在效率上得到了显著提升 - 8%!

这清楚地表明,与机器语言大小不同的数据会降低运算性能。只需用ptrdiff_tsize_t 类型替换 intunsigned类型,便可显著提升性能。该结果可首先应用于这些情形,即在索引数组和地址运算中使用这些数据类型,以及安排循环。

注:PVS-Studio 是一种面向 C、C++ 和 C++11 的商用静态程序分析工具。尽管不是针对优化程序而设计,但这种工具可帮助您进行代码重构,从而提高代码效率。例如,当 >memsize-types 修复与地址运算相关的潜在错误时,您可以使用 memsize-type,支持编译器构建更加优化的代码。

内在函数

内在函数是依赖于系统的特别函数,可执行无法以 C/C++ 级别的代码所执行的行为,也可更高效地执行这些函数。事实上,它可帮助您消除不需要或通常无法使用的行内汇编代码。

由于缺乏开销费用调用通用函数,编程人员可使用内在函数创建速度更快的代码。当然,这种代码会更大一些。MSDN列出了能够用内在版本替换的函数。它们包括 memcpystrcmp等。

除了能够自动用内在版本替换常用函数,您还可以在代码中明确使用内在函数。这将会取得显著优势,原因包括:

  • 64 位模式下的 Visual C++ 编译器不支持行内汇编器,但支持内在函数。
  • 内在函数的用法更加简单,因为不需要了解寄存器和其他类似的低级别结构。
  • 内在函数在编译器内更新,而汇编代码必须手动更新。
  • 内置优化程序不兼容汇编代码。
  • 内在函数比汇编代码更易于移植。

在自动模式下使用内在函数(得益于编译器开关的帮助)可助您提升性能,且使用“手动”开关还可获得更大的性能提升。因此,使用内在函数是一种非常有用的方法。

对齐

数据结构对齐是在计算机内存中安排和访问数据的一种方法。它涉及两个相互独立,相互关联的问题:数据对齐数据结构填充。当从内存地址读取数据或将数据写入内存地址时,现代计算机需在语言大小 (e.g., 4-byte chunks on 32-bit systems) 或更大的数据块中执行数据结构对齐。数据对齐指将数据放入相当于该语言大小几倍的内存偏移量,由于 CPU处理内存的方式,这样可提升系统的性能。为了使数据对齐,可能需要在上一个数据结构的结尾和下一个数据结构的起点之间插入一些无任何意义的字节,即数据结构填充

例如,当计算机的语言大小是 4 个字节(在大部分计算机上是 8 位,但可能因系统不同而不同)时,待读取的数据应在乘以 4 的内存偏移量中。如果不是这种情况,例如,数据从第 14 个字节而非 16 个字节开始,计算机则必须读取 2 个 4 字节的数据块,并在请求数据读取之前执行计算,或生成 对齐错误.尽管前一个数据结构在第 13 个字节处结束,但下一个数据结构应在第 16 个字节处开始。因此,需在这两个数据结构之间插入两个填充字节,以便下一数据结构与第 16 个字节对齐。

尽管所有现代计算机都面临数据结构对齐这一基本问题,但许多计算机语言和计算机语言实施功能均可自动进行数据对齐

最好在一些案例中帮助编译器通过手动定义对齐来增强性能。例如,流指令扩展 (SSE) 数据必须在 16 字节边界上对齐。您可以通过以下方法实现对齐:


// 16-byte aligned data
__declspec(align(16)) double init_val[2] = {3.14, 3.14};
// SSE2 movapd instruction
_m128d vector_var = __mm_load_pd(init_val);


安卓运行时

安卓运行时 (ART) 应用由 Google 开发,用以替代 Dalvik。该运行时具备多项全新特性,可显著提升安卓平台和应用的性能与流畅性。ART 以 Android 4.4 KitKat 形式推出;在 Android 5.0 中,它可以全面取代 Dalvik。与 Dalvik 不同,ART 使用即时 (JIT) 编译器(运行时),这意味着 ART 可在安装期间编译应用。因此,程序可提高执行速度,电池续航时间也由此得以改善。

在向后兼容性方面,ART 与 Dalvik 使用相同的字节代码。

除了能够加快速度外,ART 还可提供另一种重要优势。由于 ART 直接运行应用机器代码(本地执行),其 CPU 占用率比在 Dalvik 上编译即时代码时低得多。CPU 占用率越低,电池消耗量越小,这是便携式设备通常具备的另一大优势。

既然如此,那为何不更早实施 ART?让我们来看看预 (AOT) 编译的劣势。首先,生成机器代码所需的空间比现有字节代码大。第二,代码预编译与安装同时进行,因此安装流程耗费的时间更长。最后,执行时所占的内存空间更大。这意味着只能同时运行较少的应用。首台安卓设备面市时,其内存和存储容量较小,直接导致了性能瓶颈。这就是为何 JIT 方法当时是客户首选的原因。如今,内存更加便宜,即使在低端设备上也具备更高的冗余性,因此 ART 成为自然的选择。

也许最重要的改进在于,当 ART 安装在用户设备上时,它可将您的应用编译成原生机器代码。由于编译器面向特定架构(比如 ARM、x86 或 MIPS)而构建而,具备预编译功能,因此可帮您实现显著的性能提升。这样,每次运行应用时,再也无需采用即时编译。因此,安装应用的时间更长,但如果在 Dalvik VM 运行时期间执行多个任务的同时(例如正在执行的类别和方法验证)启动应用,它可加快启动速度。

其次,ART 团队还合作优化了垃圾回收器(GC)。Dalvik 执行每次 GC 需要暂停两次,共耗费 10 毫秒,而现在只需暂停一次,通常不到 2 毫秒。他们还实现了 GC 部分运行的并行化,并优化了回收战略,使 GC 能够感知设备状态。例如,只有当手机锁定时才会运行完整的 GC,而且是否对用户做出响应已经不再重要。对于对帧速下降比较敏感的应用来说,这是一次巨大的改进。此外,未来的 ART 版本还将包含压缩回收机制,可将配置内存的数据块移到连续数据块之中,从而减少碎片,并无需牺牲旧版应用来分配大型内存区域。

最后,ART 可充分利用名为 “Rosalloc” 的全新内存分配程序(插槽运行分配程序)。大多数现代系统均使用基于 Doug Lea 设计(采用单个全局内存锁定)的分配程序。在面向对象的多线程环境中,这样会干扰垃圾回收器和其他内存操作。在 Rosalloc 环境中,Java 中常见的小型对象在不带锁定的线程局部区域中进行分配,而大型对象通常自带锁定。因此,当您的应用尝试为新对象分配内存时,无需等待垃圾回收器释放不相关内存区域。

目前,Dalvik 是安卓设备默认的运行时,而 ART 是部分安卓 4.4 设备(比如 Nexus 手机、Google Play 版设备、运行常用安卓系统的 Motorola 手机以及其他智能手机)的可选配置。ART 目前正处于开发阶段中,目前正在广泛征求开发人员和用户的反馈意见。一旦完全稳定下来,ART 将最终取代 Dalvik 运行时。到那时,如有兴趣尝试这项全新功能并体验其优越性能,使用兼容设备的用户可从 Dalvik 切换至 ART。

如需切换或启用 ART,您的设备必须运行 Android 4.4 KitKat 并能够兼容 ART。您可通过“设置” ->;“开发人员选项” ->;“运行时选项” 轻松打开 ART 运行时。(提示:如果在设置中找不到开发人员选项,您可前往“关于手机”,向下滚动,并轻击版本号 7 次,以启用开发人员选项。)手机将重启,并开始优化面向 ART 的应用,这一过程可能持续 15-20 分钟,取决于您手机安装应用的数量。您还将注意到,启用 ART 运行时后,安装应用的大小将所有增加。

注:切换至 ART 后,当您首次重启设备时,设备将再次优化所有应用,这一过程确实有些令人不悦。

由于 Dalvik 是安卓设备的默认运行时,部分应用可能无法在 ART 中运行,尽管大多数现有应用兼容 ART 并应该能够正常运行。但如果您使用 ART 时遭遇漏洞或应用崩溃,那么请最好切换回 ART 并保持在该状态。

切换至设备上的 ART 要求您知道切换选项在设备中的所处位置。Google 将其隐藏在设置选项之下。幸运的是,我们可以通过一种技巧启用基于 Android 4.4 KitKat 的设备的 ART 运行时。

免责声明:在尝试之前,您必须备份数据。如果设备因此变成砖头机(无论怎么尝试都无法打开),英特尔概不负责。尝试有风险,后果请自负!

  • 要求根
  • 如果您已安装 WSM 工具,请不要尝试,因为它们不支持 ART。

启用 ART 时,请认真遵循以下步骤:

  1. 请确保您的设备已获得根权限。
  2. 从 Play 商店安装‘ES File Explorer’。
  3. 打开 ES File Explorer,在右上角点击菜单图表,并选择工具。在工具栏中启用 ‘Root Explorer’ 选项,并在系统提示后允许所有根访问 ES explorer。
  4. 在 ES explorer 中从菜单->; Local->;设备中打开设备 (/) 目录。前往/数据/属性文件夹。以文本形式打开 persist.sys.dalvik.vm.lib 文件,然后选择 ES note editor。
  5. 通过在选择右上角的编辑选项编辑文件。在 libdvm.so to libart.so 中重新命名命令行
  6. 回到 persist.sys.dalvik.vm.lib 文件,并选择 ‘Yes’ 保存该文件。然后重启手机。
  7. 手机将立即重启并开始优化面向 ART 的应用。重启时间取决于设备安装的应用数量。

如果您希望恢复 Dalvik 运行时,只需采取上述步骤,并重新命名 persist.sys.dalvik.vm.lib file to libdvm.so 中的文本。

结论

Google 发行了面向 Android L(即将面市)的 64 位仿真器映像 - 但仅面向英特尔 x86 芯片。全新的仿真器可支持开发人员构建或优化面向即将发布的 Android L 操作系统及其全新 64 位架构的老旧应用。迁移至 64 位能够增加可寻址内存空间,并支持更多的寄存器和面向开发人员的全新指令集,但 64 位应用无法因此而实现加速。

Java 应用由于其字节代码由全新的 64 位 ART VM 解译,因此可自动获得 64 位的卓越优势。这也说明无需对纯 Java 应用进行调整。基于 Android NDK 构建的应用将需要优化,以包含 x86_64 构建目标。英特尔可建议如何将目标定位 ARM 的代码移植到 x86/x64。借助全新仿真器,开发人员将能够创建面向基于英特尔® 凌动™ 处理器的芯片的应用。

英特尔为开发人员提供了丰富的工具和全面的安卓系统支持,特别是其英特尔® 硬件加速执行管理器(英特尔® HAXM)和一系列英特尔凌动操作系统映像。安卓程序员可定期在仿真的英特尔架构上进行测试,即使大多数部署均针对 ARM 设备。此外,全新仿真器也可 64 位升级至 HAXM 加速器,因此使用 HAXM 将会变得更具吸引力。英特尔引言:

"显然,我们不仅仅致力于交付行业首款面向英特尔架构的 64 位仿真器映像和安卓 L 开发人员预览 SDK 内的 64 位英特尔 HAXM,而且致力于在诸多领域实现更多创新,例如今年早期的首款面向安卓KitKat 的 64 位内核、64 位安卓原生开发套件 (NDK),以及在过去 10 年间不断实现的其他 64 位改进功能。"

32 位移动设备迁移至 64 位的进程中,英特尔架构是否也会有所变化?

安卓 SDK 包含一个虚拟移动设备仿真器,可在您的计算机上运行。该仿真器可支持构建安卓应用的原型,并开发和测试这种应用,而无需使用物理设备。安卓仿真器可模仿常用移动设备(无法拨打电话的设备除外)的所有软硬件特性。它可提供多个导航和控制键,您可使用鼠标或键盘“按”这些键,为您的应用生成事件。它还可提供屏幕显示您的应用以及其他活跃的安卓应用。

为了支持您更加轻松地建模和测试应用,仿真器使用 Android 虚拟设备 (AVD) 配置。AVD 可支持您定义仿真手机的部分硬件方面,并创建多项配置测试多个 Android 平台和硬件排列。如果您的应用基于仿真器运行,它可使用 Android 平台的服务调用其他应用、访问网络、播放音频和视频、保存和检索数据、通知用户,并提供图形转换和主题。

相关文章与资源

  • 请点击 此处,了解更多信息并下载安卓 NDK 版本 10d。
  • 如欲了解更多关于 Android 5.0 Lollipop 的信息,请点击此处
  • 请点击此处,了解如何使用 x86 Android* 4.4 (KitKat) 仿真器开发应用

关于作者

Egor Filimonov 任职于英特尔公司软件和服务事业部。他就读于位于俄罗斯诺夫哥罗德的罗巴切夫斯基州立大学,主修力学和数学专业,擅长应用数学与信息学。他关注的主要领域包括 HPC(高性能计算)和移动技术。

使用英特尔® 内联函数在安卓*平台上优化功耗比的代码示例

$
0
0

观看视频

简介

电池续航时间,尤其是移动设备,对于用户的重要性不言而喻。我们都遇到过这样的时刻,即在最需要的时候设备突然断电 — 无论是在一座新城市中导航,还是正在接听一个重要的电话。虽然看上去不是那么明显,但是通过优化应用性能,开发人员能够降低系统功耗,进而为用户带来帮助。

借助英特尔® 图形性能分析器  + VTune™ 放大器的组合对应用进行分析

提高应用的功耗比首先要做什么?首先,您必须了解您的应用是 CPU 密集型还是 GPU 密集型。您可以使用一套英特尔® 工具组合来实现这一点:

英特尔® 图形性能分析器 or GPA是一种用于图形分析以及优化 Microsoft DirectX* 应用和 Android* OpenGL ES* 应用的工具。了解更多信息,请访问:https://software.intel.com/en-us/articles/gpa-which-version

出于优化安卓平台的目的,我倾向于使用 GPA 控制台客户端。了解相关信息,请访问:https://software.intel.com/en-us/android/articles/using-intel-graphics-performance-analyzers-console-client-for-android-application

VTune™ 放大器可帮助您分析算法选择,并发现您的应用如何从可用的硬件资源获益。使用 VTune 放大器定位或确定:

  • 应用和/或整个系统中最耗时的功能(热点)
  • 无法有效利用可用处理器时间的代码段
  • 针对连续处理性能和线程性能优化的最佳代码段
  • 影响应用性能的同步对象
  • 应用是否处理输入/输出操作,以及具体的位置和处理方式
  • 不同同步方法、不同的线程数量或不同算法对于性能的影响
  • 线程活动和转换
  • 代码中与硬件相关的瓶颈

配置主机系统(Linux*、OS X* 或 Windows*),并在远程系统(Linux 或 Android)上运行分析。仅面向系统的 VTune 放大器支持在安卓和嵌入式 Linux 系统上执行远程分析。

了解更多信息,请访问:https://software.intel.com/en-us/node/496918

下图展示了如何使用 GPA 和 VTune 放大器的组合来分析和优化应用。

什么是英特尔® 内联函数

英特尔® 内联函数是使用汇编编码的函数,支持您使用 C/C++ 函数调用和变量来取代汇编指令。内联函数提供了使用标准 C 和 C++ 语言结构而无法生成的指令。

内联函数支持内嵌扩展,进而消除了函数调用的开销。内联函数提供了与使用内嵌汇编相同的优势,有助于提高代码可靠性、帮助指令调度以及减少调试。

了解更多信息,请访问:https://software.intel.com/en-us/node/523351

如何查找面向安卓*操作系统的英特尔® C++ 编译器,并将其连接至您的项目?

随英特尔® INDE 套件提供。面向安卓*的英特尔® C++ 编译器集成了安卓 NDK,并且提供了编译 x86 库的优化方案。

下载并安装面向安卓的英特尔 C++ 编译器。安装过程中提供 NDK 目录的路径,以便将面向安卓的英特尔 C++ 编译器集成至安卓 NDK。

成功安装后,面向安卓的英特尔® C++ 编译器将自动集成至安卓 NDK 工具链,并将编译面向 x86 架构的优化库。

示例

请查看下面的 C++ 代码,了解英特尔内联函数的使用:

Float x = 1.0f / sqrtf( y );

这种代码(尤其是物理算法中)经常出现在热点之中。

通过分析 VTune 放大器中的字符串,配置文件  将会显示编译器生成 sqrt + div(而不是 rsqrt)。

使用英特尔内联函数可修复此问题:

Float x = rsqrt( y );

Where rsqrt is:

         #include

         …

         inline float rsqrt(const float x)
         {
             float r;
             _mm_store_ss(&r, _mm_rsqrt_ss( _mm_load_ss(&x)));
             return r;
         }

参考资料

如欲了解更多信息,请观看我的视频:https://videoportal.intel.com/media/0_qgvcof5s

 

关于作者

Stanislav Pavlov 任职于英特尔公司的软件 & 服务事业部。他在技术领域拥有十几年的丰富经验。他关注的主要领域包括性能优化、功耗和并行程序设计。作为英特尔的一名高级应用工程师,Stanislav 与软件开发人员和 SoC 架构师紧密协作,帮助他们在英特尔平台上实现尽可能出色的性能。Stanislav 毕业于国立研究大学高等经济学院,拥有数理经济学硕士学位。他目前正在攻读莫斯科商学院的 MBA。

英特尔 WiDi — 面向开发人员的无线显示技术

$
0
0

作者:Paul Ferrill 和 Steve Barile

下载 PDF

在看视频、玩游戏、共享图片,甚至办公(包括 Microsoft PowerPoint* 演示)时使用多个屏幕越来越普遍。 但是用户如何使用第二个屏幕取决于具体的设备和操作系统。

本文将讨论排在前三位的技术 — Apple AirPlay*、Google Chromecast* 和 Wi-Fi Alliance Miracast*(该项技术采用英特尔® 无线显示技术 (英特尔® WiDi))之间的区别,并从开发人员的角度概括介绍每项技术的优势和劣势。

随着主设备越来越小,理想的显示屏尺寸越来越大。 通过观察早期的两个用例(小型设备以无线方式在更大的屏幕上显示其内容;以及智能电视显示互联网的内容),您可以了解专有标准和开放标准是如何产生的。 我在本文中讨论的每项技术都面临着不同方面的挑战,而每项技术都以各自的操作系统、设备和独特特性赢得了市场的认可。

Wi-Fi Alliance Miracast* 和英特尔® 无线显示技术

Miracast 不是设备或软件,而是 Wi-Fi Alliance 规范下的一项技术的名称。 它不是一款应用,虽然大家可能经常听到用 Miracast 应用这个术语来介绍该功能。 简言之,Miracast 规格旨在定义如何使用 Wi-Fi Direct* (802.11) 连接通过对等网络将 MPEG4 (H.264) 视频从供电端设备发送至接电端设备。 规范描述了如何使用规定的步骤建立连接。

供电端和接电端首先都会监听 2.4 GHz 频带社交渠道 Wi-Fi Direct 第二层的信号灯。 其中一端(通常是供电端)会扫描其他设备(在本案例中是接电端)。 供电端将收集响应,并将其呈现给用户,或代表用户选择一个响应。 接下来,供电端将对指定接电端发起连接。 两端协商后,其中一端将成为组所有者,这与接入点 (AP) 相同。 建立连接时,协商内容主要包括视频帧速率、帧尺寸、加密以及音频格式。 压缩通常是在 MPEG2 包装中采用 H.264 标准。 嵌入式 MPEG 流和带外 (OOB) 控制信号将通知接电端可变数据速率以及可能会发生的中游 (midstream) 格式变化。

Miracast 非常灵活,您可以将其作为 HDMI 连接线的替代产品。 接电端仅要求使用能够感知 Wi-Fi 的 Wi-Fi 驱动程序,以及能够播放 H.264 视频流的媒体管线。 Miracast 并不要求在供电端和接电端使用 2x2 双频视频,但是通常较好的配置都会使用该特性。 一些高级配置要求 Wi-Fi 无线电同时连接 AP、“监听”社交渠道并支持 Wi-Fi Direct 连接。 比如,当一台智能电视作为接电端时,可以随时连接 Miracast,同时保持与 AP 的连接。 这种方法不需要用户将电视设置为 Miracast 监听模式。

第二个屏幕可以连接 Miracast 或使用小型适配器(支持多家制造商的产品,通常作为 HDMI 加密狗并通过 USB 供电)。 Miracast 还可用于消费者插件设备中,包括蓝牙* 播放器以及将有线电视信号传输至智能手机的机顶盒。 用户设备可以是 Windows* 7 或更高版本、Android* 或 Kindle* Fire 系统,也可使用三星 Wi-Fi AllShare Cast* 进行连接。

使用 Miracast 的主要技术区别是:

  • 无需 AP。
  • 无需互联网连接(除非用户希望在主机设备上播放互联网内容)。
  • 本机支持可在 Windows 8.1、Android 4.2 及更高版本和 Kindle Fire 上使用。

主机设备编码,并传输 MP4 视频。 注: 当使用 Windows 8.1 或 Android 4.4 的演示平面时,您需要从 Project 菜单或 KitKat 菜单设置第二个屏幕连接。

Miracast 唯一的不足在于从云向电视传输视频内容。 在本场景中,必须通过 AP 在供电端和接电端之间再连接一条 Wi-Fi 跳线,它需要供电端一直保持开启状态,以执行视频转码,如果发电源未连接至墙壁电源将会耗干电量。 您还可能在最后一个显示屏上看到视频传输至电视前进行解码并重新编码导致的视频非自然信号。

英特尔® 无线显示技术

英特尔 WiDi 自 2010 年起投入市场,于 2012 年添加了 Miracast 支持(3.5 版)。 英特尔 WiDi 包括上述所有 Miracast 特性。 将近 4000 万台笔记本电脑预装了英特尔 WiDi。 英特尔 WiDi 支持 Windows 7 和 Windows 8/8.1。

英特尔 Pro WiDi 是专门针对商务级系统推出的新特性,专门针对会议室场景而设计,在该场景中,需要将多台电脑连接至一个大型的显示器。 英特尔 Pro WiDi 可提供快速传输和安全连接功能,支持使用简单网络管理协议、命令行接口或 Web 用户接口管理投影仪或英特尔 Pro WiDi 认证显示器上的设置。

英特尔 WiDi 可提供一台无线显示器的基本平台功能。 对于想要创建创新型双屏应用的 ISV 开发人员而言,最大的优势是无需额外添加任何设备。 开发人员可以使用多种编程语言开发双屏应用。 目前有许多标准的 OS API 调用,包括检测第二个显示器连接,在第二台显示器上创建新的全屏窗口和无边窗口。

Google Cast* (API) 和 Chromecast* 加密狗

Google 生产了一款物美价廉的硬件加密狗 (Chromecast),可插入任何包含 HDMI 端口的显示器。 该加密狗可通过连接 LAN 上的无线 (802.11) 或有线 AP 从互联网上下载内容。 从内在的系统而言,Google Cast 与其他的解决方案的差别很大,真正是两种技术的结合。 其主要模式是使用 URL 重定向播放云内容。 简言之,URL 重定向定义了应用如何向 Chromecast 加密狗发送 URL,以便在电视上进行渲染。

当将 Chromecast 加密狗连接到电视上时,必须先进行配置,以便通过使用标准 Wi-Fi (802.11) 的本地 AP 获取 LAN 和互联网访问。 然后加密狗将监听基于简单服务发现协议 1.1 版(通用即插即用标准的一部分)的发现和发布服务发现的第 5 层。

应用将收集响应,并将其呈现给用户,或代表用户选择一个响应。 当应用通过 Google Cast API 调用连接时,将通过 TCP/IP 连接为指定的 Chromecast 加密狗创建一个插座。 通过 TCP 连接,Google Cast API 支持平台上的任何应用使用 Chromecast 加密狗“发送”视频。 这些 API 支持在 Chromecast 加密狗上执行多种操作。 它可以传送一个 URL 直接从互联网渲染,也可以播放和控制互联网视频内容。 它甚至可以用来下拉应用,在 Chromecast 电子狗上运行,从而提供定制的体验。

例如,一个内容提供商可以创建一个提供视频内容目录的网站。 使用 Google Cast API 插件,可以将从内容提供商网站上获取的 URL 发送至 Chromecast 加密狗进行播放。 此外,Google 提供了一个 API,支持从发送设备上远程控制(播放、暂停、快进、倒带) Chromecast 加密狗上的视频流播放器。 二级模式基于传输本地内容和屏幕镜像。 近来,Google 根据自己的 API(它具有自身独特特点,且仅针对 Chromecast)部署了复制模式。

Google Cast 非常灵活,您可以在 Chromecast 加密狗上播放视频,而加密狗不需要“发送视频”的设备采取任何操作,发送设备甚至可以关闭。 而且,它支持随时在加密狗端下载应用,这使得该解决方案更为灵活。

Google Cast 不足之处在于播放本地视频内容,因为它需要使用第二根 Wi-Fi 跳线从供电端连接至 AP 再连接至 Chromecast 加密狗。 此外,当前 Chromecast 加密狗使用的是 1x1 Wi-Fi 无线电,通常后者在拥挤的 Wi-Fi 环境中运行状况欠佳。

面向 Apple TV* 的 Apple AirPlay*

Apple TV* 和 AirPlay 与 Google Cast 拥有一些相同的特点:它们都需要专用设备并且需要从其在线商店上下载专门软件。 虽然一些大型内容提供商 (YouTube*) 支持这两种技术,但是也有一些大型提供商(Amazon 视频)不能在上面运行。

只有运行 iTunes* 的 Apple 硬件或设备能够与 Apple TV 兼容。 在音频方面,其他的制造商(包括 Denon、JBL、Marantz、Pioneer 和 Sony)许可 AirPlay 从 Apple 设备向其设备传输音频(仅限音频)。 开发人员必须使用 Apple API 来调用 AirPlay,其应用必须通过 Apple 商店许可和分发流程,虽然 Apple 开发人员网站包含了多种可用文档和 Objective-C* (很快将升级为 Swift)样本代码。 易用性与预期中的一样,但是不够开放。

比较与对照

从不同的角度来考虑这些技术之间的差别和相似性非常重要。 想象一个商业用例场景,即多台笔记本电脑需要连接到一台大屏幕设备上进行展示,您需要能够快速连接并断开每个设备。 可能 Wi-Fi 不可用,除了使用连接线之外,Miracast/英特尔 WiDi 解决方案成为唯一的可选选项。

站在开发人员的角度,您必须评估目标受众,以及所有可用应用的潜在市场。 虽然 Apple 生态系统在消费者市场上明显占有很大份额,但是还有大量运行预装 Miracast 的 Windows 系统的 PC 和预装 Miracast 的 Android 手机和平板电脑(Android 4.2 和更高版本)。

目标解决方案不同,实际的开发流程也有很大差别。 对于 Miracast/英特尔 WiDi 目标,您无需额外使用任何代码即可在第二个屏幕上显示内容:其运行如同正常的应用。 如果您以 Google Cast 为目标,则需要学习一套新的 API 调用,并需要编写能够在 Chrome* 浏览器内运行的代码。 对于 Apple 目标解决方案,您必须通过 Apple 商店,并需要与 AirPlay 集成。

屏幕共享是三种解决方案共同支持的用例。 区别在于如何建立和控制连接。

架构差异

Miracast 使用 Wi-Fi Direct 技术,可提供直接的点对点或对等连接(无需 AP)。 AirPlay 和 Google Cast 都需要使用无线 AP 从本地设备建立连接(两根跳线)

 

 

注: Google Cast 目前允许使用 OOB HTTP 会话传递至加密狗来启动和停止流,但是在传输前需要在芯片电子狗 1.2 GHz 系统上进行转换,并且需要 16 GB 的闪存和 512 MB 的内存。

 

 

多数英特尔 WiDi 设备使用 2x2 双频无线电,因此 PC 能够与两个屏幕以及一个 AP(便于使用互联网连接)时刻保持连接。

此外,Miracast 还添加了特定的协议,以便协商视频功能,在需要时配置加密,传输内容和保持视频会话。

注:压缩通常是在 MPEG2 包装中采用 H.264 标准。 数据包中包含嵌入式 MPEG 流和带外 (OOB) 控制信号,用于通知第二个屏幕可变数据速率以及可能会发生的中游 (midstream) 格式变化。

英特尔 Pro WiDi 添加了其他类似功能不具备的隐私性、安全性和可管理性功能。

兼容性

AirPlay 最适合在其他 Apple 设备上使用,在非 Apple 设备上使用时需要 Apple TV 和 iTunes。 Google Cast 可在运行 Chrome 浏览器的任何操作系统上使用,并需要使用 Chromecast 加密狗。 Windows 商用设备需要使用一个 Chrome 浏览器插件。Miracast 能够在 Windows PC 和 Android 4.2 及更高版本的设备上即开即用。 目前,可从多家厂商购买多种 Miracast 加密狗。

更多信息

作者介绍

Paul Ferrill 为计算机贸易媒体撰稿已二十余年。 他最初主要为 PC Magazine 撰写有关 LANtastic 和早期版本的 Novell Netware 等产品的网络评论。 Paul 拥有 BSEE 和 MSEE 两个学位,曾为数不清的电脑平台和架构编写过软件。

Steve Barile 在视频编解码和各种媒体基础架构等技术领域拥有超过 25 年的经验。 他目前担任双屏幕应用支持的技术推广人员;他于 2010 年初加入英特尔无线显示技术事业部。

使用Cocos2d-x 3.0或更新版本创建多平台游戏

$
0
0

下载文档

在本教程中,您将了解到如何使用 3.0 版或更高版本 Cocos2d-x 框架在 Windows* 开发环境中创建简单的游戏,以及如何实施编译以便它在 Windows 和 Android* 上运行。

Cocos2d-x 是什么?

Cocos2d-x 是一种跨平台的游戏(及互动书本等其他图形应用)框架,基于 iOS* cocos2d,但使用 C++、JavaScript* 或 Lua*,而非 Objective-C*。

该框架的一个优势在于支持创建可部署于不同平台(Android、iOS、Win32、Windows* Phone、Mac*、Linux* 等)上的游戏,有助于保持相同的代码库,只需针对每种平台进行特定的调整。

Cocos2d-x Console

Cocos2d-console 配置在 3.0 版本中。它是一种命令行工具,提供了一些管理 Cocos2d-x 或 Cocos2d-JS 项目的功能,如创建、执行、构建、调试等。

创建您的第一款游戏

1 - 下载最新版框架,并在您的开发环境中对它进行解压。本教程使用了 3.3rc0 版,且框架解压至桌面 (C:\Users\intel-user\Desktop\cocos2d-x-3.3rc0)。


图 1。Cocos2d-x 3.3 RC0 版目录结构

2 – 如欲在 cocos2d-x 中创建新项目,请使用框架文件夹中的 setup.py(一种 Python* 脚本)配置所有的环境变量,以针对 Win32 和 Android 平台进行构建。在执行 setup.py 之前,您将需要下载、安装和配置以下项:

如果您未安装 Python Runtime,请访问下列网址下载 2.7.6 版:http://www.python.org/download/


图 2。setup.py 位置

3 - 打开命令提示符 (cmd.exe) 并执行下列命令:

- 导航至脚本文件夹(框架文件夹):
cd C:\Users\intel-user\Desktop\cocos2d-x-3.3rc0

- 运行脚本 setup.py
python setup.py(或仅 setup.py)

注意:如欲运行命令提示符中的 Python 命令,请将安装有 Python 的文件夹添加至环境变量路径

- 该脚本将会请求 Android SDK、Android NDK 和 ANT 的安装路径。

  • Android NDK 文件夹路径:


图 3。Cocos2d-console 请求 NDK 文件夹路径

  • Android SDK 文件夹路径:


图 4。Cocos2d-console 请求 SDK 文件夹路径

  • Apache ANT bin 文件夹路径:


图 5。Cocos2d-console 请求 ANT bin 文件夹路径

包含请求的路径之后,重新打开命令提示符 (cmd.exe)。该操作需要使用 cocos2d-console 命令

4 – 键入 cmd.exe 访问命令提示符(cocos2d-console 命令只可通过此处发布)并再次打开框架文件夹:

cd C:\Users\intel-user\Desktop\cocos2d-x-3.3rc0

在下一步,我们将创建新的 Cocos2d-x 项目:

cocos new MyGame –p com.Project.MyGame –l cpp –d Project


图 6。已创建的 Cocos2d-x 项目

下面简要解释一下相关参数:

  • new:创建新项目,必须紧跟项目名称(如 MyGame)
  • -p:定义包名称
  • -l:选择编程语言,值可以是 cpp 或 lua
  • -d:框架将要创建项目结构的目录

如果各项操作进展顺利,您的项目将在框架被提取的目录中的项目文件夹中被创建。


图 7。MyGame 目录结构

所创建的项目包含游戏(类别))的基础代码、资源(图像和音频等)以及每个框架所支持平台的一个项目。

构建 Android* 应用

要求

  • 您需要配置所有的环境变量,以构建 Android 应用游戏(Android SDK、Android NDK 和 ANT)。若您还未完成此步骤,请参见本文中“创建您的首款游戏”部分。
  • 安装的 Java 开发套件 (JDK)

注:Cocos2d-console 使用 javac 命令针对 Android 进行构建,因为有必要添加 JAVA_HOME 环境变量 (JDK 路径)。

1 – 我们将针对一种以上的架构对我们的游戏进行编译,因为框架不会默认针对 x86 和 armeabi-v7a 进行编译。编辑 Application.mk 文件,它位于:

C:\Users\intel-user\Desktop\cocos2d-x-3.3rc0\Project\MyGame\proj.android\jni


图 8。Application.mk 位置

2 – 将下行添加至该文件:

APP_ABI := armeabi armeabi-v7a x86


图 9。添加了 APP_ABI 行之后的 Application.mk

既然已添加好我们的目标架构,那让我们编译游戏吧!

3 – 使用命令提示符,找到框架文件夹:

cd C:\Users\intel-user\Desktop\cocos2d-x-3.3rc0

4 – 执行以下命令以针对 Android 编译并运行游戏。

cocos run –s Project\MyGame –p android


图 10。执行命令以针对 Android* 编译并运行游戏

  • run:编译并运行项目
  • -s:项目文件夹的路径
  • -p:选中的平台

注:若只进行编译,请输入:cocos compile –s Project\MyGame –p android

若一切正常,该 cocos2d-console 命令将使用 adb(若配置在环境变量中)将 APK 文件安装进所连接的设备或初始化模拟器中。若它们不可用,则该命令会等待直至设备和模拟器可用,如下图所示:


图 11。命令等待设备和初始化模拟器

若您已初始化了模拟器或连接了设备,那么将会出现该屏幕:


图 12。Android* 平台上的游戏屏幕

构建 Win32 应用(针对 Windows* 7 或 Windows* 8 桌面模式)

您将需要 Visual Studio* 2012或更高版本进行构建。

1 – 使用命令提示符 (cmd.exe),找到提取了框架的文件夹:

cd C:\Users\intel-user\Desktop\cocos2d-x-3.3rc0

2 – 执行以下命令以在 Windows 上编译并运行游戏:

cocos run –s Project\MyGame –p win32


图 13。执行命令以在 Windows 上编译并运行游戏

下面简要解释一下相关参数:

  • run:编译并运行所选定的项目
  • -s:项目文件夹的路径
  • -p:选中的平台

注:如只进行编译,使用“编译”而非“运行”,如下所示:

cocos compile –s Project\MyGame –p win32

执行完运行命令,若一切正常您将会看到以下屏幕:


图 14。Windows* 平台中的游戏屏幕

可以使用 Visual Studio 来编译并运行游戏项目:

1 – 在项目目录中,用 Visual Studio 打开 MyGame.sln文件,该文件在 “proj.win32” 文件夹中。


图 15。Win32 项目目录结构

2 – 要编译项目,请按 F6 (或 Build menu -> Build Solution)并按 F5 执行它(或 Debug menu -> Start Debugging)。在构建和执行完毕后,您会于 console 步骤之后看到相同的屏幕显示。


图 16。在 Visual Studio* 中打开了 Win32 项目

现在您了解如何针对 Android(x86 和 ARM*)、Windows 7 和 Windows 8 (桌面模式)创建和编译游戏!

参考文献

Cocos2d-x 框架的源代码根据 MIT 许可证进行了授权,如欲了解相关信息请点击此处

Cocos2d-x 及其文档:http://www.cocos2d-x.org/

cocos2d-console:http://www.cocos2d-x.org/wiki/Cocos2d-console

此时,Cocos2d-x 3.3 的发行版本在项目创建时会出现问题,不支持用户创建项目(详情请点击此处)。该问题已在最新的预发布版本中得到了解决,但该解决方法还未用于 Cocos2d-x 的最新发布版本中。

Gameloft和英特尔:共同致力于为x86安卓* 平台带来高质量画面

$
0
0

下载文档

作者:Steve Hughes

简介

包括游戏玩家在内的很多人习惯性将计算设备分为台式机和移动设备,并认为台式机能够呈现令人震撼的游戏体验,而移动设备在这方面则较为平庸。他们通常都接受这些设备之间的巨大性能差距,并无怨言。然而,去年底在测试第四代英特尔® 凌动™ 处理器 (代号为 Bay Trail)时,我发现它具有出色的硬件性能。事实上,我发现可以将台式机才能呈现的一些画面效果添加至合适的移动游戏中,于是我稍花时间创建了一款展示应用,以展示该处理器的卓越功能。在经过快速考察后,我决定与 Gameloft 就其赛车游戏 GT Racing 2 (GTR2) 展开合作。我早已知道 Gameloft 团队,一直以来他们都在加倍努力,以优化性能并使自己的游戏脱颖而出。

在本文中,我将描述 Gameloft 在 GTR2 中实施的效果,并重点介绍我们如何成功将这些效果与我们设定的 30 FPS 帧速进行了完美结合。2013 年年底,我们想在 2014 年旧金山游戏开发者大会上展示我们所取得的成果,因此时间非常紧迫。

效果

用在该游戏中的逼真效果由 Gameloft 精心选出,因为 Gameloft 知道自己最想要怎样的效果。我们进行了公平合作,他们了解他们的引擎,而我们需要快速取得成效,以便有时间进行优化。图 1 和图 2 所示为处理前后的图像,清晰显示了我们通过延长 x86 设备上的 CPU 和 GPU 时间提升了图像质量。


图 1。现有 ARM* 设备上 GTR2 的一般画面效果。 模式出色,但正常灯光情况下的画面效果不尽如人意。


图 2。在基于英特尔® 凌动™ 处理器的平板电脑上,GTR2 从亮光 (bloom) 和光轴 (light shaft) 效果方面增强了视觉效果。

 

光轴

为了实现光轴效果,我们将太阳光渲染到第二个渲染目标和与太阳光位置方向相反的径向模糊通道 (radial blur pass)。在几个通道中,对低分辨率渲染目标实施该操作可得到如下类似结果:


图 3。在低分辨率渲染目标上对太阳光进行的初次渲染。 此处的太阳光被不透明的场景物体挡住,从而形成此处的形状。


图 4。其次,采用一组径向模糊通道得到右侧图像。 从最初被部分遮挡的太阳光中,我们得到了光晕,它代表了与直射阳光发生碰撞的空中粒子。

 


图 5。然后,模糊图像被添加回原帧中,所得最终效果便如此处所见。 该赛车游戏中实时应用了该效果。

亮光

亮光虽是一种相当常见的效果,但很容易出错。亮光的目的在于,模拟场景中突然出现强光的效果,即图像会达到饱和状态,并使周围的环境变亮。

分三步完成亮光:

1:过滤原始场景,以除去场景中的任何暗色像素,只留下明亮像素。该图像被写入另一个渲染目标(图 6)。


图 6。从原始图像中提取浅色像素。

2:然后,包含浅色像素的新渲染目标便会模糊起来。这样可以模拟渗入周围暗色像素的明亮像素(图 7)。


图 7。然后,浅色像素图像便会模糊起来。

3:最后,将变模糊的浅色像素渲染目标加入到原始场景中,从而产生亮光效果(图 8)。


图 8。将变模糊的浅色像素图像加入到一定缩放比例的原始场景中,从而得到最终的亮光图像。

景深

为了实现景深,我们从游戏场景开始,应用水平和垂直方向的高斯模糊通道。


图 9.原始游戏场景

在完成这两个步骤后,我们便可看到现在整幅图像都变得模糊了(图 10)。此时,我们便得到了模糊图像和原始的清晰图像,以及来自原始渲染通道的深度缓冲器。下一步是选择深度值,该值将作为我们的焦点,如汽车中心点。针对屏幕上的每个像素,我们基于当前像素深度和焦点深度值之间的差值对模糊图像和清晰图像进行了融合。深度值与焦点相差较大的像素将更加受益于模糊图像,而深度值接近焦点的像素将更加受益于清晰图像。


图 10。模糊化游戏场景的焦点副本


图 11。Bay Trail 上的景深介绍 我们在菜单和其它非游戏屏幕中保留此效果,因为在比赛时精确的远距离视觉很重要。

最终结果(图 11)非常接近您由摄像头获得的图像景深。

热霾


图 12。在起跑线上保留热霾,以便在比赛开始前为赛车提供一种逼真热感。

热霾效果用于模拟受阳光照射物体在空气中发出的微光(图 12)。通过将动画失真效果应用于原始颜色缓冲器而产生热霾效果。为将该效果限制在赛车周围区域,我们使用阿尔法通道图像屏蔽了该效果。


图 13。由摄像头视角产生的热霾屏蔽。

将该效果限制在起跑线,因为精准的远距离视觉对于赢取比赛至关重要。

开始优化

开发人员常认为游戏优化是一种收益递减的工作。他们的意思是,将游戏优化至平均帧时为 33 毫秒、帧速为 30FPS 是通行做法,但将游戏优化至 30FPS 以上就毫无意义,因为 30FPS 是游戏的预期运行帧速。然而,对于移动设备来说却并非如此。我们总共能够添加约 12 毫秒的效果,这会将帧时增加到 45 毫秒(接近 22FPS)。这意味着,我们必须对已经过优化的游戏减去 12 毫秒以达到启用所有效果所需的最终目标帧速。

开始实施任何优化流程时需了解游戏为 GPU 密集型还是 CPU 密集型。即决定需要优化 GPU 还是 CPU 代码以增加帧时。我们使用图形性能分析器 (GPA) —— 即系统分析器 —— 捕捉了下图的数据:


图 14。对于该款赛车游戏的大部分运行阶段,GPU 利用率约为 90-100%,而 CPU 利用率平均约为 25%。 很明显,该应用为 GPU 密集型,这对于赛车游戏比较合理。

制作此图非常容易。您只需将想要的指标添加到系统分析器中,然后点击“CSV”按钮,将该指标转存到一个 csv 文件中。这时您就可以将它们加载到 Excel* 或其它绘图软件中。

很多开发人员并不知道 GPA 在 x86 移动设备上运行良好。它是一套很出色的工具,值得您考虑使用。

详细了解帧

在添加这些效果之前,我们先使用系统分析器从该游戏中捕获了许多帧,然后再用帧分析器打开它们以了解我们可轻松实现哪些目标。图 15 显示了在添加我早期使用较多的效果之前,该游戏的一帧:


图 15。把帧分成两半。 一些大 GPU 事件发生于后一半。

首先且最明显的是,在该帧的第二半中对 glClear() 的两个调用 —— 在帧图中用紫色进行了突出显示。我在引擎中常常遇到的问题是,渲染目标往往会先被清除,即使它们稍后会被完全写满。去除这些目标是一种很容易的解决方法,只需大概 5 毫秒就可让我们继续工作。

两个 glClear() 调用间的大蓝条表示一个很有趣的事件。我们一直在对 Gameloft 收到的早期开发套件支持的屏幕尺寸进行实验。非常大的屏幕(2560x1900)能够更高效地渲染到较低分辨率的后台缓冲器,然后再上采样到全尺寸屏幕。所述事件就是从后台缓冲器到屏幕的上采样。这是一个大事件,需要进行仔细审查。我发现,GPU 上执行单元 (EU) 的 大部分延迟时间都由等待 erg 上的纹理取样器造成。这实际上是有道理的,因为片段着色器非常简单,而正被复制的纹理尺寸较大(超过 8Mb),所以着色器自然就会花大量时间等待它所需要的数据以完成工作。这让我认为,我们有可能渲染至全尺寸渲染目标并摆脱上采样。最终我们并未获得真正的性能提升,因为我们所节省的时间被渲染至更大目标所抵消。我们实现了一定的视效提升。

该帧中需要注意的最后一点是,贴有 A、B、C、D 标签的 4 个大 erg。您可能已发现,我的方法是设法删除所有或部分大 erg。最好的方法是首先使用图形性能分析器。在这种具有 4 个 erg 的情况下,我们可以做的非常少。该帧中有 4 辆赛车可见。这一款赛车游戏,所以我们唯有花费一定时间对赛车进行渲染。

平台分析器调查

我们了解性能信息的一个方式是使用平台分析器,它是 GPA 中较新的一款工具。借助平台分析器,您可从整体上了解 CPU / GPU,并可了解 GPU 上的队列管理情况(图 16)。当发现驱动程序存在一个严重问题时,我很震惊:


图 16。来自平台分析器。 水平标度是时间,顶部的堆叠图表示 GPU 上的队列深度。

最初,GPU 队列看上去总是满的,一切似乎都很完美。然而,仔细观察标记点,我们便可发现大约每隔 10 帧便会发生一个事件,这阻碍了整个流程并排空了队列,致使运行几乎停止,直至重新启动。我们需要花时间查找具有一定依赖性的周期性描绘调用,但很难知道要找哪些调用。

结果,问题来自英特尔显卡驱动程序。就如预发布硬件常常发生的情况一样,驱动程序仍在被使用。这是一种拖延问题,它在几个星期之前已得到修复,但我们还没有更新驱动程序,因为我们对已有驱动程序的其他方面很满意。我们不确定是否真正提升了帧速,但是我们的确通过对驱动程序的修复获得了更加流畅的帧速。

深入分析效果

此时,我们已获得了约 5 毫秒的帧时并提升了视觉质量。我们还需大约 7 毫秒,所以我们决定分析效果。我们不会忽视视觉质量,但我们认为这些全新效果可能会带来性能提升。


图 17。帧分析器使用增加的亮光和光轴效果进行捕获。 因注意到 glClears() 的消失,所以不出所料,该帧的第二半需花费更多时间,针对效果的所有后处理在第二半中进行。

观察该帧(图 17)时,我们的注意力被吸引到贴有 B 和 C 标签的 erg,它们是针对亮光效果的模糊和明亮通道。它们各自耗时 3-4 毫秒,我们认为有些偏高。调查后,Gameloft 对效果做出了一些重要更改,从而极大地提升了性能。

首先,他们发现模糊步骤在全屏纹理上实施。缩小到四分之一的屏幕尺寸会使得几乎所有的模糊效果从帧分析器的显示中消失。

其次,明亮通道渲染目标为全高清。Gameloft 发现缩减到大约二分之一的屏幕尺寸是安全的,不但不会造成视觉变化,还会进一步大幅提升性能。

优化了亮光渲染目标并达到一定的性能提升后,我们开始更加仔细地观察亮光效果。因为大家普遍认为亮光效果看上去有些减退(图 18),所以在验证了模糊和明亮通道纹理正常后,我们还需再检查下着色器。

与下列片段中所示的典型亮光着色器相比,该款亮光着色器中的运算看起来有些复杂:

[java]

lowp vec4 bloom = texture2D(blur, vCoord0) * 1.5 - threshold; gl_FragColor = bloom * bloomFactor;

[java]

我使用了 GPA 帧分析器中一个鲜为人知的特性进行实验。通过该特性,您可以修改所捕获帧中的着色器并对它们进行重新编译,以了解它们在外观、性能等方面的差异。发明一款可在该帧范围内产生简单亮光效果的着色器花费不了多长时间(您可以更改源,但不能同时更改 GPA 中的输入或常数)。

该着色器的运行速度相比原始着色器稍快一些,但更改着色器可极大地提升视觉质量。如此,一款用于亮光通道的新着色器便创建完成,它可大幅提升亮光效果。比较图 18 和 图 19,找出不同之处。


图 18。亮光效果展示了阴影的“褪色感”和左侧的岩石。


图 19。相比旧亮光,新亮光看上去几乎达到了高动态范围 (HDR)。

结论

该项目旨在进一步优化帧速已优化到 30FPS 的游戏,以为每帧节省更多时间(毫秒),以便留出时间来添加约 12 毫秒的效果。我们成功在游戏中节省了约 7 毫秒的时间,再从效果中节省出 5 毫秒的时间,这还得归功于驱动程序的修复。我们成功证明了,Bay Trail 架构设备等现代移动设备能够实施过去只有控制台和台式机 GPU 才能提供的效果。若没有 GPA 以及与 Gameloft 的良好合作关系,我们所做的一切都不能实现。

关于 Gameloft

作为世界领先的数字和社交游戏发行商,Gameloft® 自 2000 年以来已成为游戏领域的顶级创新者之一。Gameloft 为包括功能手机、智能手机、平板电脑、机顶盒和互联电视在内的所有数字平台创建游戏。Gameloft 针对旗下多款知名游戏建立了特许经营网络,如《狂野飙车 (Asphalt®)》、《世界足球 (Real Football®)》、《现代战争 (Modern Combat)》和《秩序与混沌(Order & Chaos®)》,此外,Gameloft 还与 Marvel®、Hasbro®、FOX®、Mattel® 以及 Disney® 等主要版权持有者建立了合作伙伴关系。Gameloft 目前在全球 120 多个国家和地区销售游戏产品,拥有 5,200 多名游戏开发人员。

如欲了解更多信息,请登录 http://www.gameloft.com

关于作者

Steve 担任英特尔的高级应用工程师,负责为游戏开发人员提供 PC 和移动设备 3D 图形支持和多线程解决方案方面的技术支持。Steve 在游戏行业拥有 14 年的程序设计师从业经验,参与了 11 款游戏的开发工作,经历过 2 次破产事件,但总体而言发展比较顺利。Steve 是一位游戏发烧友,喜欢创作和演奏音乐,不过不是作曲家!

英特尔® INDE:使用商用游戏引擎的游戏开发人员的工具

$
0
0

摘要

如今,游戏开发行业的日子并不好过。一方面,开发人员需要面对多种平台上产品“半衰期”不断缩短的问题;另一方面,多种操作系统版本也带来了更多挑战。甚至针对单个平台优化游戏也变得更困难,因为系统复杂性不断加剧,而且现在功耗对游戏性能的好坏发挥着关键作用。如今,Windows* 和 Android* 设备的数量有数十亿之多,这使得潜在的投资回收期也变得颇为漫长。

在本文中,我们将介绍去年发布的跨平台工具套件英特尔® Integrated Native Development Experience(英特尔® INDE)可如何帮助您便捷创建能够在 Windows* 和 Android* 设备上实现本地运行性能的一流游戏。这些工具即使对于使用 Unity* 或 Epic Unreal Engine* 等第三方游戏引擎也非常有用。英特尔 INDE 工具仍能够提供额外功能,帮助您的游戏在竞争激烈的市场中脱颖而出。

英特尔 INDE 能够帮助您创建出色的游戏,从而为玩家带来畅快淋漓的沉浸式游戏体验。

简介

游戏开发人员正在寻找实用工具,以帮助缩短游戏上市时间,使游戏更快速部署在日益增多的平台上。因此,许多开发人员开始使用商用游戏引擎,因为它们可缩短上市时间,并让开发人员快速影响许多平台上的广泛客户群。但如果您不使用游戏引擎之外的其他工具,您可能会丧失部分性能。而这部分性能原本可为您带来更快、更一致的帧速,或更逼真的地形细节或两倍数量的僵尸 (!),从而在任何目标平台上提供令人震撼的游戏体验。换言之,在竞争激烈的市场中,您需要独树一帜,率先推出一款非凡游戏才是您的成功之道。

虽然本文面向使用商用游戏引擎的开发人员,但英特尔 INDE 也可为喜欢“运行自己的”游戏引擎的独立游戏开发人员提供帮助。不过这是本系列中另一篇文章的主题(敬请关注!)

将英特尔 INDE 与 Game Engine Workflow 配合使用

使用 Unity* 游戏引擎或 Epic* (Unreal Engine*) 时,您可能会觉得它们能够满足您的所有需求,而认为英特尔 INDE 等产品没什么用处。具体而言,许多开发人员希望游戏引擎能够“包办一切”,自己只需负责创建一些游戏资产并确保每帧中的僵尸数量合适。

幸运的是,英特尔一直在与一些重要的游戏开发人员合作,确保您使用的游戏引擎经过了英特尔 INDE 产品工具的优化。显然,英特尔 C++ 编译器一直在为引擎提供平台依赖优化方面发挥着重要作用,但其他的英特尔 INDE 分析和优化工具可帮助确保整个游戏引擎为实现自上而下的峰值性能进行了优化。具体而言,英特尔与 Unity 和 Epic 的合作可确保这些游戏引擎能够带来出色性能,无论您的目标英特尔平台是什么(Windows 或 Android)。

但即使对于使用这些游戏引擎的开发人员,英特尔 INDE 团队仍确信可从几个重要方面帮助他们提升游戏运行速度,并大幅提升单独使用游戏引擎时的运行性能。这些之前在英特尔图形性能分析器(英特尔 GPA)产品系列下发布的工具,现在只配置在英特尔 INDE 内,并可帮助实施下列工作:

  • 调试游戏资产
  • 性能分析和优化
  • 功耗分析

英特尔 INDE 优化工具可帮助高效调试游戏资产。例如,浏览 Unity 用户论坛时,我们发现很多客户都谈到会把英特尔 INDE 工具用于他们的工作流程。一个常见示例是,捕获帧进行详细分析,再使用图形帧调试器(此时仅支持 Android)在场景中一次实施一个绘制调用。在每一步中,深度了解对象的所有视觉因素和属性,实时旋转对象以寻找错位的顶点,查看线框模型以检查细节层次 (LOD) 问题,检查对象的图形属性,并查看帧缓冲器和深度缓冲器。例如,若僵尸的出现并不符合预期,请检查深度缓冲器,您可能会发现该僵尸竟然是在小屋 (shack) 后而不是小屋前被渲染。

对于性能分析,需预先计划针对性能与视觉效果设置合理目标,然后在整个开发过程中跟踪目标的实现情况。在这方面,您将会发现英特尔 INDE 系统分析器、图形帧分析器和平台分析器对于您的工作流程是极为重要的分析和优化工具。本文讨论了一些用于选择不同性能选项和验证性能影响的策略。如我所述,您需要不断地测试并证明您可为您的目标平台提供最佳游戏体验。再次强调,从竞争对手中脱颖而出的唯一秘诀是提供比他们更加出色的游戏体验。为实现特定帧速,您实施了真正的优化以部署客户要求的特性和交互性吗?此外,由于英特尔与游戏引擎开发人员正在进行紧密合作,现在很多游戏引擎都提供了分析提示,即英特尔 INDE 在运行您的游戏时可收集相关信息,然后再通过平台分析器回放跟踪文件以直观性地检查 CPU 和 GPU 中的线程交互性。您的游戏是 CPU 密集型还是 GPU 密集型?

对于移动平台,了解功耗利用率也是一个关键因素。最新英特尔系统具有相同且固定的 CPU 和 GPU 功耗范围,使用功耗太多会造成 CPU 或 GPU 的速度减慢,并影响重要的游戏交互性。运行英特尔 INDE 系统分析器以了解您的功耗利用率,在某些场景中功耗是否会突然飙升?若如此,请检查您的游戏资产或其他关键参数和选项以了解问题所在。

总之,不要一切都依靠您的游戏引擎,英特尔 INDE 作为一款非常实用的工具可助您从竞争对手中脱颖而出。英特尔 INDE 工具可帮助您选择合适选项,实现最大的成本效益。

后续步骤……

欢迎购买并使用该产品。了解更多关于英特尔 INDE 的信息,请登录产品主页。在此,您可了解到各种产品“版本”之间的差异,以及如何下载该产品的免费入门版或该产品终极版本的免费试用版。

若您想于今年三月份在旧金山参加游戏开发者大会 (GDC),请一定要查阅英特尔的各种演示文稿(部分演示文稿由英特尔与顶级游戏开发人员联合制作)。此外,还请光临英特尔 INDE 展台,我们将为您重点介绍各种性能分析和优化工具的最新版本。

敬请关注本系列其它文章,以更加详细地了解英特尔 INDE 将如何帮助您快速、轻松地开发出精品游戏。具体而言,请寻找相关文章,了解英特尔 INDE 可如何帮助那些喜欢“运行自己的”游戏引擎的开发人员。英特尔 INDE 可提供更多特性和优势,帮助您快速、轻松地开发出精品游戏!

使用英特尔® 线程构建模块计算pi

$
0
0

鉴于许多 Android* 设备的处理器都具有一个以上的内核,了解如何开发多线程应用在移动行业变得十分重要。 英特尔开发了一种名为英特尔® 线程构建模块(英特尔® TBB)的重要工具,该工具可用于开发并行应用。 英特尔® TBB 是一个跨平台模板库,用于帮助用户创建并行程序。 它可以创建和同步数据流,同时隐藏架构详情,支持您在更高的抽象化水平上工作。 英特尔® TBB 可以在所有架构上运行。 对于 Android,请使用 4.3 及更高版本。

构建英特尔® TBB

  1. 下载 TBB。 您可以在此处下载英特尔® TBB: https://www.threadingbuildingblocks.org/。 我下载了最新的稳定版(4.3 Update 1)。
  2. 将 NDK 添加至路径:

    对于 Windows*:
    • $ SET PATH=%PATH%;
    对于 Linux*:
    • $ export PATH=$PATH:

  3. 解压 TBB,转至包含源代码的目录,就在 src 文件夹中。 $ cd /src/
  4. 运行面向 Android 的 TBB: $ /ndk-build –C /src/ arch=intel64 compiler=gcc target=android clean tbb tbbmalloc –j 此命令可构建面向 64 位 Android 的 TBB。 若要构建面向 32 位 Android 的 TBB,请更改使用 arch=intel64 on arch=ia32。 它可以构建面向 Android 64 位架构的应用。
  5. 库构建完成。 在 build 目录 (/build/) 中,您可以找到包含 libs 的目录: libgnustl_shared.so, libtbbmalloc_proxy.so, libtbbmalloc.so and libtbb.so. libtbb.so and libgnustl_shared。我们会在应用开发中用到这些目录。

计算 π

计算 π 时,您可以从维基百科中选择任何一个包含定积分的公式: https://en.wikipedia.org/wiki/List_of_formulae_involving_%CF%80#Integrals。 我选择了这个公式:

针对该程序,我对这个公式进行了修改:

在积分计算中,我采用了矩形法。 积分函数除以 N = 107等于子区间长度 h = 2·10-7。 然后将 N个矩形的面积(底乘以高)加起来计算近似积分,给出以下公式:

创建应用

如果要创建一款新的 Android 应用:

创建 Main Activity

res/layout/activity_main.xml中粘贴以下代码:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context="intel.example.pitbbcalc.MainActivity"><LinearLayout
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:orientation="vertical"><TextView
            android:id="@+id/title"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@string/title"
            android:textAppearance="?android:attr/textAppearanceLarge" /><Button
            android:id="@+id/startButton"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@string/start" /><LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"><TextView
                android:id="@+id/pi_equally"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="@string/pi_equally"
                android:textAppearance="?android:attr/textAppearanceLarge" /><TextView
                android:id="@+id/pi_val"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:textAppearance="?android:attr/textAppearanceLarge" /></LinearLayout></LinearLayout></RelativeLayout>

res/values/strings.xml中添加以下代码:

<?xml version="1.0" encoding="utf-8"?><resources><string name="app_name">PiTBBCalc</string><string name="action_settings">Settings</string><string name="start">Start</string><string name="title">Calculation of π</string><string name="pi_equally">π = </string></resources>

现在 Main Activity 如下所示:

接下来,我们需要为我们的 Activity 实施 Java* 接口。 在 src/intel.example.pitbbcalc/MainActivity.java文件中,添加以下代码:

package intel.example.pitbbcalc;

import android.app.Activity;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.TextView;

public class MainActivity extends Activity {

	private native double onClickCalc();
	private TextView piEqually;
	private TextView piVal;

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);

		Button startButton = (Button) findViewById(R.id.startButton);
		piEqually = (TextView) findViewById(R.id.pi_equally);
		piVal = (TextView) findViewById(R.id.pi_val);
		piEqually.setVisibility(View.INVISIBLE);
		piVal.setVisibility(View.INVISIBLE);

		startButton.setOnClickListener(new OnClickListener() {
			@Override
			public void onClick(View v) {
				// TODO Auto-generated method stub
				double val = onClickCalc();
				piVal.setText(String.valueOf(val));
				piEqually.setVisibility(View.VISIBLE);
				piVal.setVisibility(View.VISIBLE);
			}
		});

		System.loadLibrary("PiTBBCalc");
		System.loadLibrary("tbb");
	}

	@Override
	public boolean onCreateOptionsMenu(Menu menu) {
		// Inflate the menu; this adds items to the action bar if it is present.
		getMenuInflater().inflate(R.menu.main, menu);
		return true;
	}

	@Override
	public boolean onOptionsItemSelected(MenuItem item) {
		// Handle action bar item clicks here. The action bar will
		// automatically handle clicks on the Home/Up button, so long
		// as you specify a parent activity in AndroidManifest.xml.
		int id = item.getItemId();
		if (id == R.id.action_settings) {
			return true;
		}
		return super.onOptionsItemSelected(item);
	}
}

通过右击 Project Explorer -> Android Tools -> Add Native Support 中的项目,我们可以添加本地支持。

在下一个窗口中输入我们项目库的名称并点击完成。

现在我们要实施原生代码在单线程中执行积分计算。 在 jni/PiTBBCalc.cpp文件中,添加以下代码:

#include <jni.h>
#include <math.h>

double piIntFunc (const double x)
{
	return sqrt(1 - pow(x, 2.0));
}

double calcPi()
{
	const unsigned int n = pow(10.0, 7);
	double a(-1), b(1);
	double h = (b - a) / n;
	double x (a);

	for (unsigned int i (0); i < n; ++i)
	{
		sum += piIntFunc(x);
		x += h;
	}

	sum *= h;

	return 2 * sum;
}

extern "C"
JNIEXPORT jdouble JNICALL Java_intel_example_pitbbcalc_MainActivity_onClickCalc(JNIEnv *env,
		jobject obj)
{
	return calcPi();
}

我们可以尝试在单线程模式下运行我们的应用和 π 计算。

如果使用英特尔® TBB 为我们的项目添加并行实施,我们需要编辑 jni/Android.mk。 Android.mk 是项目的生成文件(Makefile),我们需要将英特尔® TBB 库添加到这个文件中:

LOCAL_PATH := $(call my-dir)
TBB_PATH := <tbb_sources>
TBB_BUILD_PATH := /build/linux_intel64_gcc_android_cc4.9_NDKr10b_version_android-L_release

include $(CLEAR_VARS)

LOCAL_MODULE    := PiTBBCalc
LOCAL_SRC_FILES := PiTBBCalc.cpp
LOCAL_CFLAGS += -DTBB_USE_GCC_BUILTINS -std=c++11 -fexceptions -Wdeprecated-declarations -I$(TBB_PATH)/include -I$(TBB_PATH)$(TBB_BUILD_PATH)
LOCAL_LDLIBS := -llog -ltbb -L./ -L$(TBB_PATH)$(TBB_BUILD_PATH)
LOCAL_SHARED_LIBRARIES += libtbb

include $(BUILD_SHARED_LIBRARY)

include $(CLEAR_VARS)
LOCAL_MODULE    := libtbb
LOCAL_SRC_FILES := $(TBB_PATH)$(TBB_BUILD_PATH)/libtbb.so
include $(PREBUILT_SHARED_LIBRARY)

jni/目录中,创建一个 Application.mk文件并添加以下行:

APP_ABI := x86_64 APP_GNUSTL_FORCE_CPP_FEATURES := exceptions rtti APP_STL := gnustl_shared

Application.mk 用于描述您的应用需要哪些原生模块(即静态/共享库)。 在行 APP_ABI := x86_64中,我们可以指定目标架构。

现在我们可以尝试运行我们的应用。 如果您看到了应用的主界面,则说明英特尔® TBB 链接成功,我们可以开始开发应用。

向代码添加并行功能时,我们应加上英特尔® TBB 标题: #include "tbb/tbb.h",移除周期并添加以下代码:

double sum = tbb::parallel_reduce(
		tbb::blocked_range<unsigned int>(0,n),
		double(0), // identity element for summation
		[&](const tbb::blocked_range<unsigned int>& r, double sum)->double {
			for( int i=r.begin(); i!=r.end(); ++i )
			{
				sum += piIntFunc(x);
				x += h;
			}
			return sum; // body returns updated value of accumulator
		},
		[]( double x, double y )->double {
			return x+y; // joins two accumulated values
		}
	);

现在,如果您运行该应用,它就可以在多线程模式下运行了。

总结

如您所见,开发并行应用非常简单。 以计算 π 为例,我们成功演示了如何从单线程模式到多线程模式运用这些概念。

其他资源


英特尔® Energy Profiler安卓版入门

$
0
0

着手改进您的电源和能源管理

通过英特尔® Energy Profiler分析,面向系统的英特尔® VTune™ Amplifier 可帮助您优化 Linux* 嵌入式平台、Android* 或 Windows* 系统上的代码的电源管理。 通过功耗分析,您可以发现浪费能源的行为,从而显著延长设备的电池续航时间。

监控不同的硬件睡眠状态,包括硬件从睡眠状态中唤醒的原因。 探测 CPU 频率以分析您系统的功耗问题。 以可视的方式执行结果比较,快速量化和证明改进之处。

使用英特尔® Energy Profiler 执行功耗分析

  • 监控不同的硬件睡眠状态,包括唤醒原因。
  • 探测 CPU 频率以分析功耗问题
  • 以可视的方式执行结果比较,快速量化和证明改进之处。
  • 适用于多种目标系统:
    • Windows* Targets
    • Linux* Targets
    • Android* Targets

目录:

  1. 适用于本概览的主机和目标系统

  2. 检查驱动程序

  3. 安装应用

  4. 运行采集

  5. 结果可视化

  6. 解释结果

  7. 其他资源

  8. 法律声明

适用于本概览的主机和目标系统

本指南介绍了如何使用英特尔® System Studio(安装到 Windows 主机)所包含的工具在 Android 目标系统上进行能源剖析。  一些其他文本还介绍了在 Linux 主机上运行时存在的细微差异。  在此假定在 Mac* 主机上运行时情况相似,但作者并没有使用 Mac* 主机来直接证实该声明的真实性。  屏幕截图截自 Windows 主机。

 

检查驱动程序:

面向 Android 目标系统的英特尔 Energy Profiler 具有多个组件。  其中包括需要系统级许可的内核级 Android 驱动程序,如果这些驱动程序采用 Android 内核构建并已安装在设备上,便会很容易获得正确的许可。  还包括与这些驱动程序连接的低级别应用以及用于数据采集的命令行接口,后者通常被称为英特尔 SoC Watch 采集器,因为该命令名称是 socwatch,可通过 ADB 脚本轻松安装。  最后还包括集成到英特尔 VTune Amplifier GUI 中的可视化组件。  您可以借助英特尔 SoC Watch命令行采集能源分析数据,而且无需使用英特尔 VTune Amplifier 内的 GUI 接口,便可手动处理结果。  这就是为什么有关此工具的多数文献只提到“英特尔 SoC Watch”分析工具,而不提英特尔 Energy Profiler。

如要检查驱动程序是否已安装在您的目标设备上以及需要什么版本的采集工具,只需打开 adb shell 并检查 SOCWATCHx_x.ko 驱动程序的目录是 /lib/modules/ 还是 /system/lib/modules/。

Example finding SOCWATCH driver

如上所示,本示例中的设备在系统映像中已有 /lib/modules/SOCWATCH1_3.ko 驱动程序,因此本示例会使用英特尔 SoC Watch 采集器 1.3 版命令行执行数据采集。  如果发现驱动程序是 SOCWATCH1_5.ko,那就要使用 1.5 版数据采集器。  注: Android Lollipop*映像可能会将驱动程序放在 /system/lib/modules 目录中。

警告:

如果系统上不存在驱动程序,那么用户就需要进行构建,这不在本文范围之内。  用户指南中节“构建内核模块”提供了一些指导。 值得注意的是,要求驱动程序的安全签名与系统内核的相同,或能为驱动程序提供系统级许可。

 

安装数据采集应用:

既然我们已通过检查设备上的驱动程序确定了要使用什么版本的数据采集器,我们就需要为设备安装该版本的数据采集应用。  注: 每台设备只需安装一次。

找到您安装英特尔 System Studio 时所包含的 system_studio_target.tgz 文件。  它应该在 <ISS_install_dir>/Targets 中,其中 <ISS_install_dir> 是 System Studio 安装的位置。   通常在 Windows 主机上是 "C:\Program Files (x86)\Intel\System Studio yyyy.x.xxx",在 Linux 主机上是 "/opt/intel/system_studio_yyyy.x.xxx",其中 yyyy.x.xxx 是您特定安装的版本号。  这个相同的 tgz 文件包含了 Windows 或 Linux 主机所需要的文件。  将包含目录的文件解压到系统上您可以快速找到的位置。  在本示例中,我把截图放在了 "C:\Users\Public\ISS-2015_SoCWatch"。

从命令行提示窗口,导航至您刚解压的与英特尔 SoC Watch驱动程序版本相匹配(之前在设备上确定的)的目录。  在本示例中: C:\Users\Public\ISS-2015_SoCWatch\system\studio\target\socwatch_android_v1.3d\

注:  该目录中的用户指南包含大量有用的信息。  包括但不限于所用特定版本采集器上所有选件的描述、关于如何分析结果的信息以及涵盖了本文所涉及的大部分内容的快速启动指南。

在确保通过 USB 连接设备的同时,运行安装脚本。  对于 Windows,运行 socwatch_android_install.bat batch file;在 Linux 上运行 socwatch_android_install.sh shell script。  如果您已在该设备上执行过此步骤,您就会收到通知,并且系统不会执行任何操作;因此如果您无意中安装了错误的版本,您需要为设备添加 adb shell 并删除 /data/socwatch 目录,然后重新运行。  否则,您会看到有一系列文件正在复制到设备上。

Installing the collection app onto the device

 

运行采集:

用最简单的形式采集数据用于分析,这包括三个步骤:设置环境、加载驱动程序和运行采集脚本。

设置环境:

每次将一个新的 ADB shell 连接至设备用于数据采集时,就需要完成这个步骤。

通过 ADB shell 连接至设备,将目录更改为 /data/socwatch 目录。  在那里您可以找到 setup_socwatch_env.sh 脚本。  提供脚本,将环境设置传播到当前 shell。  您会注意到它提醒您加载驱动程序,这是下一个步骤。  二选一:

提供 ./setup_socwatch_env.sh 或 ./setup_socwatch_env.sh

Setting Up the collection environment using the shell script

加载驱动程序:

每次在执行数据采集之前启动设备时,都需要完成此步骤。  不一定每次都连接一个新的 adb shell。

现在您可以使用 insmod 命令,将上节“检查驱动程序”中的驱动程序加载到设备的主动内存中备用。  在本示例中,找到的驱动程序是 /lib/modules/SOCWATCH1_3.ko,说明驱动程序已加载。  您可通过使用 lsmod 命令列出加载的驱动程序,再次确认驱动程序已加载。  一些版本的 socwatch 还需要加载其他驱动程序,如 socperf_xxx.ko 驱动程序,从 set_socwatch_env.sh 脚本的输出便可明确这一点。

insmod /lib/modules/SOCWATCHx_y.ko

Loading and confirming the socwatch driver

运行采集脚本:

每次需要分析一个新的数据文件时,就要运行这个步骤。  具体参数会因所用的采集器的版本、硬件支持的特性以及执行分析所采集的信息而有所差异。  通过使用 --help 选项,可获得其中许多参数的基本解释。  这是一个命令行示例,它在 cstate 和 pstate 下采集数据,等待 5 秒才开始采集,总共采集数据 30 秒,最后将结果输出至 /data/socwatch/results/ 目录,文件名称以 GSG_examplerun 开头。

./socwatch --max-detail -f cpu -s 5 -t 30 -o ./results/GSG_examplerun

Collection Run example

注:由于 USB 线缆连接到设备上时可能会影响电源状态,用户在发出采集命令后以及开始采集前通常会断开线缆。  这可以通过使用后台任务和 nohup (如 "nohup ./socwatch <options> &")实现,然后即可断开线缆。

注:用户通常都希望只在一个特定应用执行期间采集数据,这可通过 socwatch 命令的 "-p"开关得以实现,请参阅帮助文件和用户指南获得更多信息。

 

 

VTune Amplifier 中的结果可视化

通过两个步骤可将结果导入英特尔 VTuneAmplifier,首先从设备中提取结果文件,然后将该文件导入分析器中。

从设备中提取结果:

返回主机设备上的命令提示窗口,导航至一个可存储一些数据文件的目录。  建议不要使用英特尔 VTune Amplifier 项目目录本身,因为将这些目录导入您的项目时便已自动将其复制到项目目录中了,所以可使用一个简单的临时目录。  由于各种许可原因,用户可能无法跳过该步骤并直接通过 devicename/internal storage/data 路径导航至该文件。  通过 adb pull 从您的临时目录中获取结果文件,如果具体的文件名称不容易记,可通过 "adb shell ls /data/socwatch/results"列出这些名称。

adb pull /data/socwatch/results/GSG_examplerun.csv adb pull /data/socwatch/results/GSG_examplerun.sw1

Pulling Results

 

导入 VTune™ 进行分析

在这个步骤中,只需从设备中提取出 sw1 文件。  csv (逗号分割值)文件是一个结果文本文件,供自定义分析脚本或其他用途使用。

启动英特尔 VTune Amplifier 应用,加载您感兴趣的项目。  如果您不熟悉如何执行该步骤,请参阅面向系统的英特尔® VTune™ Amplifier 入门文章或类似文章。   从 "Welcome"选项卡中点击 "Import Result..."链接。  或者从 "New Analysis tab"中选择适合分析类型的 "Energy Analysis",然后点击 "Import Data"。 

注:该方法仅适用于英特尔 System Studio 2015 Update 1 或更高版本。  如果使用之前版本的英特尔 System Studio,可能需要运行命令行放大器工具才能导入数据。 

Welcome Screen Import

Analysis Screen Import

Both cases will lead to the "Import a File and Create a Result" tab where you can browse to the sw1 file pulled off of your device and select it for import.

Import File Selection Screen

点击 "Import",系统便会分析并显示数据以供审核!

 

解释结果:

特定信息的可用性在很大程度上取决于硬件、采集器版本和用于采集的命令行选项。 加载结果时,平台电源分析观点应该是默认的(通常是唯一的选项),而且汇总选项卡界面应该显示出来。   汇总选项卡会显示一些有用的信息,例如运行时间和可用的内核时间(一般是可用内核数乘以运行时间),以及每内核每秒的唤醒事件和一些有关活跃内核频率和唤醒事件原因的信息。  该示例使用以下命令行启动运行,同时设备睡眠时黑屏但 USB 线缆仍保持连接。  正如所预期的,结果显示设备处于非常低的功耗状态中。 

./socwatch --max-detail -f cpu -f device -f sys -t 30 -s 5 –o ./results/idleasleep

Sleeping System Summary

另一个有用的选项卡是 "Correlate Metrics",它会显示各种状态和事件信息的时间表。  您可以轻松放大自己特别感兴趣的时间段,调查不同事件的时间。  值得注意的是,如果将鼠标悬停在少量数据上,屏幕上会有更多的信息弹出,如下所示。

Sleeping System Correlate Metrics

在同一窗口内比较两个结果,支持您轻松比较两个在相似条件下的不同运行,从而搜索预期改进或结果再现性。  为此,首先要分别导入两个结果,然后关闭它们的分析选项卡。  此时,建议您在 Project Navigator 窗口中为结果重命名以方便查阅,但不强制要求。  然后点击 Compare Results 图标 并确定要比较的两个结果。  分析器会显示这两个并排比较的结果。  下面的屏幕截图比较了两个运行不同性能指标测试的运行。

Multi-Run comparison summary

Multi-Run Compare with renamed results

 

 

更多信息:

在线资源: 

引用文章:

 


法律信息

英特尔、VTune 和 Intel 标识是英特尔公司在美国和/或其他国家的商标。

*其他的名称和品牌可能是其他所有者的资产。

Microsoft、Windows 和 Windows 标识是微软公司在美国和/或其他国家的商标或注册商标。

与非英特尔站点的链接仅为方便起见提供,不构成对这些站点或其内容准确度或功能的任何责任或保证。

版权所有 © 2015-2016 英特尔公司。 保留所有权利。 

如何基于英特尔® x86平台开发和评价64位安卓* 应用

$
0
0

简介

现在,越来越多的移动最终用户设备支持 64 位架构。 使用 64 位安卓*系统是打入市场的理想方式。 本文将介绍英特尔® 64 位架构上的安卓系统及其独特的功能,其中包括技术详情、性能优势、问题说明,以及可用于英特尔® 凌动™ 处理器平台上的安卓系统的解决方案。

英特尔将继续针对其 64 位处理器推出领先的全新特性。 希望利用英特尔® 高级加密标准新指令(英特尔® AES-NI)以及其他创新技术的开发人员,只需在 64 位模式下对其应用进行编译即可。 英特尔正在优化面向 64 位架构的安卓系统,并创建能够为高级开发人员带来出色 CPU 功能的 SDK。

借助谷歌最近发布的 x86 64 位安卓 5.0 模拟器映像,开发人员能够针对基于英特尔凌动的设备创建 64 位应用。

安卓 32 位与 64 位

今天许多 64 位平台,包括 Solaris, AIX*、HP-UX*、Linux*、OS X*、BSD 和 BM z/OS*,都使用 LP64模式。 由于安卓系统基于 Linux 内核,安卓 64 位平台还可使用 LP64 模式 [1。 数据类型如图 1 所示。


图 1: LP32 和 LP64 模式上不同的数据类型大小

英特尔 64 位处理器具有 32 位兼容模式。 尽管 32 位应用可在 64 位处理器上运行,但使用 64 位优化编译器进行编译可帮助其充分利用 64 位环境的架构优势。

如何构建 64 位安卓应用

对于基于 NDK 的安卓应用,您需要构建原生库支持 64 位系统。 您可以执行以下步骤:

  1. 下载并安装正确的 NDK 版本。
    您可以通过官方网站获得 [2] 或者从 AOSP 亲自构建。
    [注: 现在针对 32 位和 64 位系统有两个版本的 NDK,首先以 R10 开始。]
    对于 Windows* 和 Linux 系统,您需要解压 NDK 文件,并在系统 PATH 环境中设置正确的路径。

  2. 构建库来支持 64 位系统。
    记得使用 ABI x86_64 构建您的项目,使用 “ndk-build APP_ABI=x86_64” 命令。 这是在 x86 平台上针对 64 位安卓系统构建原生库的最佳方式。 对于其他平台,您需要将 APP_ABI 设置为 “arm64-v8a” 或 “mips64”。 或者,您可以将“APP_ABI = x86_64”行添加至 Application.mk,并直接使用“ndk-build”命令。
    在大多数情况下,上述命令能够正常工作,但有时候会发生错误。 您应根据错误信息纠正原生代码。

  3. 重新构建整个项目以获得新应用。
    现在您可以安装新创建的应用,并在模拟器或目标设备上对其测试。

如何在模拟器上运行 64 位应用

如果您没有支持完整 64 位的设备,您可以在模拟器中测试您的应用。 幸运的是,Android Dev 宣布推出面向英特尔 x86 架构的安卓 L 64 位模拟器 [3]。 您可以使用安卓 SDK 管理器,下载并安装面向英特尔凌动处理器的 64 位系统映像。 如图 2 所示,在安卓 SDK 管理器中,您可以选择 Android 5.0 (API 21) 下方的“英特尔 x86 Atom_64 系统映像”并进行安装。


图 2: 安卓* SDK 管理器中的“英特尔® x86 Atom_64 系统映像”复选框

创建基于英特尔凌动处理器 64 位系统映像的新安卓虚拟设备 (AVD),并在 SDK 管理器或 ADT 中打开 AVD 管理器。 然后点击“创建”并填写相关字段,如图 3 所示。 现在 64 位 AVD 模拟器可以正常运行。


图 3: 在 AVD 中创建 x64 映像

按照下面的步骤操作在模拟器上运行 64 位应用:

  • 将 /jni/application.mk 编辑为 APP_ABI := x86_64
  • 编译原生文件
  • 在 AVD 上作为安卓应用运行


图 4: 名为“x86_64 Hello-JNI” 的应用,在 x64 AVD 上运行

64 位 x86 安卓平台的性能

根据我们的测试,应用性能比 64 位系统更高。 我们在一台 64 位 IA 设备上运行加密演示,代号为 Bay Trail。 该加密演示可从最新版的安卓和英特尔 AES-NI 受益。 图 5 显示了加密 312M mp4 文件的结果。 在不包括 I/O 时间的情况下,32 位安卓 5.0 系统上的总时间为 1.56s;64 位安卓 5.0 系统上的总时间为 1.49s,性能提升 5% [4].


图 5: 借助英特尔® AES-NI 加密 mp4 的时间

下表中列出了详细的测试结果。

表 1: 运行加密应用的详细测试结果

总时间 (+SD R/W)

加密时间 (-SD R/W)

安卓* 5.0
64 位

安卓 5.0
32 位

安卓 5.0
64 位

安卓 5.0
32 位

7.046.

7.379.

1.498.

1.649.

7.152.

7.27.

1.49.

1.529.

7.011.

7.368.

1.529.

1.57.

7.151.

7.196.

1.487.

1.522.

7.182.

7.282.

1.468.

1.548.

7.1084.

7.299.

1.4944.

1.5636.

 

此外,我们还构建了 64 位 FFMPEG 并验证了安卓 5.0 的性能。 结果显示,在相同的 Bay Trail 平台上,64 位版的平均 FPS 性能比 32 位版高 7% [4]。


图 7: 1080p MP4 播放的 FPS(通过 64 位安卓* 系统上的 FFMPEG)

下面的两个示例显示了英特尔平台上的 64 位安卓系统的性能优势。 在中国针对多个 ISV 软件的性能指标评测显示,相比使用 32 位系统,使用 64 位系统可带来 7-8% [4] 的性能提升。

一家 ISV 的多媒体解码应用:

该基准测试显示,所有格式(3gp, avi, flv, mkv, mov, mp4, rmvb, wmv)的播放性能提高约 20% (64 位相对于 32 位系统的性能提升) [4]。


图 8: 64 位安卓* 系统上的多媒体解码应用的 FPS

一家 ISV 的语音识别应用:

针对 64 位安卓系统而启用的语音组合引擎在 64 位+ ART 上的性能提升约为 ~40% (与使用 32 位相比,使用 64 位性能提升约 10% [4]。


图 9: 64 位安卓* 系统上的语音组合引擎初始化时间

我们分别在 64 位和 32 位安卓平台上分析了该 TTS 演示模块,其中包括 get token 成本、sty cpy 成本、流成本和 getline 成本: (ms)


图 10: 64 位安卓* 系统上的 TTS 密钥模块成本

该结果显示了 64 位应用的优势,尤其是对于内存 I/O 和流操作。

总结

支持 64 位技术(面向基于英特尔凌动处理器的平台)的安卓 L 现在可帮助开发人员优化其应用。 构建 64 位安卓应用与构建传统的 32 位安卓应用一样简单。 您现在可以使用官方安卓 64 位模拟器来运行和调试 64 位应用。 为了测试 64 位 x86 安卓平台的性能,我们重新构建了多个 64 位开源项目,并邀请中国多家顶级 ISV 构建 64 位版的应用。 与使用 32 位相比,使用 64 位使系统性能提升 7-8%(在某些特殊情况下甚至高达 20%)。

相关文章

64 位 Android* 操作系统
https://software.intel.com/zh-cn/android/articles/64-bit-android-os

了解安卓平台上的 x86 与 ARM 内存对齐:
https://software.intel.com/zh-cn/blogs/2011/08/18/understanding-x86-vs-arm-memory-alignment-on-android

参考资料

[1] http://en.wikipedia.org/wiki/64-bit_computing
[2] http://developer.Android.com/tools/sdk/ndk/index.html
[3] https://plus.google.com/+AndroidDevelopers/posts/XG1WmNDMe8H
[4] 性能测试中使用的软件和工作负载可能仅在英特尔微处理器上针对性能进行了优化。 诸如 SYSmark* 和 MobileMark* 等测试均系基于特定计算机系统、硬件、软件、操作系统及功能。 上述任何要素的变动都有可能导致测试结果的变化。 请参考其它信息及性能测试(包括结合其它产品使用时的运行性能)以对目标产品进行全面评估。 配置: [Asus T100 (CPU-英特尔凌动 Baytrail T Z3740,内存-2G, SSD),性能指标评测,测试人:Zhang Li& Zhou Zhen]。 更多信息敬请登录http://www.intel.com/performance

作者介绍

Zhen Zhou 拥有上海交通大学软件工程专业的硕士学位。 他于 2011 年加入英特尔,在开发商关系部门移动支持团队担任一名应用工程师。 他与内部相关各方与外部 ISV、SP 和 运营商密切合作,负责在英特尔凌动处理器、传统英特尔架构和嵌入式英特尔架构平台等领域设计新的使用模式以及开发原型。

Zhang Li 在英特尔软件和服务事业部 (SSG) 开发人员关系部门移动支持团队担任应用工程师。 他主要负责安卓平台上支持的应用。

JumpChat案例 – 在基于英特尔® Atom™ 处理器的设备上使用MediaCodec进行硬件解码

$
0
0

下载 PDF[PDF 416KB]

Christine M. Lin,软件工程师

简介

在 Android* 平台上,处理视频流的通信应用可充分利用英特尔® 凌动™ 处理器上的硬件加速功能。 这样可以轻松提升性能,同时降低 CPU 利用率和功耗。

2014 年秋季,英特尔与 WebRTC 视频聊天 ISV JumpChat 联手合作,共同优化了其 Android 应用在英特尔凌动处理器 E3800 平板电脑上的性能。 经过此番努力,我们将双向高清视频聊天应用的 CPU 利用率降低了 10%,功耗降低了 1.3 瓦。 本案例研究描述了我们与 JumpChat 是如何合作实现上述成果的,旨在向您展示借助 MediaCodec 能够非常轻松地为支持的编解码器提供硬件加速。

JumpChat

JumpChat 是一款适用于 Android、iOS* 和 Chrome*、Firefox* 以及 Opera* 浏览器的视频会议应用。 借助这款应用,用户无需任何登陆或账户便可视频聊天。 如要发起视频通话,您只需向接受者发送房间 URL,即创建一个保证安全和加密的连接。 其中一个突出的特性是 JumpChat 在视频会议期间支持桌面共享和文件传输。

凌动处理器 E800 上的软件解码

JumpChat 首先做的一件事是将应用移植到 x86 架构,以便支持高清视频会议。 此外,我们还进行了多项 UI 改进和漏洞修复。 使用 1.0.7 版,我们在两台使用平均带宽 (500 kbps) 的 KitKat 灵动处理器 E3800 平板电脑之间实现了基准双向视频通话。 较之本地非 WebRTC 竞争产品,图像质量良好,无视频延迟或音频延迟,并且在使用一段时间后设备温度没有明显升高。 使用 NI_DAQ* 仪表系统获得的电源和性能采集结果显示,视频通话期间整体 CPU 利用率为 60%,功耗约为 7.0 瓦:

为了降低功耗和 CPU 利用率,我们特别注意了运行期间最为活跃的模块。 目前,libjingle_peerconnection_so.so (谷歌的 WebRTC 代码)是最活跃的模块,约占活跃 JumpChat 进程时间的 56%。 许多 JumpChat 线程以超过 60Hz 屏幕刷新率的频率运行,这样会阻止 CPU 进入睡眠状态。

MediaCodec

为了降低 CPU 利用率以及将可能的编码/解码活动卸载至 GPU,我们决定研究硬件加速的解码对当前正使用的软件编解码器会有多大的影响。 由于该应用是在 WebRTC 上实施的,所以 JumpChat 主要使用VP8 编解码器。 凌动处理器 E3800 平板电脑支持硬件加速的 VP8 解码,但不支持硬件加速的 VP8 编码。 1.0.8 版 JumpChat 通过 MediaCodec 支持面向 x86 和 ARM* 的硬件加速解码。 Android 的 MediaCodec 类可用于访问低级别的媒体组件。 借助 Java* 在您的设备上选择硬件解码器,使用 getSupportedTypes 评估器查询适合 MIME 类型(在本案例中是 VP8_MIME_TYPE)的 MediaCodecInfo。 面向凌动处理器 E3800 平板电脑的英特尔硬件加速编码器和解码器以前缀 “OMX.Intel” 开头,且返回字符串为 OMX.Intel.VideoDecoder.VPX。 照常创建和配置解码器初始化属性。

凌动处理器 E3800 上的硬件解码

使用 1.0.8 版 JumpChat 对两台相同的 KitKat 凌动处理器 E3800 设备进行了另一次 NI_DAQ 采集,结果显示视频通话期间整体 CPU 利用率为 51%,功耗约为 5.6 瓦。

较之软件解码器,使用硬件加速解码器将 VP8 解码卸载至 GPU 可将 CPU 利用率降低约 10%,功耗降低约 1.3 瓦。 凌动处理器 E800 后续平台配有 VP8 的硬件加速编码器,在降低利用率和功耗方面的效果可能会更明显。1

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

结论

对于处理大量视频流的应用,通过 MediaCodec 使用平台支持的硬件加速解码器和编码器可轻松降低 CPU 利用率和功耗。

基于64位架构使用英特尔® 线程构建模块并行化安卓* 应用

$
0
0

全新的 Android L 64 位操作系统已于近日推出。 在本文中,我将向您展示如何利用英特尔® 线程构建模块(英特尔® TBB)轻松地开发面向 Android L 64 位架构的并行应用。 英特尔® TBB 是一个跨平台模板库,可用于创建并行程序。 它可以创建和同步数据流,同时隐藏架构详情,支持您在更高的抽象化水平上工作。 英特尔® TBB 可以在所有架构上运行。 对于 Android,请使用 4.3 及更高版本。

构建英特尔® TBB

  1. 您可以在此处下载英特尔® TBB: https://www.threadingbuildingblocks.org/. 我下载了最新的稳定版(4.3 Update 1)。
  2. 将 NDK 添加至路径:
    对于 Windows*:
    • $ SET PATH=%PATH%; <path_to_ndk>
    对于 Linux*:
    • $ export PATH=$PATH: <path_to_ndk>
  3. 解压 TBB,转至 src 文件夹。
    $ cd <tbb_sources>/src/
  4. 通过运行以下代码构建面向 Android 的英特尔 TBB 库: $ <path_to_ndk>/ndk-build –C <tbb_sources>/src/ arch=intel64 compiler=gcc target=android clean tbb tbbmalloc -j
  5. 库构建完成。 在 build 目录 (<tbb_sources>/build/) 中,您可以找到包含这些 libs 的目录: libgnustl_shared.so, libtbbmalloc_proxy.so, libtbbmalloc.so and libtbb.so. libtbb.so and libgnustl_shared。 我们会在应用中用到 libtbb.so 和 libgnustl_shared.so。

配置仿真器

在 Eclipse* 中点击: Window -> Android Virtual Device Manager. 在该窗口中点击 Create 并在下一个界面上选择显示的选项:

点击 Ok。 如要检查它是否工作,选择列表中的新设备并点击 Start... 在新窗口中,点击 Launch 按钮启动仿真器:

创建应用

在我们的样本应用中,我们会采用矩阵相乘 (3x2) 乘以 (2x3)。 让我们开始吧!

创建一个新的 Android 应用:

通过右击我们在 Project Explorer -> Android Tools -> Add Native Support 中的项目,我们可以添加本地支持。

在下一个窗口中输入项目库的名称并点击 Finish

在项目目录中,一个新的 jni 目录创建完成了。 Android.mk 是项目的生成文件,我们需要将英特尔® TBB 库添加到这个文件中:

LOCAL_PATH := $(call my-dir)
TBB_PATH := <tbb_sources>
TBB_BUILD_PATH := /build/linux_intel64_gcc_android_cc4.9_NDKr10b_version_android-L_release

include $(CLEAR_VARS)

LOCAL_MODULE    := TBBMatrixMult
LOCAL_SRC_FILES := TBBMatrixMult.cpp
LOCAL_CFLAGS += -DTBB_USE_GCC_BUILTINS -std=c++11 -fexceptions -Wdeprecated-declarations -I$(TBB_PATH)/include -I$(TBB_PATH)$(TBB_BUILD_PATH)
LOCAL_LDLIBS := -llog -ltbb -L./ -L$(TBB_PATH)$(TBB_BUILD_PATH)
LOCAL_SHARED_LIBRARIES += libtbb

include $(BUILD_SHARED_LIBRARY)

include $(CLEAR_VARS)
LOCAL_MODULE    := libtbb
LOCAL_SRC_FILES := $(TBB_PATH)$(TBB_BUILD_PATH)/libtbb.so
include $(PREBUILT_SHARED_LIBRARY)

在 jni 目录中,创建一个 Application.mk 文件并添加以下行:

APP_ABI := x86_64
APP_GNUSTL_FORCE_CPP_FEATURES := exceptions rtti
APP_STL := gnustl_shared

Application.mk 用于描述您的应用需要哪些原生模块(即静态/共享库)。 在行 APP_ABI := x86_64中,我们可以指定我们的目标架构。 它会创建面向 64 位架构的应用。

现在我们可以尝试运行我们的应用。 如果您看到了应用的主界面,那就表明英特尔® TBB 链接成功了,我们可以开始开发我们的矩阵乘法应用了。

res/layout/activity_main.xml中粘贴以下代码:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context=".MainActivity"><ScrollView
        android:id="@+id/scrollView1"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_alignParentTop="true"
        android:layout_centerHorizontal="true"><LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="vertical"><TextView
                android:id="@+id/titleA"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_marginTop="16dp"
                android:text="@string/a"
                android:textAppearance="?android:attr/textAppearanceSmall" /><LinearLayout
		        android:layout_width="match_parent"
		        android:layout_height="wrap_content"><EditText
		            android:id="@+id/a00"
		            android:layout_width="wrap_content"
		            android:layout_height="wrap_content"
		            android:layout_weight="1"
		            android:ems="3"
		            android:inputType="numberDecimal|numberSigned" /><EditText
		            android:id="@+id/a01"
		            android:layout_width="wrap_content"
		            android:layout_height="wrap_content"
		            android:layout_weight="1"
		            android:ems="3"
		            android:inputType="numberDecimal|numberSigned"><requestFocus /></EditText></LinearLayout><LinearLayout
		        android:layout_width="match_parent"
		        android:layout_height="wrap_content"><EditText
		            android:id="@+id/a10"
		            android:layout_width="wrap_content"
		            android:layout_height="wrap_content"
		            android:layout_weight="1"
		            android:ems="3"
		            android:inputType="numberDecimal|numberSigned" /><EditText
		            android:id="@+id/a11"
		            android:layout_width="wrap_content"
		            android:layout_height="wrap_content"
		            android:layout_weight="1"
		            android:ems="3"
		            android:inputType="numberDecimal|numberSigned" /></LinearLayout><LinearLayout
		        android:layout_width="match_parent"
		        android:layout_height="wrap_content"><EditText
		            android:id="@+id/a20"
		            android:layout_width="wrap_content"
		            android:layout_height="wrap_content"
		            android:layout_weight="1"
		            android:ems="3"
		            android:inputType="numberDecimal|numberSigned" /><EditText
		            android:id="@+id/a21"
		            android:layout_width="wrap_content"
		            android:layout_height="wrap_content"
		            android:layout_weight="1"
		            android:ems="3"
		            android:inputType="numberDecimal|numberSigned" /></LinearLayout><TextView
		        android:id="@+id/titleB"
		        android:layout_width="wrap_content"
		        android:layout_height="wrap_content"
		        android:layout_marginTop="15dp"
		        android:text="@string/b"
		        android:textAppearance="?android:attr/textAppearanceSmall" /><LinearLayout
		        android:layout_width="match_parent"
		        android:layout_height="wrap_content"><EditText
		            android:id="@+id/b00"
		            android:layout_width="wrap_content"
		            android:layout_height="wrap_content"
		            android:layout_weight="1"
		            android:ems="3"
		            android:inputType="numberDecimal|numberSigned" /><EditText
		            android:id="@+id/b01"
		            android:layout_width="wrap_content"
		            android:layout_height="wrap_content"
		            android:layout_weight="1"
		            android:ems="3"
		            android:inputType="numberDecimal|numberSigned" /><EditText
		            android:id="@+id/b02"
		            android:layout_width="wrap_content"
		            android:layout_height="wrap_content"
		            android:layout_weight="1"
		            android:ems="3"
		            android:inputType="numberDecimal|numberSigned" /></LinearLayout><LinearLayout
		        android:layout_width="match_parent"
		        android:layout_height="wrap_content"><EditText
		            android:id="@+id/b10"
		            android:layout_width="wrap_content"
		            android:layout_height="wrap_content"
		            android:layout_weight="1"
		            android:ems="3"
		            android:inputType="numberDecimal|numberSigned" /><EditText
		            android:id="@+id/b11"
		            android:layout_width="wrap_content"
		            android:layout_height="wrap_content"
		            android:layout_weight="1"
		            android:ems="3"
		            android:inputType="numberDecimal|numberSigned" /><EditText
		            android:id="@+id/b12"
		            android:layout_width="wrap_content"
		            android:layout_height="wrap_content"
		            android:layout_weight="1"
		            android:ems="3"
		            android:inputType="numberDecimal|numberSigned" /></LinearLayout><Button
		        android:id="@+id/button"
		        style="?android:attr/buttonStyleSmall"
		        android:layout_width="wrap_content"
		        android:layout_height="wrap_content"
		        android:layout_marginTop="17dp"
		        android:text="@string/button" /><TextView
		        android:id="@+id/titleC"
		        android:layout_width="wrap_content"
		        android:layout_height="wrap_content"
		        android:text="@string/c"
		        android:textAppearance="?android:attr/textAppearanceSmall" /><LinearLayout
		        android:layout_width="match_parent"
		        android:layout_height="wrap_content"><TextView
		            android:id="@+id/c00"
		            android:layout_width="70dp"
		            android:layout_height="wrap_content"
		            android:textAppearance="?android:attr/textAppearanceSmall" /><TextView
		            android:id="@+id/c01"
		            android:layout_width="70dp"
		            android:layout_height="wrap_content"
		            android:textAppearance="?android:attr/textAppearanceSmall" /><TextView
		            android:id="@+id/c02"
		            android:layout_width="70dp"
		            android:layout_height="wrap_content"
		            android:textAppearance="?android:attr/textAppearanceSmall" /></LinearLayout><LinearLayout
		        android:layout_width="match_parent"
		        android:layout_height="wrap_content"><TextView
		            android:id="@+id/c10"
		            android:layout_width="70dp"
		            android:layout_height="wrap_content"
		            android:textAppearance="?android:attr/textAppearanceSmall" /><TextView
		            android:id="@+id/c11"
		            android:layout_width="70dp"
		            android:layout_height="wrap_content"
		            android:textAppearance="?android:attr/textAppearanceSmall" /><TextView
		            android:id="@+id/c12"
		            android:layout_width="70dp"
		            android:layout_height="wrap_content"
		            android:textAppearance="?android:attr/textAppearanceSmall" /></LinearLayout><LinearLayout
		        android:layout_width="match_parent"
		        android:layout_height="wrap_content"><TextView
		            android:id="@+id/c20"
		            android:layout_width="70dp"
		            android:layout_height="wrap_content"
		            android:layout_marginBottom="40dp"
		            android:textAppearance="?android:attr/textAppearanceSmall" /><TextView
		            android:id="@+id/c21"
		            android:layout_width="70dp"
		            android:layout_height="wrap_content"
		            android:textAppearance="?android:attr/textAppearanceSmall" /><TextView
		            android:id="@+id/c22"
		            android:layout_width="70dp"
		            android:layout_height="wrap_content"
		            android:textAppearance="?android:attr/textAppearanceSmall" /></LinearLayout></LinearLayout></ScrollView></RelativeLayout>

res/values/strings.xml中添加以下代码:

<?xml version="1.0" encoding="utf-8"?><resources><string name="app_name">TBBMatrixMult</string><string name="action_settings">Settings</string><string name="a">A:</string><string name="b">B:</string><string name="c">C:</string><string name="button">A x B = C</string></resources>

现在我们的主活动如下所示:

 

接下来我们需要为我们的活动实施 Java* 接口。 在 src/intel.example.tppmatrixmult/MainActivity.java 文件中添加以下代码:

package intel.example.tbbmatrixmult;

import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;

public class MainActivity extends Activity {

	private native double [][] onClickCalc(double [] a, double [] b, int aRow, int aCol);
	private EditText matrixA [][] = new EditText[3][2];
	private EditText matrixB [][] = new EditText[2][3];
	private TextView matrixC [][] = new TextView[3][3];
	private TextView titleC;
	private Button mult;

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);

		initA();
		initB();
		initC();
		mult = (Button) findViewById(R.id.button);
		mult.setOnClickListener(new OnClickListener() {
			@Override
			public void onClick(View v) {
				Toast.makeText(getBaseContext(), "Unknown error!", Toast.LENGTH_SHORT).show();
				double [][] a = getA();
				double [][] b = getB();
				if (a == null || b == null) {
					Toast.makeText(getBaseContext(), "Unknown error!", Toast.LENGTH_SHORT).show();
					return;
				}
				double [][] c = onClickCalc(matrixToArray(a), matrixToArray(b), 3, 2);
				setC(c);
				setVisibleC(true);
			}
		});
		setVisibleC(false);

		System.loadLibrary("tbb");
        System.loadLibrary("TBBMatrixMult");
	}

	private double [] matrixToArray(double [][] matrix) {
		double [] array = new double[matrix.length * matrix[0].length];
		for (int row = 0; row < matrix.length; ++row) {
			for (int col = 0; col < matrix[row].length; ++col)
			{
				array[row * matrix[row].length + col] = matrix[row][col];
			}
		}
		return array;
	}

	private void initA() {
		matrixA[0][0] = (EditText) findViewById(R.id.a00);
		matrixA[0][1] = (EditText) findViewById(R.id.a01);
		matrixA[1][0] = (EditText) findViewById(R.id.a10);
		matrixA[1][1] = (EditText) findViewById(R.id.a11);
		matrixA[2][0] = (EditText) findViewById(R.id.a20);
		matrixA[2][1] = (EditText) findViewById(R.id.a21);
	}
	private void initB() {
		matrixB[0][0] = (EditText) findViewById(R.id.b00);
		matrixB[0][1] = (EditText) findViewById(R.id.b01);
		matrixB[0][2] = (EditText) findViewById(R.id.b02);
		matrixB[1][0] = (EditText) findViewById(R.id.b10);
		matrixB[1][1] = (EditText) findViewById(R.id.b11);
		matrixB[1][2] = (EditText) findViewById(R.id.b12);
	}
	private void initC() {
		titleC = (TextView) findViewById(R.id.titleC);
		matrixC[0][0] = (TextView) findViewById(R.id.c00);
		matrixC[0][1] = (TextView) findViewById(R.id.c01);
		matrixC[0][2] = (TextView) findViewById(R.id.c02);
		matrixC[1][0] = (TextView) findViewById(R.id.c10);
		matrixC[1][1] = (TextView) findViewById(R.id.c11);
		matrixC[1][2] = (TextView) findViewById(R.id.c12);
		matrixC[2][0] = (TextView) findViewById(R.id.c20);
		matrixC[2][1] = (TextView) findViewById(R.id.c21);
		matrixC[2][2] = (TextView) findViewById(R.id.c22);
	}

	private double[][] getA() {
		double [][] ret = new double [matrixA.length][];
		for (int i = 0; i < matrixA.length; ++i) {
			ret[i] = new double [matrixA[i].length];
			for (int j = 0; j < matrixA[i].length; ++j) {
				if (matrixA[i][j].getText().toString().length() == 0) {
					Toast.makeText(getBaseContext(), "Error! One field is empty!", Toast.LENGTH_SHORT).show();
					return null;
				}
				ret[i][j] = Double.parseDouble(matrixA[i][j].getText().toString());
			}
		}
		return ret;
	}

	private double[][] getB() {
		double [][] ret = new double [matrixB.length][];
		for (int i = 0; i < matrixB.length; ++i) {
			ret[i] = new double [matrixB[i].length];
			for (int j = 0; j < matrixB[i].length; ++j) {
				if (matrixB[i][j].getText().toString().length() == 0) {
					Toast.makeText(getBaseContext(), "Error! One field is empty!", Toast.LENGTH_SHORT).show();
					return null;
				}
				ret[i][j] = Double.parseDouble(matrixB[i][j].getText().toString());
			}
		}
		return ret;
	}

	private void setC(double [][] cVal) {
		if (matrixC.length != cVal.length) {
			Toast.makeText(getBaseContext(), "Unknown error!", Toast.LENGTH_SHORT).show();
			return;
		}
		for (int i = 0; i < matrixC.length; ++i) {
			if (matrixC[i].length != cVal[i].length) {
				Toast.makeText(getBaseContext(), "Unknown error!", Toast.LENGTH_SHORT).show();
				return;
			}
			for (int j = 0; j < matrixC[i].length; ++j) {
				matrixC[i][j].setText(String.valueOf(cVal[i][j]));
			}
		}
	}

	private void setVisibleC(boolean cond) {
		if (cond == true)
			titleC.setVisibility(View.VISIBLE);
		else
			titleC.setVisibility(View.GONE);
		for (int i = 0; i < matrixC.length; ++i) {
			for (int j = 0; j < matrixC[i].length; ++j) {
				if (cond == true) {
					matrixC[i][j].setVisibility(View.VISIBLE);
				}
				else {
					matrixC[i][j].setVisibility(View.GONE);
				}
			}
		}
	}
}

现在我们正在使用面向并行代码的英特尔 TBB 构建。 下一个代码片段是 jni/TBBMatrixMult.cpp中的矩阵乘法实施。 该代码在单线程下运行:

#include <jni.h>

double ** arrayToMatrix(double * array, int row, int col)
{
	double ** matrix = new double * [row];
	for (int i = 0; i < row; ++i)
	{
		matrix[i] = new double [col];
		for (int j = 0; j < col; ++j)
		{
			matrix[i][j] = array[i * col + j];
		}
	}
	return matrix;
}

extern "C" JNIEXPORT jobjectArray JNICALL Java_intel_example_tbbmatrixmult_MainActivity_onClickCalc(
		JNIEnv *env, jobject obj, jdoubleArray aMatrix, jdoubleArray bMatrix, jint aRow, jint aCol)
{
	double * aArray = (*env).GetDoubleArrayElements(aMatrix, 0);
	double ** a = arrayToMatrix(aArray, aRow, aCol);
	double * bArray = (*env).GetDoubleArrayElements(bMatrix, 0);
	double ** b = arrayToMatrix(bArray, aCol, aRow);
	double ** c = new double * [aRow];
	for (int i = 0 ; i < 3; ++i) {
		c[i] = new double [aRow];
	}
	for (int row = 0; row < aRow, ++row) {
		for (int col = 0; col < aRow; ++col) {
			c[row][col] = 0;
			for(int k = 0; k < aCol; ++k)
			{
				c[row][col] += a[row][k] * b[k][col];
			}
		}
	};

	jclass doubleArrayClass = (*env).FindClass("[D");

	jobjectArray cMatrix = (*env).NewObjectArray((jsize) aRow, doubleArrayClass, 0);

	for (int i = 0; i < aRow; i++)
	{
		jdoubleArray doubleArray = (*env).NewDoubleArray(aRow);
		(*env).SetDoubleArrayRegion(doubleArray, (jsize) 0, (jsize) aRow, c[i]);
		(*env).SetObjectArrayElement(cMatrix, (jsize) i, doubleArray);
		(*env).DeleteLocalRef(doubleArray);
	}

	return cMatrix;
}
For adding parallelism to this code, we should include the Intel TBB header: #include "tbb/tbb.h" and change the cycle by row:
tbb::parallel_for (0, aRow, 1, [=](int row) {
	for (int col = 0; col < aRow; ++col) {
		c[row][col] = 0;
		for(int k = 0; k < aCol; ++k)
		{
			c[row][col] += a[row][k] * b[k][col];
		}
	}
});

现在我们正在使用面向并行代码的英特尔 TBB 构建。 下一个代码片段是 jni/TBBMatrixMult.cpp 中的矩阵乘法实施。 该代码在单线程下运行:

#include <jni.h>

double ** arrayToMatrix(double * array, int row, int col)
{
	double ** matrix = new double * [row];
	for (int i = 0; i < row; ++i)
	{
		matrix[i] = new double [col];
		for (int j = 0; j < col; ++j)
		{
			matrix[i][j] = array[i * col + j];
		}
	}
	return matrix;
}

extern "C" JNIEXPORT jobjectArray JNICALL Java_intel_example_tbbmatrixmult_MainActivity_onClickCalc(
		JNIEnv *env, jobject obj, jdoubleArray aMatrix, jdoubleArray bMatrix, jint aRow, jint aCol)
{
	double * aArray = (*env).GetDoubleArrayElements(aMatrix, 0);
	double ** a = arrayToMatrix(aArray, aRow, aCol);
	double * bArray = (*env).GetDoubleArrayElements(bMatrix, 0);
	double ** b = arrayToMatrix(bArray, aCol, aRow);
	double ** c = new double * [aRow];
	for (int i = 0 ; i < 3; ++i) {
		c[i] = new double [aRow];
	}
	for (int row = 0; row < aRow, ++row) {
		for (int col = 0; col < aRow; ++col) {
			c[row][col] = 0;
			for(int k = 0; k < aCol; ++k)
			{
				c[row][col] += a[row][k] * b[k][col];
			}
		}
	};

	jclass doubleArrayClass = (*env).FindClass("[D");

	jobjectArray cMatrix = (*env).NewObjectArray((jsize) aRow, doubleArrayClass, 0);

	for (int i = 0; i < aRow; i++)
	{
		jdoubleArray doubleArray = (*env).NewDoubleArray(aRow);
		(*env).SetDoubleArrayRegion(doubleArray, (jsize) 0, (jsize) aRow, c[i]);
		(*env).SetObjectArrayElement(cMatrix, (jsize) i, doubleArray);
		(*env).DeleteLocalRef(doubleArray);
	}

	return cMatrix;
}

向代码添加并行功能时,我们应加上英特尔® TBB 标题: #include"tbb/tbb.h"并逐行更改周期:

tbb::parallel_for (0, aRow, 1, [=](int row) {
	for (int col = 0; col < aRow; ++col) {
		c[row][col] = 0;
		for(int k = 0; k < aCol; ++k)
		{
			c[row][col] += a[row][k] * b[k][col];
		}
	}
});

您可以在下一个屏幕截图上看到我们应用的结果:

优化安卓* 应用内存使用的小窍门

$
0
0

简介

Android* 系统中的内存分配和释放总是会带来一定的代价。 中国有句话叫做 “由俭入奢易,由奢入俭难”,真实地反应了内存的使用情况。

我们设想这样一种最坏的场景,当您在编译包含数百亿行代码的应用时,突然出现内存溢出 (OOM) 的情况并导致系统崩溃。 于是您开始调试应用,分析 hprof 文件。 幸运的话,您可以找到问题的根源,并修复占用内存最多的进程 (memory killer)。 但有时您可能不那么走运,您会发现系统中有如此多的小型变量和临时文件占用了内存资源,以至于简单的修补也无济于事。这意味着您必须重构代码,但却只能节省几千字节甚至几字节的内存,而其中还存在潜在的风险。

这篇文章详细介绍了 Android 内存管理,解释了管理系统中非常重要的几个方面。 另外本文也会涉及改进内存管理、检测和避免内存泄漏,以及分析内存使用情况等内容。

Android 内存管理

Android 使用分页和 mmap 而非提供交换区来管理内存,即除非释放所有引用对象,否则凡是应用所占的内存都不能被调用。

面向应用进程的 Dalvik* 虚拟机堆内存是有限的。 应用启动时为 2MB,最大分配内存(标记为 "largeHeap")不得超过 36MB(因具体设备配置而异)。 典型的大堆应用包括图片/视频编辑器、摄像机、图库和主屏幕。

Android 采用 LRU 高速缓存来存储后台应用进程。 当系统运行内存较低时,它会根据 LRU 策略“杀死”进程,但同时也会考虑哪些应用占用了最多的内存。 现在 Android 最多可支持 20 个后台进程(因具体设备配置而异)。 如果需要应用在后台所处的时间更长,那么您需要在转至后台前先释放不必要的内存,这样 Android 系统发出错误信息甚至终止应用的可能性会大大降低。

如何提高内存利用率

Android 是一款全球性移动平台,数百亿的 Android 开发人员都致力于构建稳定而又可扩展的应用。 以下是提高 Android 应用内存利用率的一些方法和最佳实践:

  1. 请慎重使用 “抽象化” 设计模式。 尽管从设计模式的角度来看,抽象化能够帮助构建更加灵活的软件架构, 但是在移动领域,抽象化可能会带来副作用,因为这需要执行额外的代码,不仅费时而又多占内存。 除非抽象化能为您的应用带来巨大的优势,否则最好不要使用。
  2. 避免使用 "enum"。 Enum 比普通静态常数的内存分配多出一倍,所以请不要使用。
  3. 尝试使用经过优化的 SparseArray、SparseBooleanArray 和 LongSparseArray 集合来代替 HashMap。 HashMap 在每次映射中都会分配一个条目对象,这会引起内存效率降低,并且导致性能低下的 “自动装箱与拆箱” 操作贯穿整个使用过程。 相反,SparseArray 这类的集合会将关键值映射到普通数组。 但是请谨记,这些经过优化的集合不适用于大量条目。在执行添加/删除/搜索操作时,如果您的数据集中的记录超过几千条,那么其速度比 Hashmap 还要慢。
  4. 避免创建不必要的对象。 如果可能,请不要为短期的临时对象专门分配内存;创建对象越少则垃圾回收越少。
  5. 检查您应用的可用堆。 调用 ActivityManager::getMemoryClass() 以查询应用的可用堆值 (MB)。 如果您的分配内存多于可用内存,将会发生内存溢出异常。 如果您的应用在 AndroidManifest.xml 中申请了 "largeHeap",那么您可以调用 ActivityManager::getMemoryClass() 以查询大堆的估值。
  6. 通过执行 onTrimMemory() 回调来保持与系统协调。 在您的 Activity/Service/ContentProvider 中执行 ComponentCallbacks2::onTrimMemory(int),根据最新系统限制逐步释放内存。 onTrimMemory(int) 不仅能够帮助提高整体系统响应速度,还能让您的进程在系统中存活更久。

    当出现 TRIM_MEMORY_UI_HIDDEN 时,表明您应用中的所有 UI 已被隐藏,您需要释放 UI 资源。 当您的应用在前台运行时,您可能会收到 TRIM_MEMORY_RUNNING[MODERATE/LOW/CRITICAL],或者在后台运行时收到 TRIM_MEMORY_[BACKGROUND/MODERATE/COMPLETE]。 当系统内存紧缺时,您可以根据释放内存策略释放非关键资源。

  7. 请谨慎使用 service(服务)。 如果您需要一个 service 在后台运行任务,那么除非它需要主动执行任务,否则请避免使其持续运行。 尝试使用 IntentService 来缩短 service 的生命周期,它会在完成任务以后终止自己。 谨慎使用 service,确保在不需要时全部终止运行。 否则最坏的结果将会是整个系统性能极为低下,用户只能卸载应用(如果可能的话)。

    但是如果您想创建一个需要长期运行的应用,如音乐播放器服务,那您应该在 AndroidManifest.xml 中为您的 Service 设置属性 "android:process",以便将其拆分为两个进程:一个针对 UI,一个针对后台服务。 UI 进程中的资源可在隐藏之后释放,同时运行后台播放服务。 切记,后台服务进程不得访问任何 UI,否则内存分配将会增加一倍甚至两倍!

  8. 谨慎使用 External Libraries。 External Libraries 通常为非移动设备编写,在 Android 中使用时效率较低。 在使用之前,您必须要考虑为移动设备导入和优化 library 所费的周折。 如果您使用 library 仅是为了实现其数千用途中的一两个功能,那么您最好还是自己动手实施吧。
  9. 使用具有合适分辨率的 bitmap(位图)。 加载一个具有您所需分辨率的 bitmap,或者如果初始 bitmap 分辨率过高,则按需缩小。
  10. 使用 Proguard* 和 zipalign。 Proguard 工具能够删除未调用的代码,并混淆代码中的类、函数和字段。 它能压缩您的代码以减少映射时所需的 RAM 页。 Zipalign 工具能够重新对齐您的 APK。 如果不运行 zipalign 的话,资源文件就无法从 APK 映射,因而会需要更多的内存。

如何避免内存泄漏

借助以上方法巧妙使用内存能让您的应用逐步受益,并且提高应用在系统中的存活时间。 但是,一旦发生内存泄漏,所有这些优势都不复存在。 下面是开发人员需谨记的一些常见潜在泄漏。

  1. 查询完数据库后切记关闭光标(cursor)。 如果您需要长期打开光标,那么您必须谨慎使用,并且在数据库任务结束时立即关闭。
  2. 记得在调用 registerReceiver() 后调用 unregisterReceiver()。
  3. 避免 Context 泄漏。 如果您在 Activity 中申请了一个静态成员变量 "Drawable",然后在 onCreate() 中调用 view.setBackground(drawable),那么由于 drawable 将 view 设置为回调,而 view 引用了已存在的 Activity (Context),因此即使在旋转屏幕后创建了一个新的 Activity 实例,之前的 Activity 实例也未从内存中释放。 泄漏的 Activity 实例则意味着占用大量的内存,也将极易导致 OOM。

    有两种方式可以避免这种泄漏:

    • 不要长期引用一个 context-activity。 对 activity 的引用期限应该与 activity 的生命周期保持同步。
    • 尝试使用 context-application 来代替 context-activity。
  4. 避免在 Activity 中使用非静态的内部类。 在 Java 中,非静态匿名类能够隐式引用外部类。 如果不小心存储了这种引用,会引起 Activity 驻留,妨碍对其进行垃圾回收。 因此,使用静态内部类,并弱引用内部的 activity 对象。
  5. 谨慎使用线程。 Java 中的线程是垃圾回收的根源,即 Dalvik 虚拟机 (DVM) 对运行时系统中的所有活动线程保持强引用,因此保持运行状态的线程将无法实现垃圾回收。 除非被 Android 系统明令关闭,或者整个进程被 Android 系统“杀死”,否则 Java 线程会一直存在。 与之不同,Android 应用架构提供了多种类,以便开发人员更轻松地管理后台线程:
    • 使用 Loader 代替线程,以执行短期非同步后台查询操作,同时协调 Activity 生命周期。
    • 使用 Service 并通过使用 BroadcastReceiver 向 Activity 返回结果报告。
    • 使用 AsyncTask 执行短期操作。

如何分析内存的使用情况

为了在线/离线均能了解更多关于内存使用量的信息,您可以通过在 Android Debug Bridge (ADB) 上使用 logcat 命令来检查 Android 的系统日志,或者捕捉转储内存信息并将其命名为特定包,或者使用 Dalvik 调试监测程序服务器 (DDMS) 和内存分析工具 (MAT) 等其他工具。下面是分析应用内存使用情况的一些方法简介。

  1. 充分了解有关 Dalvik 虚拟机的垃圾回收 (GC) 日志信息,具体示例和定义如下所示:

    • GC 的原因: 是什么触发了垃圾回收?回收类型是什么? 原因包括:
      • GC_CONCURRENT: 当您的堆开始填满时,触发并发垃圾回收以释放内存。
      • GC_FOR_ALLOC: 当您的堆已经占满时,您的应用又试图分配内存,所以系统必须停止您的应用并重新分配内存,此时便发生垃圾回收。
      • GC_HPROF_DUMP_HEAP: 当您创建 HPROF 文件来分析堆时,发生垃圾回收。
      • GC_EXPLICIT: 例如当您调用 gc() 时产生的显式垃圾回收(在需要时您不应该调用,而是应该相信垃圾回收器的运行作用)
    • Amount freed(释放的内存量): 本次垃圾回收所释放出来的内存。
    • Heap stats(堆数据): 空闲内存的百分比和(活动对象数量) / (堆总量)
    • External memory stats(外部内存数据): API level 10 及以下版本的外部空间分配内存量(分配内存量) / (发生垃圾回收的临界值)
    • Pause time(暂停时间): 堆越大,则暂停时间越长。 并发的暂停时间包括两次暂停:一次是垃圾回收之初,一次是回收接近完毕时。

    GC 日志越大,则您的应用中分配/释放的内存越多,同时也意味着用户体验会有所下降。

  2. 使用 DDMS 来查看堆更新并追踪分配记录。

    利用 DDMS 能够便捷地查看具体进程的实时堆分配情况。 尝试在 "Heap"选项卡中与您的应用进行交互,并查看堆分配的更新情况。 这能够帮助您识别哪些操作占用了过多内存。 "Allocation Tracker"选项卡展示了最近所有的内存分配,提供了许多信息,其中包括对象类型,分配所在的线程、类和文件以及线路等。 欲了解更多关于使用 DDMS 进行堆分析的信息,请参阅本文结尾的参考资料章节。 以下截图展示了运行中的 DDMS,其中包括当前进程的状况和具体进程的内存堆统计情况。

  3. 查看整体内存分配情况。

    通过执行 adb 命令: “adb shell dumpsys meminfo <package_name>”,您可以看到您所有应用的当前内存分配情况,单位为 KB。

    一般您只需关注 "Pss Total"和 "Private Dirty"栏即可。 "Pss Total"一栏包括所有的 Zygote 分配(如以上 PPS 定义所述,根据其在进程中的份额进行加权) "Private Dirty"数是指专门用于您应用的堆、您自己的应用以及从 Zygote 中分离应用进程以后进行修改的所有 Zygote 分配页的实际 RAM。

    此外,"ViewRootImpl"展示了进程中活跃的根视图数量。 每个根视图均与一个窗口相连,因此有助于您识别与对话框或其他窗口相关的内存泄漏。 "AppContexts"和 "Activities"则显示了当前活跃在您进程中的应用 Context 和 Activity 对象的数量。 这将十分有助于快速发现因对其进行静态引用而无法进行垃圾回收的 Activity 对象泄漏(这种情况十分常见)。 这些对象通常具有很多其他与之相关的内存分配,是一种追踪大规模内存泄漏的好方法。

  4. 捕捉堆转储 (Heap Dump) 文件并利用 Eclipse* 内存分析工具 (MAT) 对其进行分析。

    您可以通过使用 DDMS 或者在源代码中调用 Debug::dumpHprofData() 来直接捕捉一个堆转储文件,以获取更精确的结果。 然后您需要使用 hprof-conv 工具来生成转换的 HPROF 文件。 以下截图是 MAT 中显示的内存分析结果。

总结

为了创建更多内存友好型应用,Android 开发人员需要对 Android 内存管理有一个基本的认识。 开发人员应该践行有效的内存使用方法、使用分析工具并且实施本文所提供的方法。 在实施期间,最好先创建一个稳定而又可扩展的应用,而非专注于应用修复方法。

参考资料

  1. http://developer.android.com/training/articles/memory.html
  2. https://developer.android.com/tools/debugging/debugging-memory.html
  3. http://developer.android.com/training/articles/perf-tips.html
  4. http://android-developers.blogspot.co.uk/2009/01/avoiding-memory-leaks.html
  5. http://developer.android.com/tools/debugging/ddms.html
  6. http://www.curious-creature.com/2008/12/18/avoid-memory-leaks-on-android/comment-page-1/
  7. https://eclipse.org/mat/

关于作者

Bin Zhu 是软件和解决方案事业部 (SSG) 开发人员关系部门英特尔® 凌动™ 处理器移动支持团队的应用工程师。 他负责英特尔凌动处理器上的 Android 应用支持,并专注于研究针对 x86 Android 平台的多媒体技术。

在安卓* 系统中使用音频接口用于数据传输

$
0
0

概述

作为移动设备和平板电脑的一种接口,音频接口 (audio jack) 的主要功能是播放音乐。 但是,音频接口的另一个用途也不容忽视,即传输数据。

一直以来,整个行业都在开发利用音频接口来连接设备的更多用途。 iHealth 实验室的 Glucometer[1] (血糖检测仪)、Irdroid[2](提供红外遥控电视、机顶盒和音频组件功能)和 NFC 阅读器 Flojack*[3](具有近场通信功能,支持与 NFC 标签或移动设备进行交互传输)等外围设备的功能均可通过音频接口连接来实现。

可穿戴设备和外围设备拥有广泛的市场前景,因此我相信音频接口将会成为重要的数据通信门户。 在本文中,我将会更加详细地介绍这种新特性。

简介

音频接口有两种标准: OMTP 和 CTIA[4]。  OMTP 是一种国际性标准,而 ATIS 是美国标准,常用于 Apple iPhone* 和 iPad*。 两者的区别在于 V-Mic 和 GND 的位置,如图 1 所示。

OMPT 和 CTIA
图 1.   OMTP 和 CTIA

如何传输数据

当我们发送 0x00FF 数据值时,第一步就是将数字数据值转换成模拟信号。 我们需要调制数据值。 通常我们使用正弦波载波传输模拟信号。 

FSK 调制信号
图 2. FSK[5]调制信号[6]

第二步在 Android 系统中调用 audioTrack API[7] 功能,用于播放缓冲区的数据。 以下代码通过使用 audioTrack 功能将数据发送到缓冲区。

public void send(byte[] bytes_pkg) {
		int bufsize = AudioTrack.getMinBufferSize(8000,
				AudioFormat.CHANNEL_OUT_MONO,
				AudioFormat.ENCODING_PCM_16BIT);
		AudioTrack trackplayer = new AudioTrack(AudioManager.STREAM_MUSIC,
				8000, AudioFormat.CHANNEL_OUT_MONO,
				AudioFormat.ENCODING_PCM_16BIT, bufsize,
AudioTrack.MODE_STREAM);
		trackplayer.play();
		trackplayer.write(bytes_pkg, 0, bytes_pkg.length);
	}

如何接收数据

作为接收者,我们需要将模拟信号转换成数据值,解调信号以移除载波信号,并且通过协议对数据进行解码。 该协议可以是公共数据格式,也可以是自定义协议。 

解调信号
图 3. 解调信号6]

在 Android 系统中,我们使用 audioRecord API[8]功能来记录音频

public void receive(){
		int minBufferSize = AudioRecord.getMinBufferSize(AUDIO_SAMPLE_FREQ, 2,
				AudioFormat.ENCODING_PCM_16BIT);
		AudioRecord ar = new AudioRecord(MediaRecorder.AudioSource.MIC,
				AUDIO_SAMPLE_FREQ, AudioFormat.CHANNEL_IN_MONO,
				AudioFormat.ENCODING_PCM_16BIT, minBufferSize);
		ar.startRecording();
	   }

如何从音频信号中获取电能

毫无疑问,驱动支持音频接口外围设备的电路需要能源。 比如,左声道能够发送数据信息, 右声道发送持续的方波形或正弦波形。 这些波形能够转换成电能以支持 MCU(微控制器装置)和多个传感器。

案例研究:红外设备

Androlirc[9]是一个基于 Github 的项目, 它的功能是使用音频接口来发送红外指令。 我们可以通过研究这个项目来了解音频接口的数据通信。 Androlirc 使用 LIRC[10]库来创建一个数据缓冲区。 它是一个 Linux* 红外库,可支持多种接口类型,如 USB 和音频接口等。Androlirc 可以将 LIRC 库用于数据封装。 在市场上您会发现许多种红外编码类型,比如 RC-5 和 RC-56 协议。 在本例中我们将使用 RC-5 协议来控制电视。 首先,我们需要使用 38k 频率的正弦波形调制数据值,以生成缓冲区数据;然后使用 Android 音频 API 功能来播放音频缓冲区数据。 同时我们选择一个声道来播放正弦波形或方波形,以便为外围设备硬件提供电能。

案例研究:音频接口开发工具

恩智浦半导体公司 (NXP Semiconductors) 的新型智能手机解决方案 Quick-Jack*[11]是一款以 Hijack 项目为原型的工具/开发板。 Hijack[12]是密歇根大学的一个学生项目。 Hijack 平台能够支持即插即用的小型、廉价并且以手机为中心的新型传感器外围设备。 我们可以借助 NXP 的 Quick-Jack 开发板来设计我们的原型产品。 图 4 展示了一部智能手机利用基于音频接口的外围设备气温传感器而获取的室内气温。

通过 Quick-Jack 获取的气温值
图 4. 通过 Quick-Jack 获取的气温值

图 5 展示了如何利用基于 Android 的应用控制外围设备的 LED。

在 Quick-Jack 上控制 LED
图 5. 在 Quick-Jack 上控制 LED

总结

可穿戴设备和智能设备的外围设备如今在消费市场上日益普及。 具有数据通信功能的音频接口正在被越来越多的原始设备制造商 (ODM) 和 iMaker 采用。 或许,在未来音频接口数据通信功能会得到智能手机操作系统的普遍支持。

参考资料

  1. http://www.ihealthlabs.com/glucometer/ihealth-align/
  2. http://www.irdroid.com/
  3. https://www.kickstarter.com/projects/flomio/flojack-nfc-for-ipad-and-iphone
  4. http://en.wikipedia.org/wiki/Phone_connector_(audio)
  5. http://en.wikipedia.org/wiki/Frequency-shift_keying
  6. http://www.maximintegrated.com/en/app-notes/index.mvp/id/4676
  7. http://developer.android.com/reference/android/media/AudioTrack.html
  8. http://developer.android.com/reference/android/media/AudioRecord.html
  9. https://github.com/zokama
  10. http://lirc.org
  11. http://www.nxp.com/news/press-releases/2014/05/nxp-unveils-smartphone-quick-jack-solution-transforming-audio-jack-into-multi-purpose-self-powered-data-port.html
  12. http://web.eecs.umich.edu/~prabal/projects/hijack/

关于作者

Li Liang 拥有长春科技大学信号与信息处理专业硕士学位。 他于 2013 年加入英特尔,现在是中国 Android 移动支持团队的一名应用工程师, 专注于研究对 Android 平台的差异化支持,如多窗口功能 (Multi-Window) 等。

通过蓝牙* LE (BLE)从安卓*连接到英特尔® Edison

$
0
0

简介

蓝牙* LE (BLE) 通信因其低成本和低能耗等优势,被越来越多的用于商用产品和娱乐性应用。 如果希望实现 Android* 手机或平板电脑与英特尔l® Edison 或英特尔® Galileo 项目之间的通信,蓝牙* LE (BLE) 无疑是最佳选择。

本文旨在为您介绍如何使用免费的软件工具和低成本、即时可用的硬件编写代码和连接硬件,以构建英特尔 Edison 与配备蓝牙 4.0 的 Android 设备之间的 BLE 通信。

什么是 BLE?

蓝牙低能耗 (BLE),蓝牙 LE,或 BLE (也称智能蓝牙)是一种无线个域网技术,由蓝牙特别兴趣小组设计和市场推广。 它主要针对健康医疗、健身、安全、自动化和家庭娱乐等行业的应用。

蓝牙 LE 最初由诺基亚于 2006 年推出,当时称为 Wibree。 2010 年,通过蓝牙版本 4.0 ,蓝牙 LE 加入了蓝牙标准的行业。

相比于标准蓝牙连接,BLE 可显著降低设备的能耗,同时提供常规蓝牙的大部分连接性以及约一半的连接范围(大约 15 米/50 英尺)。 安装电池的设备如果使用蓝牙 LE,可在不充电或更换电池的情况下运行数年。 比如,Estimote 推出的 Beacon 设备宣称,其电池寿命可长达三 (3) 年(www.estimote.com)。

硬件

尽管我们的专注点在于英特尔 Edison,但本篇文章的大部分内容也适用于英特尔 Galileo。 就物联网项目而言,我们使用的物理传感器和控制来自于 Seeed Studio 的 Grove 系统。 具体来说,我们将使用:

  • 带有 Arduino breakout 开发板的英特尔 Galileo
  • Seeed Grove – Starter Kit Plus Intel® IoT Edition For Galileo GEN 2
  • Seeed Grove BLE
  • 运行 Android 4.3 或更高版本的 Android 设备(我使用 Lenovo TAB S8-50)
  • 面向开发且运行 Windows* 7 或 8 的 PC(我使用 Dell XPS12)

关于硬件的几点说明:

  1. Grove Starter Kit标注为针对英特尔 Galileo 而设计,但它同时也适用于 Edison。 您还可以单独购买 Grove 组件,但套件更加划算。
  2. 在开发过程中我使用的是 Lenovo Android 平板电脑,但其他运行 Android 4.3 并支持蓝牙 4.0 的 Android 设备也均可行。
  3. 我使用 Dell XPS12 为英特尔 Edison 和 Android 项目(以及本文)编写代码。 采用 Mac* 或 Linux* 系统也可进行开发。

软件

我使用下列几款免费的软件工具。 如欲查看本示例,您应根据需要下载并安装下列软件工具:

Windows、Mac 和 Linux 均可提供上述软件,不过我将特别介绍基于 Windows 的安装。

硬件详情

英特尔® Edison

英特尔® Edison 是低成本、通用型计算平台系列计划的首款硬件。 经过专门设计,它有助于快速、轻松地构建物联网项目的原型,同时还可提供面向商业化的产品就绪型路径。

英特尔® Edison 使用 22 纳米英特尔® SoC,后者包含运行速度高达 500MHz 的双核英特尔® 凌动™ 处理器。 它支持 40 个 GPIO,并以微小外形集 1GB LPDDR3 RAM、4 GB EMMC 存储,以及双频 Wi-Fi* 和蓝牙于一身。

就内在系统而言,Edison 运行完整 Linux 内核。为了发挥 Edison 的最佳性能,您可能希望编写硬件级 Linux 代码。

但 Edison Linux 还以 Linux 程序的形式包含 Arduino 实施。 简单来说,这意味着您可以编写熟悉的 Arduino sketch,并在 Edison 开发板上运行这些 sketch。 这就是我们下面要进行的操作。

如欲了解更多有关英特尔 Edison 的信息,请访问: http://www.intel.com/content/www/us/en/do-it-yourself/edison.html

Arduino Breakout 开发板

面向英特尔 Galileo 的 Arduino breakout 开发板包含两种用途。 第一,它提供更大的原型构建平台,以轻松访问 IO 针脚。 第二,它提供可兼容 Arduino 的硬件平台,这意味着我们能够同时使用标准 Arduino 屏蔽器和英特尔 Edison (与英特尔 Galileo 的用法类似)。 图 1 所示为安装于 Arduino breakout 开发板的 Edison。

图 1.安装于 Arduino breakout 开发板的英特尔® Edison

Grove Starter Kit Plus

该套件的全称为 “Grove Starter Kit Plus - Intel® IoT Edition for Intel® Galileo Gen 2 Developer Kit”,最初针对英特尔 Galileo 第二代开发板而设计。 幸运的是,它通过 Arduino breakout 开发板可完全兼容英特尔 Edison。

该套件(如图 2 所示)旨在借助传感器、制动器和屏蔽器简化运行和原型构建。 它包含一个可兼容 Arduino 的屏蔽器以及四 (4) 个标准化针脚连接器。 这些连接器供应给可连接线缆的 IO 端口,其中的线缆也可轻松连接套件内的传感器和控制器。 这意味着,您可轻松构建项目,无需摆弄小型电线,上拉/下拉电阻,也无需担心极性问题。

如欲了解更多信息或购买套件,请访问: http://www.seeedstudio.com/depot/Grove-starter-kit-plus-Intel-IoT-Edition-for-Intel-Galileo-Gen-2-p-1978.html

Grove 套件的生产商 Seeed Studios 在线提供许多有用资源。

具体而言,我推荐克隆或下载 Sketchbook Starter 库,具体请访问: http://Github.com/Seeed-Studio/Sketchbook_Starter_Kit_V2.0

请访问下列链接,为 Grove Wiki 页面设置书签: http://www.seeedstudio.com/wiki/index.php?title=Main_Page#Grove

图 2. Grove Starter Kit Plus - Intel® IoT Edition for Intel® Galileo Gen 2 Developer Kit

Grove BLE V1

我们将使用 Grove 蓝牙低能耗 v1 模块,该模块不包含在入门套件中,但可通过 Grove 屏蔽器和连接器线缆兼容针脚。 它还是一种成本相对较低的 BLE 附件,在本文撰写过程中,它的成本最低。

Grove BLE v1 的开发基于行业标准德州仪器 CC2540。 许多其他设备均使用这种芯片。 如果您有其他的 TI CC2540 BLE 模块,比如 RedBear BLE Mini,您可以轻松修改示例代码。

更多 Grove BLE v1 详情,请访问: http://www.seeedstudio.com/wiki/index.php?title=Grove_BLE_v1&uselang=en

请注意,英特尔® Edison 确实包含支持 Wi-Fi 和 蓝牙 4.0/BLE 的板载无线模块,但 Grove BLE 模块可大大简化软硬件的设置。 使用 Grove BLE(图 3)还意味着这些项目可轻松适应英特尔 Edison。

图 3. Grove BLE V1 模块

调试 Android 设备

BLE 支持已添加至Android 4.3(API 级别:18)。 您需要运行 4.3 或更高版本的设备来通过 BLE 进行通信。

如欲了解更多有关 Android BLE 的信息,请访问: https://developer.android.com/guide/topics/connectivity/bluetooth-le.html

如果之前没有 Android 开发经验,您需要在手机或平板电脑上启用开发人员选项,然后将其用于运行和调试软件。 打开设置应用,滚动至底部,选择 “About device”,然后点击构建编号七 (7) 次,以解锁开发人员选项。

现在,设置下方应显示 Developer Options,请务必确认 “USB debugging”。

如欲了解更多有关 Android 开发人员选项的信息,请访问: http://developer.android.com/tools/device.html

安装软件并开始编写代码!

面向英特尔® Galileo 的 Arduino IDE

您需要下载专门准备的 Arduino IDE 版本,以将 Sketch 部署至英特尔 Edison 或 Galileo。 本文撰写之时,当前的版本为 1.5.3,位于:

https://software.intel.com/zh-cn/articles/install-arduino-ide-on-intel-iot-platforms

英特尔® Edison 驱动程序

您还需要从上述链接下载和安装英特尔 Edison 驱动程序。 它应该位于 “Driver Software” 下方页面的最后一个链接。 本文撰写之时,其版本为 1.0.0。

如需其他说明,下列链接提供了有用的入门指南:

software.intel.com/iot/getting-started

Android Studio

Android Studio 是一款面向 Android 开发的全新 Java* IDE,基于 IntelliJ IDEA* (https://www.jetbrains.com/idea/)。 尽管目前还只是测试版,但它性能稳定,功能齐全。 如果您习惯使用面向 Android 开发的 Eclipse* 或 IntellliJ IDEA,那您应该可以轻松理解下列关于两者用法的演示。

Android Studio 包含 Android SDK,可大幅简化安装流程。 只需下载、从压缩文件中提取内容,然后在 bin 文件夹中运行 studio.exe 即可。

如欲了解更多有关 Android Studio 的信息,请访问: https://developer.android.com/sdk/installing/studio.html

Android SDK

你可能需要下载其他 SDK 软件包。 为此,请在 Android Studio 的工具栏中点击 "SDK Manager"。 关于配置 Android SDK,我们在此不做介绍,请访问下列链接,了解更多详情:

https://developer.android.com/sdk/installing/adding-packages.html

如果之前安装过 Android SDK,您可以配置 Android Studio,并将其指向正确的路径,如图 4 所示

在 Android Studio 中,点击 Configure -> Project Defaults -> Project Structure,并设置路径。

图 4.在 Android* Studio 中设置 SDK 路径

测试英特尔® Edison

在开始演示之前,请确保您能够运行 Blink示例 sketch。 它位于 examples -> 01.Basics -> Blink文件夹中的 Arduino IDE 下载软件包。

如欲了解更多信息,请参阅 Edison 入门指南:

https://communities.intel.com/community/makers/edison/getting-started

Android Hello World

安装 Android Studio 后,请确保您能够创建新项目,并在 Android 设备上运行该项目。

  1. 连接 Android 设备和 PC
  2. 打开 Android Studio
  3. 选择 "New Project…"
  4. 选择名称和位置,并点击 "Next" 3 次 (API 15/Blank Activity)
  5. 点击 Finish,并等待项目创建完成(可能耗时 20 多秒)
  6. 移动工具栏中的绿色 Play 图标
  7. 选择您的设备,并按下 "OK"

如果所有步骤都正确,您的 Android 屏幕应显示 “Hello world!”(图 5)

图 5. Android* Studio Hello World 应用

BLE 工作原理?

BLE 根据需要提供短数据包,然后关闭链路。 这是蓝牙 LE 实现低能耗的原因之一。 相比于常规蓝牙的传统配对方法,BLE 设备仅在需要收发信息时进行链接。

BLE 的通信方式极其严密。 设备显示收发数据的服务,后者包含称之为特征的内容,以定义可共享的数据。 如需获取更多详情,特征可包含描述符,帮助定义数据。 例如,您可以拥有一项标记为 "Heart Rate Monitor"的服务,该服务包含 "heart rate measurement"等特征。

大多数蓝牙 LE API 都支持搜索本地设备和发现有关这些设备的服务、特征和描述符。

BLE 关键术语和概念

下面简单介绍启动 BLE 项目之前应该了解的关键 BLE 术语和概念。

通用属性配置文件 (GATT)

GATT 配置文件是关于通过蓝牙低能耗链路收发短数据片(称为 "属性")的通用规范。 当前所有的 LE 应用配置文件均以 GATT 为基础。 蓝牙特别兴趣小组 (SIG) (https://www.bluetooth.org) 对 BLE 设备的配置文件数量进行了预定义。 这些配置文件是关于描述设备使用方法的规范。

属性协议 (ATT)

属性协议 (ATT) 指 GATT 的构建基础。 ATT 是专门针对 BLE 设备而设计的优化型协议。 ATT 通信发送字节尽可能少的数据。 所有属性均带有通用唯一标识符 (UUID),后者为标准的 128 位字符串 ID,以唯一的方式识别信息。 ATT 传输的属性被格式化为特征和服务(定义如下)。

特征

特征包含一个单独数值以及 0 或多个描述符(见下文)以描述特征的值。

描述符

描述符指定义了的属性,可描述特征值。 它们可能是人类可读的描述,可注明单位或测量,或定义可接受的数值范围。

服务

服务指特征的集合。 下列链接提供了基于现有 GATT 的配置文件列表: https://developer.bluetooth.org/gatt/services/Pages/ServicesHome.aspx

将数据从 Android 发送至英特尔® Edison

前提条件

本文余下内容将假设您有一个面向英特尔 Edison 和 Android 开发而设置和配置的开发系统。 请确保您已完成下列步骤,并根据需要检查之前的内容。

  • 安装英特尔 Arduino* IDE
  • 安装英特尔 Edison 驱动程序
  • 安装 Android Studio
  • 安装 Android SDK
  • 在英特尔 Edison上部署和运行 Blink 演示
  • 部署和运行空白 Hello world Android 项目

Android 中的 BLE

您可以访问下列链接,从 GitHub 下载完整的项目:

https://github.com/adrianstevens/Edison_to_Android_BLE/tree/master/Android/BLEConnect

但我建议您构建自己的项目,并参考上述链接逐行写入代码。

创建新项目

打开 Android Studio(或选择的 IDE),创建新的空白 Android 应用,并将其命名为 BLEConnect。 请务必将最低 SDK 设置为至少 API 18。 否则,您将无法使用 BLE API。

图 6.创建一个新的 Android 应用:

接下来打开 AndroidManifest.xml,并在 <application> 标签上方添加下列内容,以添加所需的权限:

<uses-permission android:name="android.permission.BLUETOOTH"/>
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/>

设置 UI

为了简单起见,我们只使用默认布局,但我们需要为 TextView 设置一个 ID。 打开 layout -> activity_main.xml,选择 TextView,并将 ID 设置为 mainText,以便我们在代码中引用该 ID。

图 7.设置 TextView 的资源 id

MainActivity

就该项目而言,代码其余部分将进入 MainActivity。 如果您逐行添加代码,那么请注意,Android Studio 将自动检测丢失的输入,并提示您添加。

示例代码将:

  1. 检查 Android 设备中的 BLE 支持
  2. 搜索附近的 BLE 设备
  3. 识别和连接 Grove BLE 模块
  4. 为已知通信服务搜索可用的服务
  5. 查找通信服务的传输特征
  6. 通过将数值写入特征来发送消息

我在此不一一介绍每行代码,仅介绍一些核心概念。

类别层变量和静态值

我们在连接 BLE 模块、搜索服务和发送消息的过程中保存了一些数值。 我们还将为 Grove BLE v1 (TI CC2540) 添加部分已知静态值。 如果您使用其他模块,可能需要进行调整。 具体而言,我建议定义收发特征,例如:

CHARACTERISTIC_TX = "0000ffe1-0000-1000-8000-00805f9b34fb"
CHARACTERISTIC_RX = "0000ffe1-0000-1000-8000-00805f9b34fb"

状态辅助方法

为了简单起见,我们在之前标记的 TextView 上显示我们的进度。 代码包含一种简单的辅助方法,名为 statusUpdate,帮助我们将状态消息写入屏幕和控制台。 它还可返回至 UI 线程,以便我们安全从任一线程调用该方法。

连接 BLE 设备

为引用 Bluetooth Manager,我们首先应查看设备是否提供 BLE,调用 getSystemService以引用 Bluetooth Manager (BluetoothManager),然后调用 getAdapter()方法,以引用 BluetoothAdapter项目。 或者,您还可以直接从 Bluetooth Manager 类使用静态帮助方法 getDefaultAdapter

搜索附近的 BLE 设备

搜索设备时,我们使用计时器搜索特定时间段。 我们调用 Bluetooth manager 项目中的 startLeScan,并传入至回调项目,以知晓是否搜索到设备。

API 持续扫描设备,因此,我们可能会在每台设备的 LeScanCallback中接收到多个通知,所以在保存之前,我们应确认该设备入口的唯一性。 我们还要检查模块的设备名称,并保存引用。 就本示例而言,我们实际上不需要将设备保存在列表中。


@Override
public void onLeScan(final BluetoothDevice device, final int rssi, byte[] scanRecord)
{…
}

查找通信服务

大多数 BLE 设备都会显示一个或多个服务以供通信/互动。  Grove BLE 中的 TI CC2540 包含一个 ID 为 “0000ffe0-0000-1000-8000-00805f9b34fb” 的关键服务。 接下来,我们将查找引用,并将其保存至该服务。

首先,我们需要连接设备。 为了知晓连接时间或查找到服务的时间,我们需要 BluetoothGattCallback项目,并覆盖 onConnectionStateChangedonServicesDiscovered

请注意,使用 onConnectionStateChanged方法时,如果获悉连接成功,我们可调用 mBluetoothGatt.discoverServices()搜索服务。 识别到所需的服务后,我们可以继续发送消息。


@Override
public void onConnectionStateChange (BluetoothGatt gatt, int status, int newState)
{…
}

@Override
public void onServicesDiscovered (BluetoothGatt gatt, int status)
{
	…
}

许多其他方法可以被覆盖。 请访问此处参阅其中的文档:

https://developer.android.com/reference/android/bluetooth/BluetoothGattCallback.html

发送消息

示例代码提供了一种 sendMessage方法。 我们通过 UDID 识别到所需的特征后,调用该特征的 setValue。 最后,我们调用 BluetoothGatt 引用中的 writeCharacteristic,从而输入特征值,以发送数据。

有许多 setValue过载,但实际上可用一种更简单的过载来发送字符串,不过由于大多数 BLE 通信以字节的形式发送命令,因此这种示例更加有用。

现在开始设置英特尔 Edison。

采用 Grove Breakout 开发板和 BLE 模块设置英特尔® Edison

首先组装基础硬件。 如果没有准备硬件,那么请将英特尔 Edison 安装至 Arduino breakout 开发板。

接下来,将屏蔽器底部的针脚对准 Arduino breakout 开发板,安装 Grove 屏蔽器。 然后连接 Grove BLE v1 和串行 UART 端口。

图 8. Grove 屏蔽器和 BLE 模块相连后的英特尔® Edison

我们的首个 Sketch

我们现在要在 Android 设备和英特尔 Edison 之间进行简单的串行通信。 但我们还希望看到收发的内容,因此我们使用 Arduino IDE 的内置 Serial Monitor。

下列链接将为您显示完整版 sketch:

https://github.com/adrianstevens/Edison_to_Android_BLE/tree/master/Sketches/SimpleSerial

打开英特尔 Arduino IDE,创建新的 sketch。 保存并将其命名为 “SimpleSerial”。 与其他 Arduino 兼容开发板不同,英特尔 Edison 提供两个串行端口。 在 Edison 通过 Grove BLE 收发数据的同时,这有助于我们实现 PC 与 Edison 的通信。 通过连接 PC 的 microUSB 可访问主串行 UART。我们使用连接至 BLE 模块的 Grove 屏蔽器上的 UART 连接器。

在英特尔 Edison 上部署后,我们的 sketch 可自动运行。 它首先运行 setup()函数,然后无限地连续调用 loop()函数。 这有助于我们读取和响应串行连接的输入。

初始化串行连接

Grove BLE 的默认通信速度为 9600 波特,因此,我们从该默认值开始。 我们需要配置两个串行端口,以使用该速度。 我们还需向 Grove BLE 发送多个 AT 命令,以将其重置为最新状态。 这些均可显示在 sketch 的 setup()函数中。

请注意,我们要首先配置 “Serial”,即 microUSB 端口 UART,然后是 “Serial1”,即连接至 Grove BLE 的 UART。

循环

我们在该 sketch 的所有操作均为读取一个串行端口的数据,然后将该数据发送至另一端口。 为此,我们将调用串行端口的 read()函数(为我们提供单一字符),然后调用另一串行端口的 print()

Edison 循环的速度非常快,因此我们可轻松达到 9600 波特。

部署 Sketch

现在请点击 Arduino IDE 的确认按钮(选框),设置好所有 typo。 确认后,请确保英特尔 Edison 已连接至 PC,并上传 sketch(右箭头)。 传输完成后,sketch 启动循环,然后我们可连接 Android 应用。 现在请打开 Arduino IDE 的 Serial Monitor(右上角的放大镜图标),然后可以开始收发数据了。

Sketch 基于英特尔 Edison 运行后,运行 Android BLEConnect 应用。 您应该可以看到 serial monitor 中显示消息 “Hello Grove BLE”。

如果无法运行,可能是 Android 应用的问题。 检查状态显示,它会告知故障点。

GitHub repo 中有一个 sketch,前者也可将消息显示于 Grove LCD。 请确保您 Grove 屏蔽器设置为 5V,并将 LCD 显示屏连接至任一 I2C 连接。

图 9.运行于 Android* 手机的 BLEConnect

图 10. Adruino IDE Serial Monitor 接收 BLEConnect 消息

展望未来

创建相对复杂的项目意味着将须将架构融入 Android 代码和 sketch。 我建议将大部分 Android BLE 代码移入服务,以从 UI 中提取,并简化多个活动和项目的使用。 创建高级 sketche 时,您将希望开始使用 Arduino Time Library,以便模拟运行多个循环,并同时接收数据 (http://playground.arduino.cc/Code/Time)。 我将有关示例添加至 GitHub 存储库 (https://github.com/adrianstevens/Edison_to_Android_BLE),并在下一篇文章中讨论这些概念。

关于作者

Adrian Stevens 拥有 14 年的移动应用开发经验,尤其擅长于 C# 和 C++ 交叉平台开发。 Adrian 精通用户界面架构、音频/信号处理、传感器和数学等领域。 Adrian 长期供职于加拿大温哥华,他对技术研究抱有极大的热情,并极具创业精神。 他同时还在 Meetup 上进行 C# 交叉平台开发。

Adrian 从 2001 开始为 Palm Pilot 和 Pocket PC 等平台开发移动应用。 后来成功创立了精品移动开发工作室。 目前,Adrian 担任移动和交叉平台应用方面的讲师,教授架构和开发策略。

索引


5种优化你的安卓5.0 Lollipop代码的方法

$
0
0

下载 PDF

简介

Android 5.0 Lollipop* 发布的同时,推出了名为 ART*(简称 Android 运行时)的 创新型默认运行时环境。 它包含多项增强功能,可有效提升性能。 本文将介绍 ART 相 比于前代 Android Dalvik* 运行时所具备的部分全新特性,并分享五个技巧,帮助开发 人员进一步提升应用性能。

ART 有何新特性?

基于 Dalvik 运行时分析 Android 应用时,最终用户会面临两个主要难题:应用启动 时间较长以及大量的 jank。 应用断续、不稳定或中止时都会出现 jank,这是因为帧设 置耗费的时间过长,导致应用与屏幕刷新率不一致。 相比于之前的帧,如果一个帧的速 度明显加快或减慢,该帧就会被认为出现异常。 用户将这种 jank 视作抖动,用户体验 的流畅性会因此比预期的差。 为解决这些问题,ART 增加了几项全新特性:

  • 提前编译: ART 在安装期间使用设备上 dex2oat 工具 编译应用,并针对目标设备生成可执行的应用。 通过对比,Dalvik 使用解释器和即时编 译,在安装期间将 APK 转换成优化型 dex 字节代码,并在应用运行的时候进一步将该优 化型 dex 字节代码编译成面向热路径的原生机器代码。 因此,尽管安装时间长一些,但 有助于加快应用在 ART 中的登录速度。 在 ART 环境中,应用还需使用设备更多的闪存 空间,因为安装期间编译的代码需占用额外空间。
  • 改进内存分配: Dalvik 上需要广泛分配内存的应用可 能会遭遇性能低下问题。 内存分配器中的独立大型对象空间和功能改进有助于解决这一 问题。
  • 改进垃圾回收: ART 的垃圾回收速度更快、规模更大, 可减少碎片数量并提高内存利用率。
  • 提升 JNI 性能。优化型 JNI 调用和返回代码顺序可减 少 JNI 调用时所使用的指令数量。
  • 64 位支持: ART 充分利用 64 位架构,有效提升了基 于 64 位硬件运行的应用性能。

由于这些特性,使用 Android SDK 编写的应用以及进行多个 JNI 调用的应用在用户 体验方面均得以显著提升。 用户还可因此延长电池续航时间,因为应用只需编译一次, 而且在常规使用过程中执行的速度更快,消耗的能源更低。

ART 与 Dalvik 性能对比

ART 首次在 Android KitKat 4.4 上以预览版发布时,其性能遭到了诟病。 但早期的 性能对比不太公平,因为对比的对象分别是早期版 ART 与成熟优化版 Dalvik,所以应用 在 ART 中的运行速度比 Dalvik 慢。

现在我们可以对比面向客户版本的 ART 和 Dalvik。 由于 ART 是 Android 5.0 内的 唯一进行时,并排对比 Dalvik 与 ART 只能基于对比近期从 Android KitKat 4.4 更新 至 Android Lollipop 5.0 的设备。 就本文而言,我们使用采用英特尔® 凌动 TM处理器的 TrekStor SurfTab xintron i7.0* 平板电脑进行测试,首先是 运行 Dalvik 的 Android 4.4.4,然后更新至运行 ART 的 Android 5.0。

由于我们对比的是不同版本的 Android 系统,Android 5.0 也可能会带来性能改进, 但基于内部性能测试,我们发现,ART 是实现性能改进的最主要原因。

我们运行性能指标评测,其中 Dalvik 能够积极优化重复执行的代码,预计能够带来 优势,以及英特尔自己的游戏仿真。

数据显示,在五项性能指标评测中,ART 的表现要优于 Dalvik,而且在几个案例中明 显超过了 Dalvik。

Relative Lollipop-ART to KitKat-Dalvik performance

如欲了解更多有关这些性能指标评测的信息,请访问下列链接:

IcyRocks 1.0 版是由英特尔开发的工作负载,用以模拟真实游戏应用。 它的大部分 计算都采用开源 Cocos2d* 函数库和 JBox2D*(一种 Java 物理引擎)。 它测量不同负载水平下每秒渲染的平均动画(帧)数 (FPS),然后通过求取这些 FPS 的平均数计算最 终的指标。 它还测量跳帧的程度(每秒的跳帧数),即不同负载水平下每秒抖动帧 数的平均值。 它显示了 ART 较之 Dalvik 所实现的性能改进:

Relative ICY Rocks  animations/second
IcyRocks 1.0版也显示ART比Dalvik更连贯地渲染画面而较少出现跳帧,从而获得更平稳的用户体验。

Relative ICY Rocks  JANK/second
基于这个性能评估,很显然ART相比Dalvik已经提供了更好的用户体验和更佳性能。

代码从 Dalvik 到 ART 的转换

代码从 Dalvik 到 ART 的转换非常透明,大多数运行于 Dalvik 的应用可直接运行于 ART,无需进行修改。 因此,用户升级至新的运行时后,许多应用都可实现性能改进。 最好测试带有 ART 的应用,尤其是使用 Java 原生接口的应用,因为ART 的 JNI 错误处理比 Dalvik 更严格,如本文所述

优化代码的 5 个技巧

得益于 ART 的上述功能改进,大多数应用将实现性能提升。 此外,您还可采用其他 实践进一步针对 ART 优化您的应用。 我提供了部分简化的代码,以证明下列各种技巧的 运行方式。

由于应用各不相同,其性能主要依赖于周围的代码和环境,因此无法为您提供明显的 性能提升指示。 不过我们将解释这些技巧能够提升性能的原因,并建议您在自己的代码 环境中测试这些技巧,以验证它们对性能的影响。

这些技巧的使用非常广泛,但在 ART 案例中,可通过 dex 文件生成二进制可执行代 码的 dex2oat 编译器将实施下列优化。

技巧 #1 – 尽可能使用本地变量,不要使用公共类域。

限制变量范围后,不仅可以增加代码的可读性,降低错误率,还可使代码更加便于优 化。

在下列未优化代码中, v 的值在应用运行的时候计算。 因为 v可绕过方 法进行访问,它的值可通过任意代码更改,因此在编译期间处于未知状态。 编译器不知 道 some_global_call() 操作是否更改了 v,因为任何代码都可绕过该方法更 改 v

在优化代码中,v是本地变量,它的值可在编译期间计算。 因此,编译器 能够直接将结果放在代码中,从而避免在运行时进行计算。

未优化代码

class A {
  public int v = 0;

  public int m(){
    v = 42;
    some_global_call();
    return v*3;
  }
}

未优化代码

class A {
  public int m(){
    int v = 42;
    some_global_call();
    return v*3;
  }
}

技巧 #2 – 使用关键词 final 提示该值为常量。

关键词 final可用于防止代码被意外修改应为常量的变量,还可通过提示 编译器该值为常量来提高性能。

在下列未优化代码中,v*v*v 的值必须在运行时计算,因为 v的 值可以更改。 在下列优化代码中,为 v赋值时,使用关键词 final 将告知编 译器,该值不会被更改,因此可在编译期间执行计算,并将计算结果添加至代码,从而无 需在运行时进行计算。

未优化代码

class A {
  int v = 42;

  public int m(){
    return v*v*v;
  }
}

优化代码

class A {
  final int v = 42;

  public int m(){
    return v*v*v;
  }
}

技巧 #3 – 使用关键词 final定义类和方法

因为 Java 中的所有方法具备多形态特征,声明方法或类的最终性可告知编译器,该 方法在任意子类中都不会被重新定义。

在下列未优化代码中,m()必须在执行调用之前明确。

在优化代码中,由于方法 m()已声明为最终方法,编译器知道应该调用哪 个版本的 m()。 这样可避免方法查找和行内调用,从而该方法的内容可替换 m()调用。 性能会因此显著提升。

未优化代码

class A {
  public int m(){
    return 42;
  }
  public int f(){
    int sum = 0;
    for (int i = 0; i < 1000; i++)
      sum += m(); // m must be resolved before making a call
    return sum;
  }
}

优化代码

class A {
  public final int m(){
    return 42;
  }
  public int f(){
    int sum = 0;
    for (int i = 0; i < 1000; i++)
      sum += m();
    return sum;
  }
}

技巧 #4 – 避免 JNI 调用小方法。

在很多情况下都可使用 JNI 调用,例如能够重复利用 C/C++ 代码库或函数库时,需 要跨平台实施时,或需要性能提升时。 但最好能够最低限度地减少 JNI 调用的数量,因 为调用的开销非常大。 如果 JNI 调用用于优化性能,其开销可能导致无法达到预期效果 。 尤其是频繁调用简短 JNI 方法可能会产生负面影响,而且将 JNI 调用放在循环中会 增加开销。

代码示例

class A {
  public final int factorial(int x){
    int f = 1;
    for (int i =2; i <= x; i++)
      f *= i;
    return f;
  }
  public int compute (){
    int sum = 0;
    for (int i = 0; i < 1000; i++)
      sum += factorial(i % 5);
// if we used the JNI version of factorial() here
// it would be noticeably slower, because it is in a loop
// and the loop amplifies the overhead of the JNI call
    return sum;
  }
}

技巧 #5 – 使用标准函数库,不要用自己的代码实施相同的功能

标准 Java 函数库经过了高度优化,通常使用内部 Java 机制获取最佳性能。 相比于 使用您自己的代码实施相同功能,这种运行方式的速度要快得多。 试图避免因调用标准 函数库而产生的开销实际上会导致性能下降。 在下列未优化代码中,有种自定义代码可 用来避免调用 Math.abs()。 不过,这种使用 Math.abs() 的代码运行速度会比较快,因 为在编译期间,Math.abs() 由 ART 中的优化型内部实施所替代。

未优化代码

class A {
  public static final int abs(int a){
    int b;
    if (a < 0)
      b = a;
    else
      b = -a;
    return b;
  }
}

优化代码

class A {
  public static final int abs (int a){
    return Math.abs(a);
  }
}

英特尔对 ART 的优化

英特尔与 OEM 合作提供优化版 Dalvik,基于英特尔处理器运行时可提升性能。 英特 尔同样投资于改进 ART,使其基于全新运行时进一步增强性能。  功能优化将通过 Android 开源项目 (AOSP) 提供,并/或直接通过设备厂商提供。 同先前一样,功能优化 对开发人员和用户是透明的,因此无需更新应用便可轻松获得优势。

了解更多信息

如需了解更多有关优化面向英特尔处理器的 Android 应用,以及英特尔® 编译器的信 息,请通过网址 https://software.intel.com访问英特尔开发 人员专区。

关于作者

Anil Kumar 在英特尔公司工作已超过 15 年,在软件与服务事业部担任过多项职务。 目前,他担任高级资深软件性能架构师,并在以下方面发挥着积极的作用:通过服务于各 大标准组织帮助完善 Java 生态系统、积极参与性能指标评测(SPECjbb*、SPECjvm2008 ,SPECjEnterprise2010 等)、通过提升用户体验和资源利用率改进客户应用,以及提升 面向软硬件配置的默认性能。

Daniil Sokolov 是英特尔软件与服务事业部的资深软件工程师。 过去 7 年,Daniil 一直专注于 Java 性能的各个领域。 目前,他主要致力于提升基于英特尔 Android 设备 的用户体验和 Java 应用性能。

Xavier Hallade 是英特尔软件与服务事业部常驻法国巴黎的开发人员宣传官,主要致 力于各种 Android 框架、函数库和应用,帮助开发人员增强对新硬件和新技术的支持。
他同时还是 Android 领域的谷歌开发专家,专注于 Android NDK 和 Android TV。

使用NexStreaming NexPlayer* SDK开发应用

$
0
0

下载文档

简介

NexStreaming 是一家全球移动软件公司,总部位于韩国首尔,并在西班牙、美国、日 本和中国设有分支机构。 该公司最受欢迎的产品 NexPlayer* SDK 是一款播放器 SDK, 集成于移动应用,被大多数著名的视频服务提供商所采用。 该播放器可兼容业内所有领 先的 DRM 技术。 而且,它还可与其他配套技术结合使用,比如广告植入、受众评测或音 频增强。 NexStreaming NexPlayer SDK 可提供音/视频解码和回放服务。 应用开发人员 可使用 SDK 快速地构建自定义高效多媒体播放器。 NexPlayer SDK 具有可靠、稳定的特 征,而且经证明可兼容国际标准。 本文将介绍如何使用 NexPlayer SDK 创建 x86 播放 器应用。

平台兼容性

NexPlayer SDK 面向 x86 优化,完全支持 x86 设备。 NexPlayer SDK 可支持:

  • Android* 1.6 或更高版本
  • mp4、3gp、avi、asf 和 piff 视频文件格式
  • HTTP Live Streaming 5.0 版、3GPP Progressive Download、AES128、 HTTPS 协议、h.264、AAC、AAC+、 eAAC+ 代码。 SDK 同时还可支持软件代码和硬件代码 。
  • .smi、.srt、.sub、3GPP 同步文本、TTML 隐藏字幕(仅 PIFF/CFF)、CEA 608 和 CEA 708 隐藏字幕、以及 Web Video Text Track (WebVTT)

如何使用 NexPlayer SDK 创建 x86 播放器应用

您需要向 NexStreaming 索要 SDK http://www.nexstreaming.com/downloads-sdk。 SDK 和演示应用 下载完成后,其中自动包含针对英特尔® 芯片组的优化。 SDK 中包含完整文档。 请参考 SDK 文档和示例,根据您的应用选择相应的 API。 使用 SDK 开发应用非常简单。 您可 以将示例代码用作指南。 借助 NexPlayer SDK 开发一个完整应用只需花费 1 小时。

为了将 NexPlayer SDK 集成至 x86 Android 应用,并确保借助基于英特尔® 设备的 NexPlayer SDK 实现最佳体验,您需要执行以下简单步骤。

  • 将 SDK/函数库/文件夹中包含的函数库拷贝至您项目的资产/x86 文件夹。
  • 将 SDK/函数库/文件夹中包含的函数库拷贝至您项目的函数库/x86 文件夹 。
  • 将 SDK/src 文件夹中包含的源文件拷贝至您项目的 src/com/nexstreaming/nexplayerengine 文件夹。

NexPlayer SDK 将检测变化,并通过函数库使用英特尔资源。 所有函数库进入目录后 ,SDK 将自动切换应用的 ARM 和 x86 版本。 如果您想集成最新版 NexPlayer SDK,只 需覆写上述函数库文件即可。

NexPlayer SDK 包含大量函数库,其中包括 DRM 函数库。 这些函数库位于应用/资产 /x86 目录。 所需的函数库包含引擎、解码器和渲染层:

  • libnexplayerengine.so
  • libnexalfactory.so
  • libnexadaptation_layer_for_dlsdk.so
  • libnexralbody_audio.so
  • libnexralbody_video_opengl.so
  • libnexral_nw_ics.so
  • libnexral_nw_jb.so
  • libnexcal_oc_ics.so
  • libnexcal_oc_jb.so
  • libnexcralbody_mc_jb.so
  • libnexcal_in_aac_x86.so
  • libnexcal_in_mp3_x86.so
  • libnexcal_in_amr_x86.so

部分函数库名称中包含的缩写 “ics” 和 “jb” 分别表示 Ice Cream Sandwich 和 Jelly Bean。 如果您的应用仅支持部分 Android 版本,您可以 将不支持操作系统版本的函数库删除。

支持编解码器的函数库包括:

  • libnexcal_h364_x86.so - video lib for H.264
  • libnexcal_aac_x86.so - audio lib for AAC, AAC-Plus, and HE- AAC
  • libnexcal_mp3_x86.so - audio lib for MP2 and MP3

下列函数库支持文本说明:

  • libnexcal_3gpp_x86.so - for 3GPP timed text captions
  • libnexcal_closedcaption_x86.so - for CEA 608 and CEA 708 closed captions
  • libnexcal_ttml_x86.so - for TTML (CFF) timed text captions
  • libnexcal_webvtt_x86.so - for WebVTT text tracks

为了缩小应用,您可以只包含应用所需的函数库。

应用/函数库/x86 中的函数库需要加载至面向应用文件的 Java* 源代码中的 InitManager()。 例如,针对 “NexHDSample”,将相应的 x86 函数库添加 至 initManager() 中的 app/src/NexHDManager.java:

System.loadLibrary("NexHTTPDownloaderSample_jni");

如何使用 SDK 在屏幕上显示 x86 应用的视频

有两种方法可以用来通过 NexPLayer SDK 显示视频: NexVideoRenderer 和 OpenGL* 渲染器。 我们推荐使用 NexVideoRenderer 显示视频。 NexVideoRenderer 根据设备和 操作系统版本选择最合适的渲染器,从而显著降低表面处理和视频渲染的复杂性。 实施 NexVideoRenderer 需要采取下列步骤:

  1. 将环境 (android.content.Context) 传递进构建程序。
  2. 设置监听器(NexPlayer.IListener 和 NexPlayer.IVideoRendererListener)。
  3. 创建 NexPlayer 实例。
  4. 执行针对 NexPlayer (NexPlayer.setNexALFactory and NexPlayer.init) 的必要设置。
  5. 通过 NexPlayer 实例调用 init (NexVideoRenderer.init)。
  6. 将 NexVideoRenderer 实例作为视图添加至您的布局。

该渲染器的完整示例位于 NexPlayerSample/src/com.nexstreaming.nexplayerengine/NexVideoRenderer.java。

实时流媒体播放

HTTP 实时流媒体播放支持多个音频流和视频流。setMediaStream() API 支持内容播 放的同时从用户界面选择这些流媒体。 它由 SDK 提供支持,本文对此的提及仅供您参考 。 它主要提供三种使用案例:

  1. 包含可替换音频的随机播放列表。 在本案例中,视频和音频可以独立选择 。
  2. 包含可替换视频的随机播放列表。 在这里,每个记录都包含音频和视频, 但也提供可替换视频流(例如,相同内容不同的摄像镜头角度或视角)。
  3. 包含可替换视频和音频的随机播放列表组合。 该使用案例是上述两种案例 的结合,主视频流提供不同比特率的视频记录,但包含相同的音频,并提供独立音频记录 ,以支持可选语言选择。

x86 NexPlayerDemoApp 的 PnP 分析

在下列分析中,我评估了本地 mp4 文件的开始和闲置状态,以及示例应用 (“NexPlayerDemoApp”) (通过 NexPLayer SDK 开发)中示例流媒体的回放 。 该分析基于 Android 4.4.2 (KitKat) 执行,所使用的工具包括基于英特尔® 凌动™ 处理器的平板电脑 Z3740(速度为 1.6 GHz、配备英特尔® 高清显卡(第七代),并连接 Wi-Fi* )中面向 Android 的 VTune™ 分析器和英特尔® SoC Watch。

基于英特尔® 凌动™ 处理器的平板电脑 Z3740基准


表 1.

闲置状态下的 NexPlayerDemoApp


表 2.

应用闲置期间,基于英特尔® 凌动™ 处理器的平板电脑 Z3740 处于较高的 C0 时间将 导致能耗增加,但 NexPlayerDemoApp 未出现这种情况。 数字非常接近 Z3740 基准。

开始状态下的 NexPlayerDemoApp


表 3.

数字非常接近 Z3740 基准。

视频回放

视频回放期间,x86 版 NexPlayerDemoApp 的 CPU 平均利用率为 33%。 这一行为未 显示异常,并在整体上与内核保持一致。


图 1. x86 NexPlayerDemoApp.的视频回放时间视图。

实时流媒体播放

实时流媒体播放期间,x86 版 NexPlayerDemoApp 的 CPU 平均利用率为 25%。 这一 行为未显示异常,并在整体上与内核保持一致。


图 2. x86 NexPlayerDemoApp.的实时流媒体播放时间视图。

电池消耗

实时流媒体播放(占大部分时间)、视频回放和 x86 版短期闲置期间,x86 NexPlayerDemoApp 的耗电量为~3.8W。

结论

借助 NexPlayer SDK 开发应用既快速又简单,而且经过验证能够在 x86 移动设备上 实现高效性和低功耗等特点。

关于作者

Lana Lindberg 是英特尔软件和解决方案事业部 (SSG)、开发人员关系部门、英特尔® 凌动™ 处理器高接触性软件支持团队的一员。 加入 SSG 之前,Lana 是超便携事业部图 形软件验证团队的 OpenGL ES 测试开发人员。

参考资料和有用链接

  1. 面向 Android 6.28 版的 NexStreaming NexPlayer 软硬件 SDK 技术参考 手册。 NexStreaming 公司,2015 年 1 月 8 日。
  2. NexStreaming 网站 www.nexstreaming.com

ART vs Dalvik* - 介绍全新的安卓* x86运行时

$
0
0

Android* 5.x 一项最显著的变化是迁移至新的应用执行方法,称为 Android 运行时 (ART)。 Android 4.4 (KitKat) 发布后,随即提供了 ART 选项。 KitKat 用户可以选择 使用 ART 或其前任 Dalvik。 现在,ART 是 Android Lollipop 中唯一的运行时环境。

运行相同 Dex 字节代码时,ART 和 Dalvik 能够兼容,因此针对 Dalvik 开发的应用 基于 ART 运行的时候也可有良好表现。 本文将介绍 ART 的不同之处

让我们来看看 ART 的主要特性。

提前编译

ART 与 Dalvik 之间的主要区别是其具有提前 (AOT) 编译模式。 根据 AOT 概念,设 备安装应用时,DEX 字节代码转换仅进行一次。 相比于 Dalvik,这样可实现真正的优势 ,因为 Dalvik 的即时 (JIT) 编译方法需要在每次运行应用时都进行代码转换。 这里有 一篇文章能够为您提供更多关于 AOT 编译如何影响用户设备的性 能、电池续航时间、安装时间和存储空间的信息。

垃圾回收

ART 的另一项改进是内存管理。 垃圾回收 (GC) 对性能来说非常关键,因为它会影响 用户体验。 ART 的垃圾回收功能包含一些增强特性,要优于 Dalvik 的 GC。

首先,全新的 GC 功能列举出所有分配的对象,而且只需暂停一次就可标记所有可达对象,而 Dalvik 的 GC 需要暂停两次。

其次,并行化标记-清除算法有利于显著缩短应用暂停时间。

第三,在部分需要清理近期分配的短时对象的案例中,ART 缩短了总体 GC 时间。

第四,ART 能够更加及时地执行并发 GC。 因此,如果内存堆已满,尝试分配内存时 ,应用无需关闭。

关于内存管理的最后一个变化是紧凑型 GC 特性的出现。 有时出现 OutOfMemoryError 不是因为应用内存耗尽,而是没有大小合适的独立块来处理应用请求 。 针对这种错误,Android 开源项目 (AOSP) 正着手开发面向 ART 的紧凑型 GC。 紧凑 型 GC 能够合并释放在一个内存区的单个块,这样能够轻松分配。

这种特性非常有用,但目前还处于开发阶段,有一定的局限性,尤其是针对带有 Java* 原生接口 (JNI) 的应用。 Android 建议开发人员避免实施非兼容操作,并更多地 关注指示器。 同时,最好使用 CheckJNI 捕捉潜在错误。

开发和调试

最后一项 ART 增强特性关于应用开发和调试。

ART 增加了对专用取样分析器的支持。 过去,通常采用面向执行日志的图形查看器( 称为 TraceView)分析 Android 应用。 但使用这种工具会对运行时性能产生不利影响。 取样分析器测量操作系统中断。 因此,使用取样分析器对应用的干扰更少,还有许多相 比于其他方法的副作用。 这种添加至 TraceView 的全新专用分析器现在可提供准确的应 用行为图,同时应用继续以自然速度运行,不会出现明显的速度下降。

而且,ART 支持一些全新的调试选项,比如监控锁定,计算部分类中的实时实例,设 置现场观测点以在出现特定事件时关闭应用,等等。

可加速性能的 ART 还具备另一项功能改进,即异常和崩溃报告中的诊断详情更加清晰 明了。 最新版 Dalvik 针对 java.lang.ArrayIndexOutOfBoundsException 和 java.lang.ArrayStoreException 扩展了异常详情。 ART 可提供详细的 java.lang.ClassCastException、java.lang.ClassNotFoundException 和 java.lang.NullPointerException 信息。

java.lang.NullPointerException: Attempt to write to field 'int android.accessibilityservice.AccessibilityServiceInfo.flags' on a null object reference

java.lang.NullPointerException: Attempt to invoke virtual method 'java.lang.String java.lang.Object.toString()' on a null object reference

如欲了解更多信息,请参阅: 验证 Android 运行时 (ART) 上的应用运行情况

总结

ART 实现了相对于 Dalvik 的显著改进,包括本文介绍的 AOT 编译、垃圾回收、开发 和调试改进,以及其他特性。 由于现代技术的支持,设备能够配备多核、大内存和存储 容量,从而满足 ART 要求。 而且,部分 ART 功能(比如紧凑型 GC)目前正处于开发过 程中。 全新运行时始终处于谷歌开发的前沿,其功能会得到进一步的扩展和升级。

参考资料

为基于英特尔® x86 平台的安卓*应用编译ZeroMQ库文件

$
0
0

下载Compiling ZeroMQ library for Android x86 Platform.pdf

简介

ZeroMQ 是一种被全球编程人员广泛采用的开源函数库。 在维基百科中,其定义为: “ZeroMQ(也可拼写为 ØMQ、0MQ 或 ZMQ)是一种高性能异步信息传输 函数库,使用对象为可扩展分布式或并发应用。 它提供消息队列,但与面向消息的中间 件不同,ZeroMQ 系统能够在没有专用消息代理的情况下运行。 该函数库可提供熟悉的插 槽式 API。”

在 ZeroMQ 网站,社区为 Android 平台提供了构建指令 (http://zeromq.org/build:android)。 然而,这些指令以 ARM* 架构为主,因此,当开发人员尝试通过下列说明逐步构建 x86 版 ZeroMQ 函数库时,会遇到构建故障问题。 这就是本文撰写的原因所在。

构建环境准备

构建 ZeroMQ 的首要目的是为依赖 Android NDK 的 x86 创建独立的单机版工具链。

  1. 下载并安装最新版 Android NDK:
    http://developer. android.com/tools/sdk/ndk/index.html。 在本文中,我们使用 android-ndk- r10d-linux-x86_64。
  2. make-standalone-toolchain.sh 中的已知问题可以通过 https://code.google.com/p/android/issues/detail?id=74145.中提到 的补丁加以修复。 如果不使用该补丁,下一步中的构建将无法进行。
  3. 生成面向 Android x86 平台的单机版工具链。

    ./home/xxx/android-ndk-r10c/build/tools/make-standalone-toolchain.sh -- arch=x86 --toolchain=x86-4.9 --install-dir=/home/xxx/android-standalone- toolchain-x86

    您可以采纳下列两点建议避免出现意外的构建问题。
     
    1. 在单独目录中生成一个 x86 工具链。 不要将 x86 工具 链和 ARM 工具链混合在一个目录中。
    2. 在您的 /home 目录中安装单机版工具链,以便避免 root/no-root 授权问题。
  4. 配置环境变量。

    export PATH=/home/xxx/android-standalone-toolchain-x86/bin:$PATH export OUTPUT_DIR=/home/xxx/tmp/zeromq-android
     

构建面向 Android x86 的 ZeroMQ

下列步骤将引导您构建 ZeroMQ,以及 Jzmq 及其相邻 JAR,以便直接在 Android  中使用并加载至 APK 文件。

  1. 下载源代码并构建 ZeroMQ 3.x。

    mkdir /home/xxx/tmp cd /home/xxx/tmp git clone https://github.com/zeromq/zeromq3-x.git cd zeromq3 -x/ ./autogen.sh ./configure --host=i686-linux-android --prefix=$OUTPUT_DIR LDFLAGS="-L$OUTPUT_DIR/lib" CPPFLAGS="-fPIC -I $OUTPUT_DIR/include" --enable-static --disable-shared LIBS="- lgcc" make make install
  2. 下载源代码并构建 jzmq。

    cd /home/xxx/tmp git clone https://github.com/zeromq/jzmq.git cd jzmq/ ./autogen.sh ./configure -- host=i686-linux-android --prefix=$OUTPUT_DIR LDFLAGS="-L $OUTPUT_DIR/lib" CPPFLAGS="-fPIC -I$OUTPUT_DIR/include" -- disable-version --with-zeromq=$OUTPUT_DIR LIBS="-lpthread -lrt" make make install
    如果发现 “./configure” 执行过程中出现故障 ,请删除 LIBS="-lpthread –lrt"并重新尝试。
  3. 将 libjzmq.so 和 zmq.jar 文件拷贝至目标目录,至此 ZeroMQ 已成功构 建!
    cp /home/xxx/tmp/zeromq-android/lib/libjzmq.so /home/xx/tmp/ cp /home/xxx/tmp/zeromq-android/share/java/zmq.jar /home/xx/tmp/

Android x86 中 ZeroMQ 的使用示例

现在,您可以在 Android 应用中使用 ZeroMQ 了。 下面我们举例说明如何使用 ZeroMQ 执行 ZeroMQ 客户端与 ZeroMQ 服务器之间的消息互动。 部分示例代码来自于 ZeroMQ 官方指南,并打包至 Android 项目。

  1. 将 libjzmq.so 拷贝至目录 “DemoJZMQ/jni/”,并将 zmq.jar 拷贝至目录 “DemoJZMQ/libs/”。
  2. 将下列脚本添加至 Android.mk
    LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) LOCAL_MODULE := libjzmq LOCAL_SRC_FILES := libjzmq.so include $(PREBUILT_SHARED_LIBRARY)
  3. 将下列脚本添加至 Application.mk。
    APP_ABI := x86
  4. 执行服务器端函数。
     
     public void startZMQserver() throws Exception {
      ZMQ.Context context = ZMQ.context(1);
    
      // Socket to talk to clients
      responder = context.socket(ZMQ.REP);
      responder.bind("tcp://*:" + SERVER_PORT);
    
      printLog("Server", "Started");
      while (!Thread.currentThread().isInterrupted()) {
       // Wait for next request from the client
       byte[] request = responder.recv(0);
       printLog("Server", "Received:" + new String(request));
    
       // Do some 'work'
       Thread.sleep(1000);
    
       // Send reply back to client
       String reply = "World";
       responder.send(reply.getBytes(), 0);
       printLog("Server", "Response send.");
      }
      responder.close();
      context.term();
     }

     
  5. 执行客户端函数。
     
     public void startZMQclient() throws Exception {
      ZMQ.Context context = ZMQ.context(1);
    
      // Socket to talk to server
      printLog("Client", "Connecting to ZMQ server…");
    
      requester = context.socket(ZMQ.REQ);
      requester.connect("tcp://" + SERVER_IP + ":" + SERVER_PORT);
    
      printLog("Client", "Connected to server.");
      for (int requestNbr = 0; requestNbr != 10; requestNbr++) {
       String request = "Hello";
       printLog("Client", "Sending Hello " + requestNbr);
       requester.send(request.getBytes(), 0);
    
       byte[] reply = requester.recv(0);
       printLog("Client", "Received " + new String(reply) + ""
         + requestNbr);
      }
      requester.close();
      context.term();
     }

     
  6. 在设备上运行示例应用,首先点击 “start server” 按钮,然 后点击 “start client”。 您可以通过 “adb logcat” 查看执 行结果(图 1),结果显示,客户端向服务器发送 “Hello”,服务器向客户 端回复 “World”,持续了 10 次。

    图 1. Demo JZMQ 开始屏幕以及日志文件示例。

总结

本文逐步介绍了如何编译面向 Android x86 平台的 ZeroMQ 函数库。 并列举了消息 传输示例,以介绍如何在 Android 应用中使用 ZeroMQ。 如上所述,成功编译面向 x86 的 ZeroMQ 的关键是为基于 Android NDK 的 x86 生成正确的单机版工具链。 这种 方法不仅适用于 ZeroMQ,而且适用其他第三方函数库的构建。

参考资料

  1. ØMQ - http://en.wikipedia.org/wiki/%C3%98MQ
  2. 面向 Android 的 ZeroMQ 构建 - http://zeromq.org/build:android
  3. ØMQ - 指南 - http://zguide.zeromq.org/page:all

关于作者

Bin Zhu 是软件和解决方案事业部 (SSG) 开发人员关系部门英特尔® 凌动™ 处理器移 动支持团队的应用工程师。 他负责英特尔凌动处理器上的 Android 应用支持,并专注 于研究针对 Android x86 平台的多媒体技术。

将安卓Logcat消息与VTune Amplifier时间轴联系起来

$
0
0

简介

      Android logcat 是一款非常强大的调试工具。 借助  Android logcat,我们可以查看系统或应用的许多有用信息。 Android 提供关于日志的 标准化 API。 在我们的开发过程中,我们可以轻松添加日志,并使用 logcat 查看项目 日志。 面向系统的 VTune™ Amplifier 是一款用于系统或应用性能调优的分析工具。 VTune Amplifier 提供功能强大的时间轴面板,以便开发人员查看当时的性能矩阵。

      如何建立 logcat 消息与 VTune Amplifier 时间轴面板之间 的相关性? 开发人员通过 logcat 查看日志,或通过 VTune Amplifier 时间轴面板查看 其他性能矩阵合情合理。 这么做,我们可以了解特定时间的情形,以及当时的相关性能 数据。

      英特尔® VTune™ Amplifier 可以处理和整合通过定制采集器 或您的目标应用从外部收集的性能统计数据,同时执行本地 VTune Amplifier 分析。 为 此,需要借助预先定义的结构以 csv 文件的方式提供收集到的自定义数据,并将该文件 加载至 VTune Amplifier 结果。

      VTune Amplifier 用户指南详细介绍了如何创建包含外部数据 的 CSV 文件。 打开 VTune Amplifier Help 文档,前往 Intel VTune™ Amplifier > User’s Guide > External Data Import,我们可以看到包含外部数据的 CSV 文件,以及 CSV 文件格式示例。 如欲查看 VTune 时间轴面板中的 logcat 消息,请按 照指南将 logcat 消息改成 CSV 文件,并在 VTune Amplifier 中加载消息。

示例

举个例子, 我有一个 JAVA 应用,“com.example.Thread1”。 应用中的 某个函数包含多项计算。 伪代码如下所示:

void myfunction ()
{
        Log.v(“MYTEST”, “start block 1”);
        {
		...//my computation block 1
	}
        Log.v(“MYTEST”, “start block 2”);
        {
		...//my computation block 2
	}
	Log.v(“MYTEST”, “my computation finished”);
}

    如下列 Vtune Amplifier 时间轴面板所示,我们看到该函数执行了 6 次。 应用的主线程 ID 为 12271。 棕色栏显示的是我们在 Tune Amplifier 分析期间收 集的性能数据(CPU 时间)。

就每次函数执行来说,如果我们收集带有 “logcat -v threadtime” 的 logcat 消息,结果则如下所示:

01-12 11:13:19.090  2174  2174 V MYTEST  : start block 1
01-12 11:13:19.260  2174  2174 V MYTEST  : start block 2
01-12 11:13:19.500  2174  2174 V MYTEST  : my computation finished

现在,我们使用可加载至 VTune Amplifier 的格式将消息从 logcat 转化成 csv 文 件。 根据 VTune Amplifier 的文档,我们拥有下列 csv 文件,例如:

name,start_tsc.UTC,end_tsc,pid,tid
V/MYTEST : start block 1,2015-01-12 03:13:19.090,2015-01-12 03:13:19.090,2174,2174
V/MYTEST : start block 2,2015-01-12 03:13:19.260,2015-01-12 03:13:19.260,2174,2174
V/MYTEST:my computation finished,2015-01-12 03:13:19.500, 2015-01-12 03:13:19.500, 2174, 2174

此处我们将日志标记和消息字符串用作“名 称”。 我们将消息时间用于 start_tsc.UTC 和 end_tsc,并将 logcat 中的进程 ID 和线程 ID 用于 pid 和 tid。 字段通过逗号隔开。

在 VTune Amplifier “Analysis Type” > “Import from CSV” 中选择我们创建的 csv 文件,VTune Amplifier 将加载数据,并显示相关 logcat 消息 和该时间轴上的性能数据。 查看下列输出的截图,在消息点(黄色小三角)上移动鼠标 ,查看关于我们应用的 logcat 的所有消息。

下面介绍几点创建 CSV 文件的技巧。

  1. logcat 的时间就是与特定时区相关的 Android 操作系统的时间。 在 VTune Amplifier 接受的 CSV 文件中,该时间需要更改为 UTC 时间。
  2. CSV 文件名称应注明主机名称,定制采集器在该主机中收集数据。 例如: [user-defined]-hostname-<hostname-of-system>.csv. 就  Android 目标而言,您可以通过读取 adb shell 的文件 /proc/sys/kernel/hostname, 获取主机名称。
  3. 您可以在 CSV 文件的“名称”栏中自定义字符串。 在上述截图 中,我将整个 logcat 消息行作为“名称”。 请注意,如果 logcat 消息中 出现逗号,您需要删除“名称”字符串中的逗号。 逗号 “,” 是 保留的分隔符,不应出现在名称字符串中。
  4. 您还可以将譬如从 “dmesg” 传来的内核信息转化成 CSV 文件 ,并在 VTune Amplifier 时间轴面板中查看内核日志。 这点非常适用于系统层开发人员 ,例如,开发内核模块或设备驱动程序。 进程 ID 和线程 ID 应根据内核的消息指定为 0. 请注意,譬如从 “dmesg” 传来的内核信息时间戳是系统启动时的时间偏 移(秒)。 生成消息时,您需要将该时间戳转化为 UTC 时间。 例如,使用该方法获取 消息时间: “time of message” = “now” - “system uptime” + “timestamp of the message”。 此处的 “now” 表示系统的当前时间。 您可以借助 adb shell 的 “date” 命令获取 “now” 时间。 “system uptime” 表示系统启动耗费的时间(秒)。 您可以通过文件 /proc/uptime 获取 该时间。

 

脚本

我创建了一个试验性 bash 脚本,以简化操作。 您可以使用脚本 “logcat2vtune.sh” 收集 logcat 数据,并自动生成所需的 CSV 文件。 该 脚本可收集 logcat 和/或内核消息、读取目标系统信息、解析日志,并自动将日志转化 成 CSV 文件。

使用该脚本文件时,您需要 Linux 主机本地提供的 bash 环境。 如果您运行 Windows 主机,我建议您安装 Cygwin* ,它 可在 Windows 系统上提供 Linux 形式的 bash 环境。 下面是在 VTune Amplifier 分析 期间获取 CSV 文件的基本步骤。

  1. 在 shell 命令中,确保您可以访问 “adb”、采用合适的选项 启动 logcat2vtune.sh 脚本,例如 > ./logcat2vtune.sh -c logcat -g MYTEST
  2. 开始收集 VTune Amplifier 性能数据。 这一步可通过 VTune Amplifier GUI 或命令行完成。
  3. 停止收集 VTune Amplifier 性能数据。
  4. 在 logcat2vtune.sh shell 窗口中按任意键停止日志收集。 该脚本将读取 收集的日志数据、借助 bash 正则表达式解析日志,并将日志转化成 CSV 文件。 您可以 在当前 shell 文件夹中查找 .csv 文件。
  5. 将 CSV 文件加载至 VTune Amplifier,并通过 VTune 时间轴视图检查日 志消息。

下面提供几种脚本文件的常见使用模式。

$logcat2vtune.sh -c logcat -g MYTEST

收集 logcat 数据、使用字符串 “MYTEST” 过滤 logcat 消息,并生成 CSV 文件。 使用脚本中的以下命令 收集 logcat。
      $adb shell logcat -v threadtime
“MYTEST” 是用来过滤 logcat 消息的字符串。 强烈推荐使用过滤 ,因为 logcat 可能获取非常大的日志,但我们只关注自己进程的日志。 过滤可以是  logcat 标记名称的字符串、进程 ID、线程 ID,或其他字符串。 您可以使用逗号 “,” 注明多个字符串。 通过 “-g” 选项匹配任意字符串的日 志将被解析,并生成至 CSV 文件。

$logcat2vtune.sh -c dmesg -g “MYDRIVER”

收集 “dmesg” 输出、借助 “MYDRIVER” 字符串过滤,并生成 CSV 文件。

$logcat2vtune.sh -c logcatk -g MYTEST,MYDRIVER

同时收集 logcat 数据和内核消息数据,通过字符串 “MYTEST”或 “MYDRIVER” 进行过滤,并生成 CSV 文件。 在本 案例中,您可以在 VTune Amplifier 时间轴面板中查找 logcat 的用户层日志,以及 “dmesg” 的内核层日志。 这些日志通过脚本使用以下命令从内部收集:
>adb shell logcat -v threadtime -f /dev/kmsg | adb shell cat /proc/kmsg
在本案例中,您可以借助 TID 0 查看 vmlinux 的内核日志,以及 logcat 中面 向 TID 1922 的用户层日志。


 

请使用命令 “logcat2vtune.sh -h” 获取关于脚本使用的更多详情。 您可以根据自己的 需要定制脚本。 请注意,该脚本为试验性脚本,未经过完全验证。 如果在脚本使用中遇 到任何问题,请随时告诉我。

 

本文适用于:
产品: 英特尔 System Studio 2015、英特尔 System Studio 2016
主机操作系统/平台: Windows(IA-32、英特尔 64)、Linux* (IA32、英特尔 64)
目标操作系统/平台:  Android*

Viewing all 172 articles
Browse latest View live


<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>