CPUG联盟::

CPUG::门户plone

BPUG

SPUG

ZPUG

SpreadPython Python宣传

用SSE2指令协助Python进行高性能计算


介绍

  在本文的教程中,我要讨论一下怎样用Python调用外部库来进行高性能计算。Python调用库的计算结果,计算由汇编语言调用的SSE指令集,此速度在极限条件下(不作分支判断,只在一个循环中做加减乘除计算)与纯Python计算相比可以提升2000倍。
在进入教程之前,我们先简单介绍一下本文所讲述调用过程。
Python先调用这个库,这个库的计算由HLA提供,C语言调用HLA函数。然后计算结果通过标准Python接口传给Python。

SSE介绍

  SSE指令集也叫单指令多数据流扩展。最早是由英特尔提出,是为了加强浮点运算、图像处理等多媒体应用的能力,能更好的对整个系统进行控制,提高处理性能的指令的集合。
SSE2指令集是Intel公司在SSE指令集的基础上发展起来的.相比于SSE,SSE2 使用了144个新增指令,扩展了MMX技术和SSE技术,这些指令提高了广大应用程序的运行性能.随MMX技术引进的SIMD整数指令从64位扩展到了 128 位,使SIMD整数类型操作的有效执行率成倍提高.双倍精度浮点SIMD指令允许以 SIMD格式同时执行两个浮点操作,提供双倍精度操作支持有助于加速财务、工程和科学应用.除SSE2指令之外,最初的SSE指令也得到增强,通过支持多种数据类型的算术运算,支持灵活并且动态范围更广的计算功能.SSE2指令可让软件开发员极其灵活的实施算法,并在运行诸如MPEG-2、MP3、3D图形等之类的软件时增强性能. 从SSE1到SSE4都是由英特尔所提出,而AMD除了自己的指令集3DNow!,也支持这些SSE指令集。

HLA介绍

  HLA(High Level Assemble)是一种高级汇编语言。它提供了一些高级语言的特性,比如一些高级控制结构和过程调用。当然这些高级特性也是有代价的。好在它的自由度也很高,使用者也可以忽略它的这些特性,发挥低级汇编的效率特点。它的源文件还可以编译成FASM,Gas,MASM,TASM。


开始

HLA

开始用HLA写程序之前,先确保HLA已经安装。

unit hlaFuncUnit;
    #include( "stdlib.hhf" ) 
    
procedure hla_Vec3Add( var vector1:real128; var vector2:real128; var vector3:real128 ); @cdecl; @external( "_hla_Vec3Add" );   //声名过程hla_Vec3Add及其传递的参数,还有外部调用方式和调用名

    procedure hla_Vec3Add( var vector1:real128; var vector2:real128; var vector3:real128);      
    begin hla_Vec3Add;      
          mov(vector1, eax);
          movdqa((type real128 [eax]), xmm0);   // 将vector1放入xmm0寄存器
          mov(vector2, eax);
          movdqa((type real128 [eax]), xmm1);   // 将vector2放入xmm1寄存器
          addps(xmm1, xmm0);                                    // vector1 + vector2
          mov(vector3, eax);
          movdqa(xmm0, (type real128 [eax]));    // 将结果保存到vector3
    end hla_Vec3Add;
end hlaFuncUnit;

将上面这段程序用记事本保存成hlaFunc.hla。运行cmd.exe,在控制台上输入

> hla –c hlaFunc.hla

之后生成hlaFunc.obj。

C

{{{ 用VC新建一个win32项目。生成类型选择Dynamic Library (.dll),项目头文件和库文件路径选择Python安装路径下的头文件和库文件目录。在Link 配制页的input项中,Additional Dependencies这一项填入刚刚生成的hlaFunc.obj和HLA库文件hlalib.lib。在Link 配制页的General项中,Output File这一项填入$(OutDir)\sse.pyd。 将下面的程序保存成C源文件。}}}

#include "Python.h"


typedef __declspec(align(16)) struct {
        float x;
        float y;
        float z;
} Vec3;

typedef __declspec(align(16)) struct {
        float f1;
        float f2;
        float f3;
        float f4;
} __m128;

extern void hla_Vec3Add( Vec3* vector1, Vec3* vector2, Vec3* vector3);
static PyObject *sseError;

static PyObject *sse_vec3Add(self, args) PyObject *self; PyObject *args;
{

        Vec3 vector1 = {0.0f, 0.0f, 0.0f};
        Vec3 vector2 = {0.0f, 0.0f, 0.0f};
        Vec3 vector3 = {0.0f, 0.0f, 0.0f};

    if (!PyArg_ParseTuple(args, "(fff)(fff)", &(vector1.x), &(vector1.y), &(vector1.z), &(vector2.x), &(vector2.y), &(vector2.z)))  // 将Python传过的的参数进行解析。
        return NULL;

        hla_Vec3Add( &vector1, &vector2, &vector3);     // 调用HLA函数
    return Py_BuildValue("(fff)" , vector3.x, vector3.y, vector3.z); // 返回结果
}

static PyMethodDef sseMethods[] = {    
        {"Vec3Add",  sse_vec3Add, METH_VARARGS, "vec3 = vec1 + vect2"},
         {NULL,      NULL}        /* Sentinel */
};

#ifdef MS_WIN32
__declspec(dllexport)
#endif

void initsse()
{
    PyObject *m, *d;

    m = Py_InitModule("sse", sseMethods);
    d = PyModule_GetDict(m);
    sseError = PyErr_NewException("sse.error", NULL, NULL);
    PyDict_SetItemString(d, "error", sseError);

}

Build之后,生成sse.pyd。将其放到Python的安装目录下。

Python

打开Python,输入

>>> import sse
>>> sse.SSEAdd((0.1, 0.2, 0.3), (0.4, 0.5, 0.6,)) ##输入两个数

这样我们就可以在汇编程序那端进行计算,然后将结果告诉Python。


需要注意的是小规模的计算还是用Python比较方便,Python在调用库函数时进行的频繁出栈入栈和解析打包参数比较耗时。如果计算只计算一次两数相加,那还不如用Python自己来算快。

Azureon Email: <[email protected]>

PyWorkWithEES2 (last edited 2009-12-25 07:09:36 by localhost)