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

面向大数据的 Go* 语言

$
0
0

利用配有 Go* 编程语言的英特尔® 数据分析加速库 (英特尔® DAAL)支持批量、在线和分布式处理

最热门的现代基础设施项目均Go*,包括 Kubernetes*、Docker*、Consul*、etcd* 等。Go 成为了广泛应用于开发运营、web 服务器和微服务的 go to语言。它简单易学、便于快速部署,还为开发人员提供了一套出色的工具。

由于业务越来越受数据驱动,需要在公司基础设施的不同层次集成计算密集型算法,包括应用 Go 的层级。因此,有必要关注如何将机器学习、分布式数据转换和在线数据分析集成至蓬勃发展的基于 Go 的系统中。

为了在 Go 中提供高性能、可扩展的强大数据处理,可以在 Go 程序中使用英特尔® 数据分析加速库(英特尔® DAAL)。该库为一系列实用任务提供现成的批量、在线和分布式算法:

由于 Go 实现了与 C/C++ 的良好交互,我们可以将这个功能轻松地添加至 Go 程序,通过利用面向即购即用架构而优化的英特尔库来实现。如此处所示,进行某些操作时(如主组件分析),英特尔 DAAL 的速度比 Spark* 和 MLlib* 的速度高出 7 倍。哇!我们要抓紧时间探索如何利用上述性能升级 Go 应用。

安装英特尔® DAAL

英特尔 DAAL 以开源方式提供,可以通过以下指令安装。Linux* 设备上的指令非常简单:

  1. 下载开源代码。
  2. 运行安装脚本。
  3. 设置必要的环境变量(可以通过给出的 shell 脚本设置)。

将英特尔 DAAL 集成至 Go 程序前,请确保一切正常运行。根据英特尔 DAAL 文档中的各种入门指南进行操作。具体而言,这些入门指南提供面向 Cholesky 分解的示例英特尔 DAAL 应用,随后我们将利用 Go 重新创建 Cholesky 分解。Cholesky 分解的原始 C++ 示例如下所示:

/*******************************************************************************
!版权所有 © 2014-2017,英特尔公司。保留所有权利。
!
!本文所含的源代码、信息和材料
!归英特尔公司及其供应商或许可方所有,
!材料的所有权仍归英特尔及其供应商或许可方所有。材料
!包含了英特尔及其供应商或许可方的
!专有信息。这些材料受全球版权法律和条约条款
!的保护。不经英特尔事先书面许可,不得以任何形式使用、复制、翻制、
!修改、出版、上传、张贴、传输、发布或公开
!这些材料。本材料
!未通过明确、隐含、诱导、禁止反言或其它任何方式,
!授予您任何专利、版权或其它知识产权
!的许可。任何知识产权
!的授权必须得到英特尔
!的书面批准。
!
!*其他的名称和品牌可能是其他所有者的资产。
!
!除非经过英特尔的书面同意认可,不得以任何方式移除或更改
!本声明或英特尔及其供应商或许可方材料中的
!其他声明。
!
!*******************************************************************************
!内容:
!    Cholesky 分解示例程序。
!******************************************************************************/

#include "daal.h"
#include <iostream>

using namespace daal;
using namespace daal::algorithms;
using namespace daal::data_management;
using namespace daal::services;

const size_t dimension = 3;
double inputArray[dimension *dimension] =
{
    1.0, 2.0, 4.0,
    2.0, 13.0, 23.0,
    4.0, 23.0, 77.0
};

int main(int argc, char *argv[])
{
    /* Create input numeric table from array */
    SharedPtr<NumericTable> inputData = SharedPtr<NumericTable>(new Matrix<double>(dimension, dimension, inputArray));

    /* Create the algorithm object for computation of the Cholesky decomposition using the default method */
    cholesky::Batch<> algorithm;

    /* Set input for the algorithm */
    algorithm.input.set(cholesky::data, inputData);

    /* Compute Cholesky decomposition */
    algorithm.compute();

    /* Get pointer to Cholesky factor */
    SharedPtr<Matrix<double> > factor =
        staticPointerCast<Matrix<double>, NumericTable>(algorithm.getResult()->get(cholesky::choleskyFactor));

    /* Print the first element of the Cholesky factor */
    std::cout << "The first element of the Cholesky factor:"<< (*factor)[0][0];

    return 0;
}

