关键词:
文件编程(精选五篇)
文件编程 篇1
在数据挖掘中,数据输入是整个数据挖掘的准备阶段,数据的预处理是数据输入前的必不可少的环节。ARFF(Attribute-Relation File Format)文件是WEKA默认的储存数据集文件,它是一种ASCII文本文件。在实际的应用中,许多的原始数据采用的是Excel或数据库存放,以Excel转换为ARFF文件格式为例,通常的做法是Excel导出为CSV格式,再利用WEKA的数据预处理功能导入CSV文件,这种做法对于单个Excel文件且单个Sheet表方法可行,但对于批量的Excel文件或多个Sheet表的手动转换方式,显得过于繁琐。因此,对于WEKA数据文件的动态生成存在实际的应用需求,本文利用Java编程实现了WEKA ARFF数据文件的生成。
1 ARFF数据格式描述
每个ARFF文件就是一个数据集,相当于一个二维表格。列表头是数据集的各个属性(Attrbute),相当于统计学中的一个变量,或者数据库中的一个字段;除开表头的表格的各行是数据集的各实例(Instance),类似于统计学中的各个样本,或者数据库中的记录。
ARFF文件有固定的数据格式,包括头信息和数据两个部分,头信息定义了表的名称和表的结构,数据部分以行为单位按表结构定义了数据集的各实例。在具体的ARFF的数据格式描述中提供了一套统一的符号集,其表示的含义如表1所示。
在表结构的定义中,属性具有唯一性,以行为单位进行书写,每一个属性都以@attribute语句开头,来定义其属性名称和数据类型,属性定义的基本格式如下:
ARFF所支持的数据类型有:数值型(整型或实型)、标称型、字符串型、日期时间型等,数据类型的具体符号如表2所示。
在ARFF文件中以@data语句开始的后续行部分定义了数据实例,每个实例的属性值用逗号作为分隔符,若没有属性值,则用问号“?”表示取缺省值。以下给出了一个包含头信息和数据部分的ARFF文件的实例:
2 ARFF数据文件的生成
2.1 相关的包和类
ARFF数据文件实质上一个纯文本文件,理论上讲只要文件内容处理为ARFF规范的数据格式,保存扩展名为.arff即可,在利用Java编程处理过程中,可以采用两种方法来实现:第一种,通过Java的基础类与方法;第二种,通过WEKA自带的weka.jar包中的类与方法。采用第二种方法实现ARFF数据文件的生成,在实现的方法上显得较为直接和简单,因此,本文采用了该方法。
weka.jar包可作为二次开发使用,它集成了数据挖掘中如:数据预处理、数据输入、聚类和分类算法、数据可视化等常用操作,在MyEclipse展开后的包层次如图1所示。
在weka.jar包中的weka.core包,涉及到数据文件生成的主要类有Attribute、FastVector、Instance和Instances等[2],其具体功能说明如下:
2.2 编程实现
2.2.1 编程环境
编程环境为:Windows 7 64bit,Java的sdk 1.6.0_43,MyEclipse 10.0,weka.jar 3.6.1 2。
2.2.2 导入jar包
启动MyEclise,新建Java project,导入weka.jar和commons-io-2.1.jar包,新建CreateArffFile类,最终工作区的目录结构如图2所示。
2.2.3 实现的相关代码
CreateArffFile类实现“学生情况.arff”ARFF数据文件的相关代码如下:
3 结语
介绍了WEKA数据挖掘平台中ARFF数据文件的数据格式,并通过Java编程实现了数据文件的生成与创建,使用者可根据应用的需要灵活调整,为进一步作数据挖掘奠定基础。
参考文献
文件编程 篇2
close()方法方法关闭打开的文件,关闭的文件无法读取或写入更多东西。文件已被关闭之后任何操作会引发ValueError。但是调用close()多次是可以的。
Python自动关闭,当一个文件的引用对象被重新分配给另外一个文件。它使用close()方法来关闭一个文件一个很好的做法。
语法
以下是close()方法的语法:
fileObject.close();
参数
NA
返回值
此方法不返回任何值
例子
下面的例子显示了close()方法的使用
#!/usr/bin/python# Open a filefo = open(“foo.txt”, “wb”)print “Name of the file: ”, fo.name# Close opend filefo.close()
当我们运行上面的程序,它会产生以下结果:
文件编程 篇3
世界上存在多种文字, 在用计算机处理文字信息时, 为了节省存储空间、提高处理速度, 一些国家和地区为文字制定了相应的字符集, 有的一种文字存在多种字符集。字符集可以以表格形式描述为字符到单字节值或多字节值的映射。ASCII码字符集是所有多字节字符集的一个子集;GB2312字符集是中国大陆制定的, 容纳了6763个简体汉字;Big5字符集是台湾地区制定的, 容纳了13053个繁体字;GBK字符集GB2312的扩展集, 它兼容GB2312, 所以也兼容ASCII, 它能容纳二万多汉字;Unicode字符集设计思想是要它容纳全世界所有文字, 它是国际通用的字符集。
代码页就是指一个字符集, 其中包含数字、标点符号及其他字形。在Windows系列操作系统中, 不同的语言和locale设置会调用不同的代码页。例如英语和大多数欧洲语言使用ANSI代码页1252, 而中国大陆和新加坡使用代码页936 (GBK字符集) , 中国台湾地区使用代码页950 (Big5字符集) 。程序启动时, 自动将多字节代码页设置为操作系统默认的代码页。运行库中大多数多字节字符函数的行为, 与当前多字节代码页设置有关。比如记事本程序 (notepad.exe) 在打开一个ANSI编码的文本时, 会根据操作系统的中的设置来决定使用哪个代码页。这个设置在控制面板—区域和语言选项---高级选项卡----Language for non-Unicode programs设置中, 如果一个文本原本是用代码页936 (简体中文) 编码的, 而操作系统在此处的设置为Chinese (Taiwan) , 用记事本打开时将会显示乱码, 将设置改为Chinese (PRC) 就可正常显示。由于Unicode是国际通用的宽字节字符集, 所以用Unicode编码的文本文件不受此处设置的影响, 都可以正常打开。
2 功能及特点
字符编码转化程序的界面如图1所示。使用这个程序转化文本文件的字符编码时, 只要在“文件名”前的“编辑浏览控件”中选择或者直接输入需要转化的文件路径及其名称, 然后根据需要点击界面中间的命令按钮即可。所生成的新文件名称会在界面最下方的编辑框中显示出来。
3 设计思路
由于字符编码之间没有对应的规律, 如果要把某个文本文件从一种字符编码转为由另一种字符编码来存储, 一般使用查表映射的方式。在Windows系列操作系统中, 使用VC++编程时, 这种字符映射不需要由程序员来编写, 可以使用Windows操作系统提供的两个函数MultiByteToWideChar、 WideCharToMultiByte来实现。
函数MultiByteToWideChar可以把多字节字符串转化为宽字节字符串, 宽字节编码只有Unicode, 这是确定无疑的, 无需特别指出, 而多字节编码种类却有很多, 使用这个函数可以把全部的多字节编码都能分别转化为宽字节编码。它的第一个参数是指与多字节字符相关的代码页, 它的设置可以是非常具体的, 如:936 (表示简体中文) 、950 (表示繁体中文) 、1361 (表示韩国语) 等;也可以相对的、由操作系统或当前程序所处环境决定的, 如CP_ACP、CP_OEMCP、CP_THREAD_ACP等。比如下面函数就可以把简体中文编码的文本转化为Unicode编码:
如果要把繁体中文编码的文本转化为Unicode编码, 只要把上面代码中的MultiByteToWideChar函数的第一个参数改为950就可以。依次类推, 如果要把UTF8编码的文本转化为Unicode编码, 只要把上面代码中的MultiByteToWideChar函数的第一个参数改为CP_UTF8就可以。
函数WideCharToMultiByte的功能是把宽字节字符串转化为多字节字符串, 它的第一个参数也是指与多字节字符相关的代码页, 用法与MultiByteToWideChar基本相同。
4 编程实现
4.1 新建项目
启动Visual Studio 2010, 在“文件”菜单上, 指向“新建”, 然后单击“项目”打开“新建项目”对话框。从Visual C++项目列表中选择“MFC应用程序”, 在“名称”框中键入“changeCharactorSet”, 然后单击“确定”按钮。在“MFC应用程序向导”的“应用程序类型”中, 选择“基于对话框”, 然后不停地点击“下一步”, 最后点击“完成”即可。本项目的字符集要设置为“使用Unicode字符集”。
4.2 在对话框中放置控件
在对话框中放置如图1所示的各个控件, 在表1中, 按照从左到右, 从上到下的顺序对这些控件的类型、ID、标题、相关变量、作用等作了具体的介绍。
4.3 编写用于调用的函数代码
编写下列7段函数代码:
(1) 函数UnicodeToGBK:功能是把Unicode编码的宽字符串转化为GBK编码的多字节字符串:
(2) 函数UnicodeToBig5;功能是把Unicode编码的宽字节符串转化为Big5编码的多字节字符串;只要把函数UnicodeToGBK的函数体中所调用的函数WideCharToMultiByte的第一个参数由936改为950即可。如下所示:
(3) 函数UnicodeToUtf8;功能是把Unicode编码的宽字节符串转化为UTF8编码的多字节字符串。如下所示:
(4) 函数Big5ToUnicode;功能是把Big5编码的多字节字符串转化为Unicode编码的宽字符串。
(5) 函数UTF8ToUnicode;功能是把UTF8编码的多字节字符串转化为Unicode编码的宽字符串。只要把函数Big5ToUnicode的函数体中所调用的函数MultiByteToWideChar的第一个参数由950改为CP_UTF8即可, 如下所示:
(6) 函数GBKToUnicode;功能是把GBK编码的多字节字符串转化为Unicode编码的宽字符串。
(7) 编写一个函数, 用于改变文件名称, 它所生成的新文件名称是由旧文件名称加上当前时间所组成。
4.4 添加各个命令按钮的单击响应消息
(1) 标题为“Unicode转为GBK”命令按钮的单击响应消息:
(2) 标题为“Unicode转为Big5”命令按钮的单击响应消息代码与“Unicode转为GBK”的单击响应消息代码基本相同, 只要把其中所调用的函数UnicodeToGBK改为UnicodeToBig5即可。
(3) 标题为“Unicode转为UTF8”命令按钮的单击响应消息代码与“Unicode转为GBK”的单击响应消息代码基本相同, 只要把其中所调用的函数UnicodeToGBK改为UnicodeToUtf8即可。
(4) 标题为“GBK转为Unicode”命令按钮的单击响应消息:
(5) 标题为“Big5转为Unicode”命令按钮的单击响应消息代码与“GBK转为Unicode”的单击响应消息代码基本相同, 只要把其中所调用的函数GBKToUnicode改为Big5ToUnicode即可。
(6) 标题为“UTF8转为Unicode”命令按钮的单击响应消息代码与“GBK转为Unicode”的单击响应消息代码基本相同, 只要把其中所调用的函数GBKToUnicode改为UTF8ToUnicode即可。
(7) 多字节字符编码之间相互转化时需要以Unicode编码作为中介。标题为“GBK转为UTF8”命令按钮的单击响应消息:
(8) 标题为“GBK转为Big5”命令按钮的单击响应消息代码与“GBK转为UTF8”的单击响应消息代码基本相同, 只要把其中所调用的函数UnicodeToUtf8改为UnicodeToBig5即可。
(9) 标题为“Big5转为GBK”命令按钮的单击响应消息代码与“GBK转为UTF8”的单击响应消息代码基本相同, 只要把其中所调用的函数GBKToUnicode改为Big5ToUnicode, 把UnicodeToUtf8改为UnicodeToGBK即可。
(10) 标题为“Big5转为UTF8”命令按钮的单击响应消息代码与“GBK转为UTF8”的单击响应消息代码基本相同, 只要把其中所调用的函数GBKToUnicode改为Big5ToUnicode即可。
(11) 标题为“UTF8转为Big5”命令按钮的单击响应消息代码与“GBK转为UTF8”的单击响应消息代码基本相同, 只要把其中所调用的函数GBKToUnicode改为UTF8ToUnicode, 把UnicodeToUtf8改为UnicodeToBig5即可。
(12) 标题为“UTF8转为GBK”命令按钮的单击响应消息代码与“GBK转为UTF8”的单击响应消息代码基本相同, 只要把其中所调用的函数GBKToUnicode改为UTF8ToUnicode, 把UnicodeToUtf8改为UnicodeToGBK即可。
5 结语
介绍了使用VC++编程实现转换文本文件字符编码的方法, 可使文本文件的字符编码在Unicode、Big5、UTF8、GBK等编码之间实现自由转换。所有代码均在VC++2010下通过测试, 实验证明该方法简易实用, 代码的可移植性很强。经过编码转换后的有些文件, 使用Windows操作系统中的“记事本”程序可能无法正确打开, 解决方法是:用鼠标右击该文件, 在弹出菜单的打开方式中选择Microsoft Office Word, Word程序启动后会提示选择“使文档可读的编码”, 选择合适的文本编码, 就能把编码转换后的文件正确打开。
摘要:介绍了使用VC++实现转换文本文件字符编码的方法, 可使文本文件的字符编码在Unicode、Big5、UTF8、GBK等编码之间实现自由转换。
关键词:VC++,字符编码,Unicode,Big5,UTF8,GBK
参考文献
[1]陈小荷.现代汉语自动分析——Visual C++实现[M].北京:北京语言文化大学出版社, 2000.
[2]D.S.Malik.C++编程——数据结构与程序设计方法.晏海华, 等译.北京:电子工业出版社, 2003.
自编头文件的C-MEX混合编程 篇4
MATLAB在科学研究与工业技术开发方面有着极为广泛的应用,具有强大的矩阵运算、数据处理和图形显示功能,其输出结果可视化,编程效率极高,极少的代码即可实现复杂的运行。利用这一完整的数学平台,用户可以快速实现十分复杂的功能,极大地提高了工程分析计算的效率。但与其他高级程序相比,Matlab使用一种脚本语言,它的执行是逐行解释执行的,也就是边解释边执行,执行效率非常低,这就是常常看到的在开发一些复杂的算法时,通常会发现程序执行很慢[1]。
C / C + + 语言是目前最为流行的高级程序设计语言之一[2]。它的功能丰富、表达力强、使用灵活方便、应用面广、目标程序高、可植入性好,适合作为系统描述语言,既可以用来编写系统软件,也可以用来编写应用软件。在工程实践中,用户使用MATLAB进行大规模数据处理时,会碰到复杂的循环语句程序,以及多程序调用多算法函数的情况。MAT-LAB往往由于执行效率的问题而显得力不从心,这时可以使用C/C + + 语言进行算法的设计,然后在MATLAB环境中调用,从而大大提高数据处理的效率[3]。在C/C + + 与MAT-LAB混合编程过程中,使用C-MEX文件是一种常用的方法。本文基于MATLAB7.0和VC6. 0开发环境,自编声明算法实现函数的头文件,在接口函数mex Function( ) 程序中包含该头文件,实现C - MEX的混合编程。
1 MEX 文件的环境配置
实现MEX文件的编写与编译首先需要对MEX编译器进行配置,生成配置文件[1]。对MATLAB编译应用程序mex的配置命令为:
> > mex - setup
然后按系统提示进行选择,本文选择Microsoft VisualC + + 6. 0作为编辑器。
2MATLAB 调 用 自 编 头 文 件 的MEX 文件
所谓MEX是MATLAB Executable的缩写,它是一种“可在Matlab中调用C( 或Fortran) 语言的衍生程序”[4]。C语言MEX文件的源程序主要由两个截然不同的部分组成,分别用于完成不同任务。第一部分称为计算程序,它包含了所有实际需要完成计算功能的源代码,用来完成实际的计算工作,即用户以前所编写的算法和程序; 第二部分称为入口子程序,它是计算子程序同MATLAB环境之间的接口,用来完成两者之间的通信任务。它定义被MATLAB调用外部子程序的入口地址,MATLAB系统向子程序传递的子程序参数、子程序向MATLAB系统返回的结果参数,以及调用计算功能子程序等[1]。MEX源文件的两个组成部分既可以存放在一个文件中,也可以分为两个文件来存放。
当MEX源文件的计算程序和入口子程序分开存放时,即算法实现函数和接口函数mex Function( ) 分别在两个程序中,这时可以自编一个头文件,声明该算法实现函数,然后在新的程序中定义算法实现函数。注意编写的入口子程序必须包含自编的头文件。主要实现步骤如下:
1) VC6. 0的编译环境设置。添加相应的头文件的路径:打开菜单【Tools: Options. . . 】选择”Directories”选项卡。在”Show Directories for”组合框中选取”Include files”,添加”D: PROGRAM FILES MATLAB R2010A EXTERN IN-CLUDE”; 添加相应的库文件的路径: 在”Show Directories for”中选取”Library files”添加”D: PROGRAM FILES MATLAB R2010A EXTERN LIB WIN32 MICROSOFT”; 注意根据用户MATLAB安装位置,修改相应目录。
2) 创建工程,并在工程中添加入口子程序和计算程序两个C文件。
3) 在工程中添加声明计算程序中定义的算法实现函数的自编头文件。
4) 为工程添加DEF文件。
5) VC6. 0的工程设置。添加需链接的库文件: 打开菜单【Project: Setting】选取”Link”选项卡,在”Object/Li-brary Modules”里添加libmx. lib、libmat. lib、libmex. lib和libeng. lib; 动态库调试程序路径的设置: 打开菜单【Pro-ject: Setting】选取”Debug”选项卡,在”Executable debug forsession”中选择matlab. exe作为调试程序。
6) 调试程序,生成可执行mex文件。
3 实例说明
下面通过一个简单的实例说明MATLAB如何调用含自编头文件C/C + + 生成可执行mex文件。
有一个用C语言写的函数,实现了一个功能,如一个简单的函数:
double add( double x,double y)
{ return x + y; }
通过C - MEX混合编程生成mex文件实现在MAT-LAB使用它,如输入:
> > a = add( 1. 1,2. 2)
a = 3. 300 0
mex文件实际上是一个特别的动态链接库,输出函数为mex Function( ) 。当使用C - MEX混合编程时,使用C/C + + 中的Win32 Dynamic Link Library、MFC APPWizard( DLL) 静态链接库和扩展MFC动态链接库创建MEX程序[3]。首先根据章节2的步骤1做好VC6. 0编译环境设置。这里新建一个MFC App Wizard( dll) 工程,工程名为add,选择使用静态连接的MFC的DLL。在工程中添加入口子程序add. c、计算程序a. c、自编头文件add. h。为了方便读者阅读,将主要程序代码源代码整理如下:
根据章节2的步骤5做好工程设置后,打开该工程DEF文件,并对其内容进行编辑,即在Exports后面添加一行”; mex Function( ) ”。按F5运行程序调试,编译连接通过后,MATLAB作为调试 路径自动 启动。在CommandWindow里输入:
> > mex – v add. c a. c
成功生成add. mexw32,MATLAB便可以像调用m文件那样直接调用该文件。在MATLAB的Command Win-dow里输入:
> > a = add( 1. 1,2. 2)
a = 3. 3000
这与单独使用C/C + + 编写程序的运行结果一致。上述实例中只含有一个子程序a. c。如果入口子程序需调用多个算法函数,只需要在自编的头文件add. h中声明这些函数,在新的子程序中编写算法函数代码。需要注意的是在MATLAB的Command Window里输入的mex命令需包含主程序( 入口程序) 以及所有的子程序。
4 结语
简单阐述了基于VC和MATLAB的自编头文件的C- MEX混合编程方法,实现了两种编程语言的无缝链接。对于多程序调用多个子程序的复杂情况,该方法可供工程人员进行参考,方便编程,提高数据处理效率。
摘要:当使用MATLAB进行大规模数据处理时,一个工程中包含了多个程序,而且各程序需交叉调用多个子程序,这将导致程序执行效率低下。基于VC与MATLAB的自编头文件的CMEX混合编程可以有效解决这一问题。该方法在C/C++中自编一个头文件,声明需要用到的所有算法实现函数,并在另外的程序中编写这些函数的代码,通过mex Function接口函数实现VC与MATLAB混合编程。
文件编程 篇5
为解决这一问题,通常的方法是在Lustre的上层加入一个并行I/O库,通过该库将多个不连续小数据块的I/O操作转化为对并行文件系统中的少量连续大数据块的实际I/O操作,最大程度地利用并行文件系统所提供的带宽,减少I/O瓶颈对程序性能的影响,在库的级别上优化并行I/O的访问效率。本文提出了一种基于MPI-IO库(并行I/O库)编程接口的改进方案,以保证在小数据整合为大数据的过程中进一步减少进程间的通信量和时间消耗,从而提升集群并行效率,为传统MPI集群的升级改造提供新的思路或新的方法。
1 Lustre文件系统性能介绍
Lustre是面向集群的存储架构,是基于Linux平台的开源集群(并行)文件系统。由于其提供了与POSIX兼容的文件系统接口,从而使其扩展了应用领域。Lustre拥有高性能和高扩展性两个最大特征。高性能是指它能够支持数万客户端系统、PB级存储容量和数百GB的聚合I/O吞吐量;高扩展性是指由于Lustre是Scale-Out存储架构,由此可以借助强大的横向扩展能力,只要以增加服务器的方式便可扩展系统总存储容量和系统总性能。
Lustre文件系统主要由元数据服务器MDS(Metadata Server)、对象存储服务器OSS(Object Storage Server)、客户端(Lustre Client)三部分组成。MDS用于存储数据描述信息,管理命名空间和目标存储地址;OSS进行实际的数据存储,提供文件I/O服务;Lustre Client则运行Lustre文件系统,并作为集群计算端与MDS和OSS进行信息交互[2]。如图1所示。
由于Lustre设计的初衷是要提高数据访问的并行性和扩展聚合I/O带宽,所以在设计过程中广泛采用了元数据与数据分离、数据分片策略、数据缓存和LNET网络等技术。而在这些设计中,数据分片设计和后端改进的EXT3文件系统非常适合大数据连续I/O访问,所以Lustre在大数据应用中其性能表现得非常好。但对于不连续小数据I/O而言,由于Lustre在读写文件前需要与MDS进行交互,以获得相关的属性和对象位置信息,从而增加了一次额外的网络传输和元数据访问的开销(与传统本地文件系统相比)。若有大量频繁的小数据读写时,Lustre客户端上Cache的作用将失效,其命中率也会大大降低。如果数据小于物理页大小,则还会产生额外的网络通信量,小数据访问越频繁,则网络开销也就越大,进而影响Lustrer的总体I/O性能。OST(对象存储目标服务器)后端虽然采用了改进的EXT3文件系统,但它对小数据的读写性能存在先天的缺陷,其元数据访问效率也不高,且磁盘寻址延迟和磁盘碎片问题严重。所以,Lustre的设计决定了它对小数据I/O的表现,其实际I/O带宽远低于所提供的最大带宽。
因此Lustre集群及其并行架构非常适合众多客户端并发进行大数据读写的场合,但对于不连续小数据的应用是不适合的,尤其是海量小文件应用LOSF(Lots Of Small Files)。
2 MPI-IO对并行I/O的实现
MPI-IO是一个并行I/O库,作为MPI-2规范的一部分,提供了执行可移植的、用户级的I/O操作接口。MPI可以通过该接口在文件和进程间传送数据,如图2所示。ROMIO是MPI-IO的一个具体实现,而ADIO是位于并行文件系统和上层I/O库ROMIO之间的软件层,它的作用是使得上层并行I/O库的实现具有更好的移植性和更好的I/O访问效率。由于ADIO可以在不同的并行文件系统上实现,提供了一组最基本的并行I/O访问的函数,所以I/O库只需要在ADIO上实现一次,就可以通过ADIO实现对所有底层文件系统的访问。一些特定的并行文件系统的优化通常也在ADIO层上实现,所以ADIO的实现使得上层库可以透明访问底层并行文件系统,而不必考虑底层并行文件系统方面的细节。正基于此,本文所提出的改进策略也是在ADIO上实现的[3]。
MPI-IO可分为非集中式(noncollective I/O)和集中式(collective I/O)两种I/O操作。
非集中式I/O操作,是MPI-IO的普通I/O操作方式。在此操作下,若每个进程访问文件中存放位置不连续的小片数据时,往往是对每一个小片连续的数据使用单个独立函数来读写数据,就像在Unix或Linux中一样,所以这种方法I/O延迟较大,系统开销也较大。但对于连续大数据的读写,此种方式可以利用操作系统缓存和Lustre数据分片等技术,充分发挥并行I/O的优势。
而对于集中式I/O操作,由于集中式I/O具有并行程序的对称性,当I/O发生时集群中所有节点均运行到各自子进程的相同位置,所以各子进程会在一个很短的时间间隔内各自发出I/O请求,集中式I/O便合并这些小的读写请求,使之整合成为大的读写请求,从而减少磁盘磁头移动和I/O次数,提高I/O读写速度。本文的改进型编程接口也正是基于此类I/O方式将多个不连续的小数据I/O整合为少量的大数据I/O,进而再利用底层的Lustre并行文件系统对大数据I/O的优势,使得集群的整体并行效率得到提升。集中式I/O有很多实现方案,ROMIO是采用基于Client-level的两阶段法来实现集中式I/O的。
两阶段法通过分析集中式I/O请求对数据的总体需求,将一次I/O操作分成两个阶段来完成。第一个阶段是所有计算进程交换彼此I/O信息,确定每个进程的文件域,从磁盘上读取相应I/O数据;第二个阶段是按照应用需求将缓冲区的数据交换分配到各进程的用户缓冲区,写操作与读操作类似。在这两个阶段中,进程之间需要进行相互通信,同步同一通信域中的相关进程的数据信息,从而得到程序总体的I/O行为信息,以便整合各个进程独立的I/O请求。如图3所示。
第一阶段:
(1)确定参加读操作的通信域中所有进程,并进行同步;
(2)确定每个进程各自需要读取的一个包含所请求数据的连续的文件区域;
(3)每个进程根据自己的文件区域进行I/O访问,并将这些数据存放在一个临时缓冲中,同步所有进程到每个进程的I/O操作结束。
第二阶段:
(1)进行I/O数据的置换,将缓存中数据发送给目标进程;
(2)同步所有进程到数据置换结束。
写操作类似于读操作。两阶段I/O整合了多个进程所请求的数据范围,访问到稠密的数据区域的可能性比较大,因此对于非连续数据分布比较分散情况,该算法有明显的性能优势。但是,两阶段I/O的性能很大程度上依赖于MPI所提供的高效的数据传递机制,即消息传递机制。如果MPI无法提供比底层文件的聚合带宽更快的速度,那么在两阶段I/O中数据传递所带来的额外开销将会大大降低该I/O方式的性能[4]。
3 改进的并行I/O编程接口
从上述分析可知,MPI-IO在整个集群系统中起到了承上启下的作用,向上连接MPI计算系统,向下连接Lustre并行文件系统,所以其效率的高低一直影响到集群的整体并行I/O效率。为此,本文所提方案需要对以下三个方面进行改进。
(1)如何界定数据块的大小
在Linux 32 bit的操作系统中,一个页面大小为4 KB,同时也是Lustre网络传输的基本单位。由参考文献[4]可知,当数据大小不到一页或不同进程同时读写一个页面中数据的时候,Lustre的I/O性能最差,甚至不如传统的本地文件系统;若数据块的大小不足一个条块(数据分片),则客户端每次读写只与一个OST交互信息,没有从真正意义上实现Lustre并发I/O的功能。
所以如何界定数据块的大小,是提高系统并行效率的关键。本文依据参考文献[4]所提供的实验数据,设定Lustre条块大小为64 KB,若数据块小于该值,则视为小数据,按本文改进的集中式MPI-IO编程接口操作;若数据块大于该值,则视为大数据,按非集中式MPI-IO接口操作。
(2)采用直接I/O模式以减少内核消耗
Lustre文件系统底层采用Portals协议进行数据传输,上层则连接Linux操作系统的VFS(虚拟文件系统)。应用程序所有的I/O操作都必须通过Linux的VFS才能进入Lustre。在Linux操作系统中,读写模式分为文件缓存(Cache)模式和直接(Direct)模式。
对于Lustre文件系统而言,由于该文件系统是以页面为I/O的基本单位,所以当n个进程去读取仅需一个页面的数据流量时,若采用缓存机制,则实际系统需要n个页面的I/O流量才可完成,而随着I/O请求的增加,这种额外的开销将会越来越大;而对于写操作,缓存机制在小数据I/O的问题则更加突出。在写操作时,由于每个进程必须获得该页面上的文件锁才能进行,以保证对这个文件写操作的独占性。但是当一个进程获得锁后,其他进程只有等待该进程结束操作,所以对于同一个页面的写操作是串行的,系统不能发挥程序的并行作用而造成性能下降。并且,当数据块小于4 KB时(不足一个页面),文件缓存的作用已经不明显,反而使得I/O调用的开销越来越突出。因此,Lustre在非连续小数据I/O时,采用可以绕过系统缓存的直接I/O方式,直接将文件数据传递到进程的内存空间则是一个可行的办法。
(3)减少集中式I/O第一阶段的通信量
由前述可知,MPI-IO中的集中式I/O操作是采用两阶段法来进行的。该算法第一阶段,通信域中所有进程同步操作方式采用广播的形式,即每个进程的数据信息通过广播的方式发送给通信域里的所有进程。也就是说,若有n个进程参与此次集中I/O操作,则整个过程中进程间需要n×(n-1)次点对点通信才能完成。这样,随着集群节点数的增加,进程间的通信次数势必会给I/O性能造成较大影响。所以,本文的优化方案就是以减少进程间的通信量为目的,尽量减少进程间的通信次数,以提高整体的I/O性能。这里,选取通信域中的一个进程作为主进程,通过它从其他进程收集各自的I/O信息进行相关的计算,并汇总整合本通信域中的所有I/O信息,再将其进行广播,以达到进程间I/O信息同步的目的。若还是n个进程参与此次集中式I/O操作,则进程间的点到点通信次数将会降到2×(n-1)次。显然,改进后的策略可以明显降低进程之间的通信次数,进而提高了整个集中操作的I/O性能。
4 实验与分析
为验证改进方案的可行性,在传统的MPI集群上做了相应的测试。测试环境为7个节点的集群系统(3台OST,3台客户端,1台MDS),每个节点包含一颗PIV处理器和2 GB内存,40 GB的硬盘,操作系统采用Redhat Linux Enterprise 5(内核为2.6.18),并行集群软件为MPICH2.1,并行文件系统为Lustre 1.0.2。在测试程序中,在3个客户端上同时运行3个进程读写一个文件的不同部分,测试文件为2 GB,其他的什么都不做,来测试改进前后的I/O读写带宽(改进前的读写方式为Lustre系统的普通读写方式)。
如图4所示,先分析改进前Lustre系统的读写情况:数据块为64 KB时,由于大部分RPC数据包中都包含了数十个页,从而减少了I/O调用的次数,效率达到最高;而当数据块小于64 KB(一个分片的大小)时,此时客户端每次读写只能与一个OST交互信息,从而无法使用Lustre分片技术的并发功能,造成这一阶段读写性能不佳;而当数据块大小为4 KB或者小于4 KB时,性能则快速下降,特别是写操作,性能下降得更为严重,这主要是因为数据块的大小不足1个页面(4 KB),此时大部分的RPC数据包仅仅包含1个页面而导致I/O效率不高,特别是在不同进程同时读写一个页面中的数据的时候,则性能更糟。
由于改进后的方案采用了MPI-IO中的改进集中式操作和直接I/O技术,使得数据块在64 KB以下区段性能差异较大。但对于数据块大于64 KB时,改进前后的差异不大,这是因为改进后的编程接口将大于64 KB的数据包均视为大数据,不做修改,所以与改进前基本一致;对于数据包小于64 KB时,改进后的读写明显放缓了I/O读写下降的速度,且读操作和写操作的速度基本持平,这主要是因为改进后的方案中使用了MPI-IO中的改进集中式操作,由于MPI-IO中可以使用单个函数来访问非连续数据,且可多次使用,同时MPI-IO也允许一组进程在同一时间访问一个普通文件,在进行多次读写时,I/O的访问开销不但可以降低,而且还可将定义开销分摊到各次访问,从而改善了小数据I/O操作的性能。
总之,基于Lustre的MPI-IO编程接口的改进方案是可行的。在该机制下构建的MPI集群系统能够有效解决I/O瓶颈,提升并行计算速度,与传统的MPI集群相比性能提高空间很大。但是由于研究刚刚起步,还需要在总结现有不足的基础上,继续实践、改进和完善。
摘要:针对传统MPI集群并行I/O效率不高的问题,通过分析Lustre并行文件系统的特点和MPI-IO集中式I/O操作的算法,提出了一种基于MPI-IO编程接口的改进方案,用以改善集群I/O瓶颈,提高I/O并行效率,并通过实验验证了该方案的可行性。
关键词:并行文件系统,编程接口,集中式I/O,Lustre,MPI-IO
参考文献
[1]钱迎进,金士尧,肖侬.Lustre文件系统I/O锁的应用与优化[J].计算机工程与应用,2011,47(03):1-5.
[2]BRAAM P J.Lustre:A scable,high-performance filesystme[M].Lustre Whitepaper Version 1.0,2002.
[3]刘辉,胡静,王振飞,等.MPI-2中的并行I/O的使用分析[J].计算机工程,2003,29(2):229-232.