65.9K
CodeProject 正在变化。 阅读更多。
Home

在 Arduino Create 中使用 Intel® 数学内核库

emptyStarIconemptyStarIconemptyStarIconemptyStarIconemptyStarIcon

0/5 (0投票)

2018 年 3 月 22 日

CPOL

14分钟阅读

viewsIcon

18137

本文档介绍了使用 Intel® 数学内核库 (Intel® MKL) 的用例并提供了示例。

概述

本文介绍了使用 Intel® 数学内核库 (Intel® MKL) 的用例并提供了示例。示例将使用 Arduino Create*(一个基于云的 IDE)和 UP2*(一个基于 Intel Apollo Lake 平台的单板计算机)。这些用例旨在向用户展示 Intel® MKL 提供的功能,而示例则提供了实现这些用例的简短代码片段。

注意:任何支持 Intel® MKL 要求的硬件设备都可以用作目标硬件。

要求

硬件要求

  • UP2(推荐)
  • 包含支持 SSE2(流式单指令多数据扩展 2)的 Intel 处理器的硬件设备

请参阅 Intel® MKL 要求以了解支持的硬件。

软件要求

关于 Intel® 数学内核库 (Intel® MKL)

需要数学函数(如矩阵乘法)的软件应用程序,可以通过利用 Intel® MKL 来提高性能(加快响应时间)。请参阅 https://software.intel.com/en-us/mkl/features/benchmarks 以获取 Intel® Core™ i7、Intel® Xeon® 和 Intel® Xeon Phi™ 处理器的 Intel® MKL 基准测试的完整审查和比较。

Intel® MKL 提供了数百个函数,开发人员可以根据应用程序的要求选择最适合优化的领域。下面的图 1 显示了 Intel® MKL 的组件,其中两个组件被突出显示,并且是本文的重点。

图 1 – Intel® MKL 中提供的组件
组件 (Component) 名称 描述
BLAS 基本线性代数子程序 GEMM - 通用矩阵乘法
LAPACK 线性代数包 SVD - 单值分解
表 1 - 本文引用的 MKL 组件

只有使用支持最低 SSE2 指令集的 IA-32 或 Intel® 64 架构才能实现使用 Intel® MKL 带来的性能提升,这包括了大多数随 Intel® 奔腾® 4 处理器发布的 CPU。

Intel® MKL 的基本要求可以在此处找到:https://software.intel.com/en-us/articles/intel-mkl-111-system-requirements

Intel® MKL 在边缘设备上的应用

本节将概述三个与数据压缩和图像处理相关的用例。代码示例部分提供了实现这些示例所需的代码。

物联网 (IoT) 设备最接近真实世界数据的采集源,因此是理想的响应时间优化目标。在边缘设备上提高响应时间的两种方法是:1) 在设备本身上执行代码,或 2) 减小需要传输进行分析的数据量。

在设备本身上做出决策可以实现最快的响应时间,因为它避免了将数据转发到另一个服务或云进行分析并等待响应。或者,在某些情况下无法在边缘计算,数据必须转发到单独的系统进行分析。减小数据大小和压缩数据有助于提高这些情况下的性能响应时间。最后,边缘设备比以往任何时候都更需要过滤和科学函数,这使得开发人员能够快速计算、转换和推导,这对于近乎实时响应至关重要。

在边缘受益于性能提升的两个示例领域包括:

  • 图像处理和分析
  • 数据压缩

由于图像可以在内存中以矩阵形式表示,因此可以使用熟悉的强大线性代数算法来处理和分析它们。一些转换类型及其现实世界应用包括:缩放(矩阵乘法)、平移(矩阵加法和转置)以及使用奇异值分解进行压缩。

Intel® 开发者中心提供了许多针对 Intel® MKL 的开发者支持资源,包括对矩阵基础知识的精彩概述。

用例 1:使用矩阵加法进行噪声滤波

使用矩阵加法和减法,可以以一种偏移和过滤单个元素的方式修改二值矩阵的元素。在实际应用中,可用于降低对比度、更改颜色或完全偏移像素值。下面是一个将值添加到二值灰度图像的示例,该图像将 100,000 添加到灰度值低于 100,000 的某些元素。

图 4 – 将 100,000 添加到每个灰度值 < 100,000 的像素前后的效果

用例 2:使用矩阵转置进行图像翻转

可以将矩阵中的值进行转置,或围绕对角线翻转,以改变图像的方向。下面是一个 200x200 灰度位图图像变换的示例输出。仔细观察,您会发现它不仅仅是旋转,而是围绕对角线进行了变换。