尝试进行编译和运行,确保成功安装了英特尔 DAAL,还让您提前体验了一次 Go 中的操作。如果对英特尔 DAAL 安装有任何问题,可以在英特尔 DAAL 论坛中讨论,对我而言,它是宝贵的资源,有助于熟练使用英特尔 DAAL。 

在 Go 中使用英特尔 DAAL

可以利用以下几种方式在 Go 中使用英特尔 DAAL:

  1. 利用包装函数从 Go 程序中直接调用英特尔 DAAL。
  2. 创建一个包装特定英特尔 DAAl 功能的可复用库。

下面将展示两种方法,访问此处查看本文使用的全部代码。这只是一个示例,建议最终为存储库添加更多的 Go 和 英特尔 DAAL 示例。试验完毕后,提交您的 Pull 请求。我非常期待看到您的成果!

如果您刚刚接触 Go,可以在阅读本教程前,花点时间熟悉 Go。事实上,本地安装 Go 不是学习 Go 的必要条件。您可以参加在线 Go 概览课程,并使用< a href="https://play.golang.org/" target="_blank">Go Playground,一切就绪后,本地安装 Go

从 Go 中直接调用英特尔 DAAL

Go 实际提供一款称为 cgo的工具,生成调用 C 代码的 Go 程序包。在本例中,利用 cgo 实现 Go 程序与英特尔 DAAL 的互操作。

请注意:互联网上深入讨论了在 Go 程序中使用 cgo 的利弊(特别建议您查看 Dave Cheney 的讨论和 Cockroach 实验室* 撰写的一篇文章)。选择 cgo 之前,您应该考虑这些成本,至少知道成本的存在。在本示例中,为了利用高度优化的分布式英特尔 DAAL 库,我们乐意寻找均衡利用 cgo 的方法,极有可能改善特定的数据密集型或计算密集型使用案例。

为了在示例 Go 程序中集成英特尔 DAAL Cholesky 分解功能,需要创建以下目录结构(在 $GOPATH中):

cholesky`
├── cholesky.go`
├── cholesky.hxx`
└── cholesky.cxx`
 

cholesky.go文件就是利用英特尔 DAAL Cholesky 分解功能的 Go 程序。cholesky.cxxcholesky.hxx文件是包含英特尔 DAAL 的 C++ 定义/声明文件,将等待包装的英特尔 DAAL 功能的信号传输给 cgo。让我们逐一查看。

首先查看 *.cxx文件:

#include "cholesky.hxx"
#include "daal.h"
#include <iostream>

using namespace daal;
using namespace daal::algorithms;
using namespace daal::data_management;
using namespace daal::services;

int choleskyDecompose(int dimension, double inputArray[]) {

    /* Create input numeric table from array */
    SharedPtr<NumericTable> inputData = SharedPtr<NumericTable>(new Matrix<double>(dimension, dimension, inputArray));

    /* Create the algorithm object for computation of the Cholesky decomposition using the default method */
    cholesky::Batch<> algorithm;

    /* Set input for the algorithm */
    algorithm.input.set(cholesky::data, inputData);

    /* Compute Cholesky decomposition */
    algorithm.compute();

    /* Get pointer to Cholesky factor */
    SharedPtr<Matrix<double> > factor =
        staticPointerCast<Matrix<double>, NumericTable>(algorithm.getResult()->get(cholesky::choleskyFactor));

    /* Return the first element of the Cholesky factor */
    return (*factor)[0][0];
}

然后查看 *.hxx文件:

#ifndef CHOLESKY_H
#define CHOLESKY_H

// __cplusplus gets defined when a C++ compiler processes the file.
// extern "C" is needed so the C++ compiler exports the symbols w/out name issues.
#ifdef __cplusplus
extern "C" {
#endif

int choleskyDecompose(int dimension, double inputArray[]);

#ifdef __cplusplus
}
#endif

#endif

