OpenMP使用经验

为了利用OpenMP来加速C/Fortran程序,我记录一些阅读OpenMP API Version 3.0 Specification (May 2008)的经验。另外,这篇文章主要关注C语言中OpenMP的使用经验。

本文包括 摘要、经验和其他注意事项、参考 三部分。

摘要 (按照Specification的顺序)

(1)第一章是Glossary, 定义了各种OpenMP使用的名词(terms),例如:construct, directive, clause, task, tied task … 这个可以在看不懂的时候返回来查询。其中有一个被多次使用的名词是sentinel,这似乎是Fortran中使用OpenMP时应该使用的名词,和C并没有关系。

(2)2.2:  _OPEN 这个macro 被定义成yyyymm形式,表示OpenMP API的版本

(3) parallel construct:表示紧挨着的程序可以parallel运行。用#pragma omp parallel 来使用,当程序遇到parallel construct之后,会用固定个数的threads形成一个team去完成work。至于有多少个threads,这不一定,可参考2.4.1中决定threads number的算法。注意,当parallel里一个thread结束的时候,其他的threads都会被结束 (If execution of a thread terminates while inside a parallel region, execution of all threads in all teams terminates. If execution of a thread terminates while inside a parallel region, execution of allthreads in all teams terminates.)

(4) worksharing construct:有4类:loop;sections constructs;single construct;workshare construct

对于worksharing loop construct来说,有5种scheduling,即安排工作,的方式。static(静态分配), dynamic(每一个thread动态要求一个chunk of iterations), guided(execution thread负责给其他threads分配chunks), auto(根据compiler和system的情况决定), runtime(运行期决定)。2.5.1.1给出了一个决定scheduling的流程图

四种类型的区别:

loop:在C中紧接着一个for循环

section:与loop类似,不必要是for循环,只要是structure block就行

single:只能由一个thread执行(不一定是master thread)

worshare:只有Fortran中出现,是把structure block分成若干份,每一份由一个thread执行

(5)2.6节讲了结合parallel construct 和worksharing construct,就是这两个construct可以合在一起用。然后分3个小节介绍了parallel loop construct (相当于loop construct 后直接用parallel construct),parallel section construct(相当于section construct 后直接用parallel construct)和parallel workshare construct(相当于worshare construct 后直接用parallel construct)。

(6)2.7节是task construct,这定义了一个task。当一个thread碰到task construct的时候会立刻产生一个task,并按照data-share attribute的指示准备相应的数据环境(data environment)。这个task可能被立刻执行,也可能被延后执行。

注意当task construct带有if clause (if 从句)的时候,当前的 thread会暂停(suspend)当前的task,并切换到刚刚生成的task。这里的if clause中的变量对于task construct后的structure block是引用型的(不是传值,是传引用)。默认的task是tied task (这个task被某个thread suspend后,只能由这个thread来resume)

task scheduling point 是指在这一点可以改变task的状态(如可以被suspended),或是task 结束的位置。包括task construct开始的地方;taskwait construct开始的地方;遇到barrier directive;隐含的barrier 区域; 在tied task region的末尾。

(7)2.8节介绍了master and synchronization constructs,包括master constructs(只有master thread可以执行), critical constructs(同一时间只能有一个thread来执行。可以给critical constructs起名字), barrier constructs(指定一个明确的barrier,举例:在parallel region的explicit tasks必须在barrier之前都完成,之后的程序才能继续执行。注:在C语言中使用有一定限制。), taskwait constructs(等待当前task生成的子程序全部完成), atomic constructs(原子语句,注:只支持+,-,*,/,++,–,|, &,+=,-=,*=等简单运算,原子性只是保证赋值的那一步), flush constructs(保证thread view里的数据和memory的数据相吻合,另外需考虑不同thread执行flush的order,见74页的范例), ordered constructs(保证按照loop region指定的顺序来运行thread).

(8)2.9节是Data Environment数据环境,即并行计算时不同thread间的变量是如何影响的。

construct里变量的数据共享属性(Data-sharing Attribute):提前决定的(private:用threadpriviate声明的,在construct里声明的,for construct里的循环变量;shared:在heap上的,static的变量),显示决定的(在construct上指明的),隐示决定的(default clause可以指定的;如果default clause没有指定,则比较复杂,例如parallel construct中是shared,全部规则见79页)。额外的不能由上面隐式规则推出的可以见92页。 (我认为如果数据共享属性已经复杂到不好看出,那是不是这个程序本身写的太不清晰了!)

不在construct里而是在region里的数据共享属性(Data-sharing Attribute),见2.9.1.1

threadprivate见2.9.2

default的数据共享属性见2.9.3 。包括shared, private,firstprivate(private,且给变量赋初值),lastprivate(private,在task结束后会改变原始变量的值),reduction(做functional programming里的reduction,需要提供运算符。先使用private copy,然后用初始值做reduce,最后更新原始的变量)