图 5 – 24 位灰度图像矩阵变换前后的效果

用例 3:使用奇异值分解进行图像尺寸减小

一个简单的 24 位灰度位图图像,如果尺寸为 500x500,将占用略高于 40KB 的内存。表示一个像素占用 4 字节 = 200*200*4 = 40000,这还不包括千字节的头部。

奇异值分解定理指出,所有 MxN 矩阵都可以分解为三个更小的矩阵,当重新组合时,可以提供一个原始矩阵的表示,该表示通常适用于特定目的。幸运的是,MKL 内置了计算矩阵 SVD 的例程,但这里是关于其工作原理的快速概述。

任何代表 MxN 像素图像的给定矩阵 A(其中 m=行,n=列)可以表示为三个矩阵的乘积:左奇异向量的列 (U)、右奇异向量的行 (VT) 以及实奇异值的对角线。

为了计算 SVD,通过特征向量的推导来构造三个矩阵

AAT 和 ATA 分别构成 V 和 U 的列。类似地,S 的奇异值通过取 AAT 或 ATA 的特征值的平方根来计算。一旦计算出来,可以通过从每个矩阵中选取小于 m 的一组行来对这三个矩阵进行采样,然后将它们相乘,得到原始值的非常接近的近似值。下面的图 5.2 显示了 SVD 在实际世界中的应用,可将图像尺寸减小高达 90%。

图 5.2 – 24 位灰度图像矩阵压缩前后对比,从 40K 到 4K

与 Arduino Create 集成

Arduino Create 是一个用于编程物联网设备的云 IDE。配备管理仪表板,用户现在可以远程管理和编程他们的物联网设备,轻松推送代码,就像设备直接连接一样。要开始,请访问 http://create-intel.arduino.cc/ 并创建一个帐户。

本节中的示例使用的是运行 Ubuntu 16.04 操作系统的 UP2 硬件和 Intel® MKL 2017,但此概述也适用于任何受支持的兼容堆栈。

图 6 – Arduino Create 设备仪表板的截图

安装

在开始之前,需要将 Intel® MKL 库安装到板上并正确配置。

要在 Ubuntu 上安装 Intel® MKL,请访问 http://software.intel.com/mkl 并按照下载说明进行操作。下载前需要注册,但完全免费。选择适用于您要使用的操作系统的 Intel® Performance Libraries 以及最新版本。然后,单击 Intel® MKL 链接开始下载。

图 7 – Intel MKL 下载选项的截图

下载后,通过解压存档、运行安装程序并设置环境变量来启动安装。默认安装文件夹为 /opt/intel/mkl/

tar –zxvf [Name of MKL file].tgz
cd [unpacked folder]
sudo ./install.sh OR sudo ./install_GUI.sh (if running on a desktop GUI)
cd [install folder] (Default is /opt/intel/mkl/bin/)
sudo ./mklvars.sh intel64 (This script will set environment variables for your platform)
图 8 – Linux 版 Intel® MKL 下载后的安装说明

Intel® MKL 附带了许多示例,可帮助开发人员尽快上手。探索默认安装文件夹(通常为 /opt/intel/mkl)下的 /examples/ 子文件夹,并根据您的具体要求修改代码。本文档中概述的代码示例摘自 Intel® MKL 的默认示例,并已迁移为可与 Arduino 风格的程序结构配合使用。从 C 迁移到 Arduino 仅意味着确保 setup() 和 loop() 标准函数可用,并且引用了 ,这是一个 Intel® MKL 库的头文件封装器。请注意,代码中引用的任何库都必须在编译时可以在 Arduino 云中找到。下表 2 提供了将 MKL C 示例代码迁移到 Arduino 所采取的常见操作示例。