这些文件定义了一个以 C++ 编写的 choleskyDecompose 包装程序函数,该函数利用英特尔 DAAL Cholesky 分解功能计算输入矩阵的 Cholesky 分解,并输出 Cholesky 因数的第一个因素(和英特尔 DAAL 入门指南的内容相似)。请注意,在本示例中,输入阵列的长度等于矩阵的长度(即 3 x 3 矩阵与长度为 9 的输入阵列对应)。需要将 extern “C”置于 *.hxx文件中。这样做将使 cgo 调用的 C++ 编译器知道我们需要导出 C++ 文件中定义的相关名称。

*.cxx*.hxx文件中定义 Cholesky 分解包装程序函数后,可以从 Go 中直接调用该函数。cholesky.go如下所示:

package main

// #cgo CXXFLAGS:-I$DAALINCLUDE
// #cgo LDFLAGS:-L$DAALLIB -ldaal_core -ldaal_sequential -lpthread -lm
// #include "cholesky.hxx"
import "C"

import (
	"fmt""unsafe"
)

func main() {

	// Define the input matrix as an array.
	inputArray := [9]float64{
		1.0, 2.0, 4.0,
		2.0, 13.0, 23.0,
		4.0, 23.0, 77.0,
	}

	// Get the first Cholesky decomposition factor.
	data := (*C.double)(unsafe.Pointer(&inputArray[0]))
	factor := C.choleskyDecompose(3, data)

	// Output the first Cholesky dcomposition factor to stdout.
	fmt.Printf("The first Cholesky decomp. factor is:%d\n", factor)
}

下面将概述各个步骤,介绍其中的原理。首先告知 Go 需要在编译程序时使用 cgo 以及利用特定的标记编译:

// #cgo CXXFLAGS:-I$DAALINCLUDE
// #cgo LDFLAGS:-L$DAALLIB -ldaal_core -ldaal_sequential -lpthread -lm
// #include "cholesky.hxx"
import "C"

为了使用 cgo,需要导入“C”,C 是一个伪程序包,利用 C 通知 Go 我们正在使用cgo。如果导入“C”后立即出现了一条注释(被称为引言),编译程序包的 C++ 部件时将注释用作标头。

利用 CXXFLAGS 和 LDFLAGS 指定编译过程中 cgo 使用的编译和链接标记,可以通过 // #include "cholesky.hxx”添加 C++ 函数。我使用安装 gcc 的 Linux 编译本示例,因此,使用上文中给出的标签。您也可以依据 本指南决定如何将应用连接至英特尔 DAAL。

然后,编写 Go 代码(和其他程序的编写方式一样),利用 C.choleskyDecompose()访问包装函数:

// Define the input matrix as an array.
inputArray := [9]float64{
	1.0, 2.0, 4.0,
	2.0, 13.0, 23.0,
	4.0, 23.0, 77.0,
}

// Get the first Cholesky decomposition factor.
data := (*C.double)(unsafe.Pointer(&inputArray[0]))
factor := C.choleskyDecompose(3, data)

// Output the first Cholesky dcomposition factor to stdout.
fmt.Printf("The first Cholesky decomp. factor is:%d\n", factor)

使用 cgo 的一个独有特性是需要将 float64 片段的第一个要素指示器转换为不安全指示器,继而面向 choleskyDecompose函数明确转换为 *C.double(兼容 C++)指示器。不安全包,顾名思义,帮助我们绕过 Go 程序的类型安全。

非常好!现在,我们有了一个调用英特尔 DAAL Cholesky 分解的 Go 程序。现在我们开始创建并运行这个程序。像往常一样,利用 go build创建:

$ ls
cholesky.cxx  cholesky.go  cholesky.hxx
$ go build
$ ls
cholesky  cholesky.cxx  cholesky.go  cholesky.hxx
$ ./cholesky
The first Cholesky decomp. factor is:1
$

我们获得了预期的输出!事实上,第一个 Cholesky 分解因数为 1。我们成功了,可以从 Go 中直接利用英特尔 DAAL 的功能!但是,由于不安全包和 C 位,这个 Go 程序有点与众不同。而且,它属于一次性解决方案。现在,为了使我们的 Go 程序包和其他 Go 程序包一样能够导入,需要将它转换为可复用 Go 程序包。

利用英特尔 DAAL 创建可复用 Go 程序包