数据拷贝从句(Data Copy Clause),见2.9.4

(9)第3章是运行库里的子程序

3.1 所有函数的原型在omp.h中,都是用C做链接(link)的。

3.2 控制执行环境的函数,包括设置/取得线程数,得到最多的支持的线程数,设置线程数的上限等等。

3.3 Lock程序,这是为了给线程加锁而提供的函数,分简单锁(simple lock)和级联锁(nested lock,区别是可以set多次)

3.4 时间程序。只有两个:omp_get_wtime() 返回double型的时间 和omp_get_wtick()返回1秒等于多少个时钟的tick

(10)第4章讲环境变量,可以通过设置环境变量来改变调度方式(schedule type)OMP_SCHEDULE,线程数OMP_NUM_THREADS ,最多的线程数OMP_THREAD_LIMIT等等

(11)第5章有各种各样的样例程序。这样当我们不清楚概念的时候,都可以快速的查看,例如如何使用lock,如何用reduction……

经验其他注意事项:

这个Specification里很有结构化,对于各种construct都给出了Summary,Syntax,Binding(使用范围), Description,Restriction。

网上一些程序中常常显示指明shared variable,这样做可能是为了减少不必要的数据拷贝。

对于多重循环,只对外层循环并行化处理不一定能达到负载均衡。解决方法可以用,把多重循环合并成一层循环,见【4】

参考:

【1】OpenMP Specification Version 3.0 Complete Specifications – (May, 2008). (PDF)

【2】OpenMP C/C++ Summary Card http://www.openmp.org/mp-documents/OpenMP3.0-SummarySpec.pdf

【3】Wikipedia (其中介绍OpenMP语言架构的图很不错)http://en.wikipedia.org/wiki/OpenMP

【4】对多重循环的优化 http://blog.csdn.net/drzhouweiming/archive/2008/05/23/2472454.aspx

【5】OpenMP 编程指南 http://blog.csdn.net/drzhouweiming/archive/2009/04/20/4093624.aspx

How to set up BuyVM with LAMP, WordPress and VPN

在2011年1月份的最后一天,我非常幸运的发现BuyVM.net的每年15美元的VPS计划居然还没卖光。按耐不住的我立刻掏钱。之后就有了本文。我将分三部分介绍如何安装和调试(Optimize) LAMP, 安装及迁移WordPress 和设置VPN.

1. LAMP的安装和优化

如果像我一样用BuyVM.net 每年$15美元的计划,你可以选择Ubuntu 10.10 LAMP 系统,这样你自己就拥有了LAMP整套系统,但是这样的系统不能适应Wordpress程序——内存经常不够用。因此我们必须想办法减少Apache2和MySQL的内存消耗。

对于Apache,通过检查aptitude程序可以发现,Ubuntu安装的是Apache-prefork-mpm版本(版本号中的prefork表示的是和Apache 1.3类似的架构)。这个架构下比较关键的参数是最少和最多的apache进程的个数。如果不限制,则会很容易出现8-10个apache进程,有的进程占内存30-40M,很快你的VPS就会反应变慢,甚至crash。参考apache – How to reduce memory usage on a Unix webserver – Server Fault之后,在/etc/apache2.conf里面,我们需要这样的设定:

StartServers          1
MinSpareServers       1
MaxSpareServers       5
ServerLimit          16
MaxClients           16
MaxRequestsPerChild   0
ListenBacklog        100

为了减少MySQL的内存占用,我们需要改动/etc/my.cnf,有文章(Google “MySQL reduce memory”)建议不直接使用MySQL提供的为small memory使用的配置文件(例如:考虑到Wordpress经常会同时使用多达10个表进行查询),因此给出关键部分([mysqld]部分)的我的参考配置如下:

[mysqld]
user            = mysql
port            = 3306
socket          = /var/run/mysqld/mysqld.sock
skip-locking
key_buffer_size = 1M
max_allowed_packet = 1M
table_open_cache = 10
sort_buffer_size = 64K
read_buffer_size = 256K
read_rnd_buffer_size = 256K
net_buffer_length = 2K
thread_stack = 64K
skip-innodb
# Don't listen on a TCP/IP port at all. This can be a security enhancement,
# if all processes that need to connect to mysqld run on the same host.
# All interaction with mysqld must be made via Unix sockets or named pipes.
# Note that using this option without enabling named pipes on Windows
# (using the "enable-named-pipe" option) will render mysqld useless!
#
#skip-networking
server-id       = 1
# Uncomment the following if you want to log updates
#log-bin=mysql-bin
# binary logging format - mixed recommended
#binlog_format=mixed
# Uncomment the following if you are using InnoDB tables
#innodb_data_home_dir = /var/lib/mysql/
#innodb_data_file_path = ibdata1:10M:autoextend
#innodb_log_group_home_dir = /var/lib/mysql/
# You can set .._buffer_pool_size up to 50 - 80 %
# of RAM but beware of setting memory usage too high
#innodb_buffer_pool_size = 16M
#innodb_additional_mem_pool_size = 2M
# Set .._log_file_size to 25 % of buffer pool size
#innodb_log_file_size = 5M
#innodb_log_buffer_size = 8M
#innodb_flush_log_at_trx_commit = 1
#innodb_lock_wait_timeout = 50