Intel® MKL 示例 C 源文件 Arduino 迁移后的源文件
#include "MKLSpecificHeader.h"
int main(argc, arv){ func();}
					func1(){//poll or //setupCallbacks}
#include "ArduinoMKL.h"

setup() { func(); }
					loop() { //poll or //setupCallbacks /ordonothing }
表 2 - MKL C 到 Arduino 所需的示例代码结构迁移

验证安装

现在 Intel® MKL 已安装,请返回 Arduino Create 云 IDE(URL)并运行一个利用 MKL 的示例应用程序。在导航菜单中,选择库,然后搜索“MKL”。打开并探索 mkl-lab-solution 示例,该示例演示了使用 DGEMM – 双精度通用矩阵乘法进行简单的矩阵乘法。

图 9 – Arduino Create 库搜索 MKL 的截图

接下来,通过单击左侧导航菜单上的 Monitor 来打开串行监视器窗口。这将打开标准 Arduino IDE 中熟悉的调试窗口,该窗口允许与程序交互并打印调试语句。Arduino Create IDE 现在应该同时显示源代码和串行监视器窗口,如图 10 所示。

图 10 – Arduino Create 编辑器和监视器窗口

在此阶段,程序可以立即进行验证或直接上传到板上。图 11 显示了一个名为“Up2Ubuntu”的设备上 mkl-lab-solution 准备上传的示例。在此过程中,MKL 实际上是在云中编译的,作为验证过程的一部分。MKL 库在目标设备上执行时是动态链接和引用的。

图 11 – Arduino Create 将草图上传到设备

如图 12 所示,底部窗格将显示编译器输出,最后是结果摘要,其中指示了程序大小和使用的存储空间百分比。

图 12 – Arduino Create 的构建输出显示进程 ID 号 (PID)

通过登录目标平台,可以使用 ps –A 验证进程 ID,并通过运行 top –p2001 来监视它。

图 13 – ps –A 的输出显示匹配的 processID 确实正在执行

在 Arduino Create IDE 中,请注意监视器窗口正在请求要相乘的矩阵的大小。使用小于七的值将显示矩阵乘法的输出,允许您在需要时手动验证结果。探索代码并尝试不同的值。图 14 显示了 sample lab solution 的输出执行。

图 14 – 矩阵小于 7 时 mkl-lab-solution 的输出

代码示例

BLAS

示例 1:双精度通用矩阵乘法 (DGEMM)

现在我们有了基本示例,可以修改代码来检查使用标准矩阵乘法时的性能差异,并将结果与 MKL 的 DGEMM 进行比较。我们将把示例代码重构为几个函数以提高可读性,实现一个非常基本的 CMM(经典矩阵乘法)算法,并提供一个测试接口来改变矩阵大小和运行次数。图 18 中的代码片段提供了一个通用指南来测试性能差异,省略了 mkl-lab-solution 中的冗余代码。

Intel 的软件开发者中心提供了许多旨在协助开发人员使用 MKL 的资源,包括矩阵基础知识的精彩概述

https://software.intel.com/en-us/mkl-developer-reference-c-matrix-fundamentals

本示例中使用的特定矩阵乘法例程可以在此处参考:

https://software.intel.com/en-us/mkl-developer-reference-c-cblas-gemm

void cblas_dgemm (const CBLAS_LAYOUT Layout, const CBLAS_TRANSPOSE transa, constCBLAS_TRANSPOSE transb, const MKL_INT m, const MKL_INT n, const MKL_INT k, const double alpha,const double *a, const MKL_INT lda, const double *b, const MKL_INT ldb, const double beta, double*c, const MKL_INT ldc);
图 15 – MKL BLAS GEMM 例程签名定义
… / Includes
 
void setup()
{
// Maximize the number of threads
	max_threads = mkl_get_max_threads();
	printf (" Requesting Intel(R) MKL to use %i thread(s) \n\n", max_threads);
	mkl_set_num_threads(max_threads); 
 
…
	printf("\n\nEnter matrix size OR -1 to exit");
	scanf("%d",&N);
	MM_Standard();
	MM_Optimized();
 
}
 
void Dgemm_multiply(double* a,double*  b,double*  c, int N)
{
  double alpha = 1.0, beta = 0.;
  int incx = 1;
  int incy = N;
  cblas_dgemm(CblasRowMajor,CblasNoTrans,CblasNoTrans,N,N,N,alpha,b,N,a,N,beta,c,N);
}
 
void MM_Optimized()
{
  start = clock();
  Dgemm_multiply(a,b,c,N);
  stop = clock();
  time_Optimized = (double)(stop - start) / CLOCKS_PER_SEC;
}
void MM_Standard(){
  int row, col, i=0, k=0;
  start = clock();
  for (row = 0; row < N; row++){
  	for (col = 0; col < N; col++){
      	    for(k=0; k < N; k++){
          	       c[N*row+col] += a[N*row+k] * b[N*k+col];
      	    }
  	}
  } 
  stop = clock();
  time_Manual = (double)(stop - start) / CLOCKS_PER_SEC;
}
 
void loop() {
   exit(0);
}
图 18 - 用于比较 CMM 和 MKL DGEMM 的示例代码片段

示例 2:矩阵加法

可以使用 omatadd() 例程将两个矩阵相加。矩阵加法可以应用于图像以产生有趣的视觉效果,如引言部分所示。下面的示例代码将把“Matrix_Input”和“Matrix_Fade”两个矩阵相加,并将结果存储在一个名为“Matrix_Out”的数组中。

https://software.intel.com/en-us/mkl-developer-reference-c-mkl-omatadd

void mkl_domatadd (char ordering, char transa, char transb, size_t m, size_t n, const doublealpha, const double * A, size_t lda, const double beta, const double * B, size_t ldb, double * C,size_t ldc);
图 19 - MKL BLAS 矩阵加法例程签名定义
// Pseudocode for adding matrices
 
…
mkl_domatadd ('R', 'N', 'N', height, width, 1.0, Matrix_Input, height, 1, Matrix_Fade, height, Matrix_Out, height);
图 20 – 矩阵加法代码示例

示例 3:矩阵转置

MKL BLAS 的另一个示例用法是转置数据矩阵。矩阵转置是将行值转换为列值的操作,这是一项对更复杂的线性代数定理至关重要的操作。使用 MKL 转置矩阵包含在矩阵复制例程中,您可以在其中同时复制矩阵并选择转换矩阵的全部或部分。 https://software.intel.com/en-us/mkl-developer-reference-c-mkl-imatcopy

void mkl_simatcopy (const char ordering, const char trans, size_t rows, size_t cols, const floatalpha, float * AB, size_t lda, size_t ldb);
图 21 - MKL BLAS 矩阵复制签名定义
Example of using mkl_simatcopy transposition
 
Source matrix:
----------
1  1  1  1  1  1  1  1  1  1 
0  1  0  0  0  0  0  0  0  0 
0  0  1  0  0  0  0  0  0  0 
0  0  0  1  0  0  0  0  0  0 
0  0  0  0  1  0  0  0  0  0 
0  0  0  0  0  1  0  0  0  0 
0  0  0  0  0  0  1  0  0  0 
0  0  0  0  0  0  0  1  0  0 
0  0  0  0  0  0  0  0  1  0 
0  0  0  0  0  0  0  0  0  1 
 
-----------
Transposed matrix:
1  0  0  0  0  0  0  0  0  0 
1  1  0  0  0  0  0  0  0  0 
1  0  1  0  0  0  0  0  0  0 
1  0  0  1  0  0  0  0  0  0 
1  0  0  0  1  0  0  0  0  0 
1  0  0  0  0  1  0  0  0  0 
1  0  0  0  0  0  1  0  0  0 
1  0  0  0  0  0  0  1  0  0 
1  0  0  0  0  0  0  0  1  0 
1  0  0  0  0  0  0  0  0  1 
[Figure 22 – Output of Transposed matrix example]
 
  size_t n=10, m=10; /* rows, cols of source matrix */
  float src[]= {                   	
 	1,1,1,1,1,1,1,1,1,1,
 	0,1,0,0,0,0,0,0,0,0,
 	0,0,1,0,0,0,0,0,0,0,
 	0,0,0,1,0,0,0,0,0,0,
 	0,0,0,0,1,0,0,0,0,0,
 	0,0,0,0,0,1,0,0,0,0,
 	0,0,0,0,0,0,1,0,0,0,
 	0,0,0,0,0,0,0,1,0,0,
 	0,0,0,0,0,0,0,0,1,0,
 	0,0,0,0,0,0,0,0,0,1
  };
 
  printf("\nExample of using mkl_simatcopy transposition\n\n");
 
  printf("Source matrix:\n----------\n");
  print_matrix(n, m, 's', src);
                     	
  //Copy matrix and transpose using Row-major order
  mkl_simatcopy('R' /* row-major ordering */,
            	'T' /* A will be transposed */,
            	m   /* rows */,
            	n   /* cols */,
            	1.  /* scales the input matrix */,
            	src /* source matrix */,
            	m   /* src_lda */,
            	n   /* dst_lda */);
 
  printf("\n-----------\nTransposed matrix:\n");
  print_matrix(n, m, 's',src);

LAPACK

示例 4:用于压缩的 SVD

最后一个值得一提的用例与数据压缩有关。在边缘设备上处理大型矩阵时,采用矩阵数据的大小减小技术将非常有益。大型矩阵会占用大量内存,这对于本地存储以及在需要使用低带宽介质将数据传输到其他位置时来说是一个问题。利用流行的线性代数定理——奇异值分解,可以显著减小矩阵的大小,以帮助解决边缘设备上的这些问题。

MKL 内置了对计算实数和复数矩阵的 SVD 的支持,并在 MKLHOME/examples/lapacke/ 文件夹中包含示例源代码。与本文中的所有示例一样,代码可以通过表 2 中概述的迁移步骤从 MKL 随附的 C 示例迁移到 Arduino。

本示例中使用的特定例程可在此处参考:https://software.intel.com/en-us/node/521150

lapack_int LAPACKE_sgesvd( int matrix_layout, char jobu, char jobvt, lapack_int m, lapack_intn, float* a, lapack_int lda, float* s, float* u, lapack_int ldu, float* vt, lapack_int ldvt,float* superb );
图 24 - MKL LAPACK 通用奇异值分解签名定义

图 25 – Arduino Create Monitor SVD 输出
void setup() {
…
          	info = LAPACKE_dgesvd( LAPACK_ROW_MAJOR, 'A', 'A', m, n, a, lda,
                                       	s, u, ldu, vt, ldvt, superb );
          	/* Check for convergence */
          	if( info > 0 ) {
                         	printf( "The algorithm computing SVD failed to converge.\n" );
                         	exit( 1 );
          	}
          	/* Print singular values */
          	print_matrix( "Singular values", 1, n, s, 1 );
          	/* Print left singular vectors */
          	print_matrix( "Left singular vectors (stored columnwise)", m, n, u, ldu );
          	/* Print right singular vectors */
          	print_matrix( "Right singular vectors (stored rowwise)", n, n, vt, ldvt );
….
图 26 – 调用 dgesvd 例程的代码片段

结论

Intel 提供的数学内核库提供高度优化的数学函数和算法,这些函数和算法仅设计用于 Intel 硬件。对于使用复杂的数学函数(如矩阵代数或奇异值分解)并需要比典型软件优化编程更快响应时间的应用程序,MKL 可以提供更快的响应时间。无论是在云端还是在边缘运行,Intel 都提供专为数学密集型、科学计算应用所需优化而设计的硬件。

附录:代码向量化

使 Intel® MKL 增强成为可能的核心功能是通过编译器优化实现的,这些优化通过 SSE 优化实现代码向量化。代码向量化是一种确保在编译时,每个计算机操作都包含数据和指令,从而以并行方式利用 SSE 寄存器。SSE(流式单指令多数据扩展)是代码向量化的架构实现。SSE 于 1999 年随 Intel® Pentium® 3 处理器发布,包含一组八个 128 位专用浮点寄存器和 70 个新操作,增强了对不同数据集之间复制的操作的性能。

Intel® MKL 利用的另一个功能是高级向量扩展 (AVX)。它是 Intel® Core™ 微体系结构中包含的附加指令集,但不支持 Pentium® 或 Celeron® 处理器。

如何确定您的系统是否支持 Intel® MKL

要确定特定 CPU 的硬件功能,请查看 /proc/cpuinfo

cat /proc/cpuinfo | grep ‘avx’
cat /proc/cpuinfo | grep ‘avx2’
cat /proc/cpuinfo | grep ‘sse’
图 2 – 用于确定处理器功能的 Unix 命令

有兴趣直接调用 SSE 的开发人员可以查看Intel® Intrinsics Guide。它提供了 SSE 和 SSE2 的 C API,使开发人员无需汇编语言经验即可直接访问 SIMD 功能。

注意:此产品与 Intel® MKL 完全分开,但在讨论 SSE 时值得一提。

Intel 的链接行顾问用于构建链接器标志

Intel 的链接行顾问是一个基于 Web 的工具,可帮助开发人员快速构建满足其平台要求的链接器标志(用于链接库)。您的平台要求是顾问的输入(例如,64 位整数接口层),输出是链接行和编译器选项。

图 3 – Intel 链接行顾问帮助构建链接器标志的截图

关于作者

Matt Chandler 自 2004 年以来一直是 Intel 的高级软件和应用工程师。他目前专注于物联网的规模赋能项目,包括智能建筑、设备安全和零售数字标牌垂直领域的软件供应商支持。

参考文献

Intel® 数学内核库 2017 安装指南

Intel® 数学内核库食谱

Intel® 数学内核库深入培训

在 Java 中使用 Intel® 数学内核库

安装指南

© . All rights reserved.