为了创建包装英特尔 DAAL 功能的 Go 程序包,需要使用一种名为 SWIG*的工具。除 cgo 以外,Go 具有在构建时调用 SWIG 的功能,编译包装 C/C++ 功能的 Go 程序包。为了启用这种构建,需要创建以下目录结构:

choleskylib
├── cholesky.go
├── cholesky.hxx
├── cholesky.cxx
└── cholesky.swigcxx
 

*.cxx*.hxx包装程序文件可以同时存在,现在需要添加一个 *.swigcxx文件,如下所示:

%{
#include "cholesky.hxx"
%}

%include "cholesky.hxx"

该文件指示 SWIG 工具生成面向 Cholesky 函数的包装代码,可以将代码用作 Go 程序包。

由于现在正在创建可复用 Go 程序包(而非独立的 Go 应用),不需要将 package main 或 function main 纳入 *.go文件。只需要定义程序包名称。本示例称其为 cholesky,意味着 cholesky.go如下所示:

package cholesky

// #cgo CXXFLAGS:-I$DAALINCLUDE
// #cgo LDFLAGS:-L$DAALLIB -ldaal_core -ldaal_sequential -lpthread -lm
import "C"

(再次提供标头标记。)

现在,可以在本地创建并安装程序包:

$ ls
cholesky.cxx  cholesky.go  cholesky.hxx  cholesky.swigcxx
$ go install
$

通过上述操作,创建了所需的全部二进制和库,当 Go 程序使用这个程序包时,可以调用这些二进制和库。Go 会发现目录中存在一个 *.swigcxx文件,随后它会自动利用 SWIG 创建程序包。

太棒了;我们创建了使用英特尔 DAAL 的 Go 程序包。我们看一下应该如何导入并使用这个程序包:

package main

import (
	"fmt""github.com/dwhitena/daal-go/choleskylib"
)

func main() {

	// Define the input matrix as an array.
	inputArray := [9]float64{
		1.0, 2.0, 4.0,
		2.0, 13.0, 23.0,
		4.0, 23.0, 77.0,
	}

	// Get the first Cholesky decomposition factor.
	factor := cholesky.CholeskyDecompose(3, &inputArray[0])

	// Output the first Cholesky dcomposition factor to stdout.
	fmt.Printf("The first Cholesky decomp. factor is:%d\n", factor)
}

好极了!和直接包装英特尔 DAAL 相比,这样看起来整洁多了。按照导入其他 Go 程序包的方式导入 Cholesky 程序包,并将包装函数命名为 cholesky.CholeskyDecompose(...)。此外,SWIG 帮助我们处理了所有的不安全因素。现在我们可以将原始 float64 片段的第一个元素的地址传输给 cholesky.CholeskyDecompose(...)

和其他 Go 程序类似,利用 go build编译与运行:

$ ls
main.go
$ go build
$ ls
example  main.go
$ ./example
The first Cholesky decomp. factor is:1
$

哇!答案正确。现在可以在另一个需要 Cholesky 分解的 Go 程序中使用这个程序包。

结论/资源

利用英特尔 DAAL、cgo 和 SWIG,能够将经过优化的 Cholesky 分解集成至 Go 程序中。但是,这项技术不仅限于 Cholesky 分解,您可以创建 Go 程序和程序包,使它们以同样的方式利用英特尔 DAAL 中的任何算法。这意味着您可以在 Go 应用中实施批量、在线和分布式神经网络、集群、加速、协同过滤等各种操作。

如欲获取本文使用的全部代码,请访问此处

Go 数据资源:

英特尔 DAAL 资源:

关于作者

Daniel (@dwhitena) 是一名拥有博士学位的数据科学家,目前就职于 Pachyderm (@pachydermIO)。Daniel 开发了创新型、分布式数据管线,包含预测模型、数据可视化、统计分析等。他在全球各大会议上发表演讲(ODSC、Spark 峰会、Datapalooza、DevFest Siberia、GopherCon等),在 Ardan 研究院 (@ardanlabs) 教授数据科学/工程,面向 Jupyter 维护 Go 内核,并积极支持各种开源数据科学项目的筹办。


Viewing all articles
Browse latest Browse all 583

Trending Articles



<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>