2. WordPress的安装

可以下载Wordpress的最新版,然后用他提供的Famous 5 minutes 安装完毕。我遇到的问题是如何把旧系统(http://zhanxw.dyndns.info/blog)迁移到这个新地址(http://zhanxw.com/blog),那么官方(http://codex.wordpress.org/Moving_WordPress) 提供了英文文档应对这种情况。粗看起来比较复杂,但原理上相当自然:

(1) 备份旧系统的blog文件夹和数据库;

(2) 拷贝这两样并安装到新域名下;

(3) 在新域名下激活系统(就是访问一下,结果登录的时候被转回旧系统);

(4) 到旧系统中在Setting里把主机(domain)改成新的域名;

(5) 把旧系统的blog文件夹和数据库再次拷贝到新的域名下;

(6) 在新的域名下登录,这回就应该没问题了!

3. VPN的安装

主要参考两个文章:

(1).Linode VPS PPTP VPN 安装配置教程 – VPS侦探

(2).Ubuntu 上安装 pptp » jKey.lu

其中按照1的步骤可以进行到iptables命令前,这时候看文章2的iptables命令即可。(注意默认系统不提供iptables,需手动安装)。注意,如果不完成文章2提到的步骤,在Windows 7里面你仍然可以连接到VPN,但无法访问任何网页。

4. 使用BuyVM的其他经验

Web方式的管理界面可以通过http://manage.buyvm.net 来进行。登录之后可以进行开关机,重启,查看CPU、内存、带宽使用情况,还可以通过一个Java Application以Console方式登录到VPS。

为了推广站点,我们可以使用Google Analytics去了解访客的来源,也可以到Goolge Site Admin网页提交自己的站点。

另外,对于我使用的Wordpress,推荐使用的Plugin包括:

(1)SI Captcha : 在访客输入comment时提示CAPTHA

(2)Akismet :防止无聊的Spammer

(3)NextGEN Gallery:提供一个展示自己图片的方式

(4)Shareaholic:在每一个Post下面增加一行,方便访客将内容转发到delicious,twitter……

(5)MathJAX:为Wordpress提供LaTeX语法支持,方便今后输入和显示数学公式

(6)Limit Login Attempt:当用户错误输入密码超过一定次数时,拒绝该用户的‘恶意’猜测。

Trackback and Pingback

讲解trackback/pingback的文章:

http://www.optiniche.com/blog/117/wordpress-trackback-tutorial/

讲解了如何测试对方的wordpress是否支持trackback/pingback,并且检查自己的post是否ping 了对方的post。
这是很实用的技巧。

EDIT:
刚刚发现随便乱用trackback是不礼貌的行为。所以下面的测试现在已经不起作用了。

这个用来测试matrix67 blog的trackback功能。

Trackback: http://www.matrix67.com/blog/archives/2660/trackback

用wordpress的话来说,trackback, pingback都是一个相同的目的,但后者更安全,可靠。

他们的目的(在我这个文章的例子里)是,我可以在我的blog发表comment,同时我的comment会在matrix67的blog里出现。

既然如此,那么来看看需要多久我的comment会在这个网页出现 :)

http://www.matrix67.com/blog/archives/2660

Uninitialized variable makes a mysterious bug

Reason:

I used strtod() function in C, forgot to set errno to 0, then after calling strtod(), the value of errno is unpredictable.

When found the bug:

I tried to use Ptyhon ctypes module. In script mode, python pyCtypes.py always crash but in command line mode, the code becomes all right. It’s mysterious running Python in different ways turns out to give different results.

First, I thought it is clear that Python has some bug, otherwise the same Python code should give the same result.

Then I realized my Python code use a DLL routine using C language, and I recalled in the man page of “strtod”, it says we need to initialize errno value to zero every time before calling.

Example code:
Line 1 should be added to ensure correctness.

        errno = 0;
        vec->value[vec->len++] = strtod(temp, NULL);
        //vector_print(vec);
        if (errno != 0) {
            perror("strtod");
            fprintf(stderr, "%s\n", temp);
            exit(EXIT_FAILURE);
        }

Note:

WordPress supports syntax highlight.

I am using SyntaxHighlighter Evolved http://wordpress.org/extend/plugins/syntaxhighlighter/.

Official documentation mentions another way: http://en.support.wordpress.com/code/posting-source-code/.