Unified Parallel C - 统一并行C语言。用于并行计算和编程,是C语言的一个扩充版本。
转载任何内容都请注明出处。若用于任何教育或商业目的请先联系作者,谢谢。

3.1 UPC指针

和C差不多,但根据UPC公有变量和私有变量的不同,两两组合有4种指针

1 int *p1; // private to private
2 shared int *p2; // private to shared
3 int *shared p3; // shared to private
4 shared int *shared p4; // shared to shared

P3是公有指针,指向私有变量。不是非法,但应该避免,因为这与UPC的分布式共享内存模型的设计初衷(公有空间对所有线程可见,私有数据仅对相关联的线程可见)相违背。

3.2 指针的算术

1 #define N 16
2 shared int x [N];
3 shared int *dp, *dp1;
4 dp = &x [5];
5 dp1 = dp + 9;
1 #define N 16
2 shared int x [N];
3 shared int *shared dp;
4 shared int *dp1;
5 dp = & x [5];
6 dp1 = x+MYTHREAD;

dp只有一个实例,指向x[5]。dp1的偏移量是线程id,正好是每个线程的首个被分配到的元素

 若将最后一行改成:

dp1 = &x [MYTHREAD];

则dp1指向每个线程的首个数组元素的地址

为追踪共享数据,UPC的公有指针(即指向一个公有对象的指针)包含三个域:thread, phase, virtual address

 thread域指出所指向数据相关联的线程;block address 指出块地址(block address);phase指出这个块内的数据项的所在位置。

通过给出一个有相同分块信息(blocking factor)的指针,可用于遍历分块数组(blocked array),看下面

1 #define N 16
2 shared [3] int x[N], *dp, *dp1;
3 shared int *dp2;
4 ...
5 dp = &x [5];
6 dp1 = dp + 9;
7 dp2 = dp + 3;

若4线程,数据布局为

 其他都好理解,为什么dp2指向x[12]呢?因为dp和dp1声明时提供了分块信息,他们把共享空间看作3个以所指向类型的数据为一块而构成的许多数据块。而dp2仍是默认方式,他不知道数据是有三个元素的一块,因此每次增加他都移向下一个线程的一个新元素。而dp1可以用于访问分块数组中的每个元素。

3.3 指针类型转换

shared->private 线程信息丢失;private->shared 出错;private->自己本地的共享数据,这是可以的,比如

shared int x [THREADS];
int *p;
p = (int *) &x [MYTHREAD]; /* p points to x [MYTHREAD] */

 再看一个例子

1 #define N 4
2 shared int x[N][N];
3 shared int *dp1;
4 int *dp2;
5 dp1 = x+MYTHREAD;
6 dp2 = (int *)dp1+MYTHREAD;

分析:x是以轮转法分配的共享数组,元素为存放在公有空间的整型;dp1指向每个线程的第一个数组元素;dp2相对于dp1的偏移量等于本线程id。

如图:

 看一个较难的例子

shared int x [N];
shared [3] int *dp=&x[5], *dp1;
dp1 = dp + 9;

数据分布竟然是···

 和之前某个例子的区别在于,这里的数组并没有按块划分,但指针却有分块信息。因此从dp指向的那个元素开始,三个三个为一块。

3.4指针信息与相关函数

size_t upc_threadof (shared void *ptr);

参数:ptr是一个私有指针,指向任意共享类型的对象

返回值:与ptr所指向的公有对象相关联的线程的id

size_t upc_phaseof (shared void *ptr);

返回值:phase

size_t upc_addrfieldof (shared void *ptr);

返回值:ptr所指向的公有对象的本地地址(返回值依赖于函数实现)

举个例子,以下程序使用thread 0打印指针的三个域,其中地址域以10进制显示,线程数和phase域以2进制显示

 1 //PrintPointerElements
 2 #include <stdio.h>
 3 #include <upc.h>
 4 #include <upc_relaxed.h>
 5 #define BLOCKING_FACTOR 3
 6 #define SIZE BLOCKING_FACTOR*THREADS
 7 shared [BLOCKING_FACTOR] int buffer [SIZE];
 8 int main(void)
 9 {
10   int i;
11   shared [BLOCKING_FACTOR] int *buffer_ptr;
12   if(MYTHREAD == 0)
13   {
14     buffer_ptr = buffer + 5;
15      for(i=0; i<SIZE; i++, buffer_ptr++)
16     {
17       printf("&buffer [%d]: ", i);
18       printf("THREAD: %02d \t ",
19       upc_threadof(buffer_ptr));
20       printf("ADDRESS: %010Xh \t ",
21       upc_addrfield(buffer_ptr));
22       printf("PHASE: %02d\n",
23       upc_phaseof(buffer_ptr));
24     }
25   }
26 return 0;
27 }

第14行,使得buffer_ptr指向第二个块的最后一个元素buffer[5],打印结果应该是:线程号=1,phase=2

buffer_ptr = upc_resetphase(buffer_ptr);

函数功能:重置指针的phase,使其指向当前块的首元素。

如果把它放在上面程序的打印之前,phase值会变成0而不是2

这些函数的具体实现:略

3.5更多的例子

// Matrix Squaring
shared double A [THREADS][THREADS], A_Sqr [THREADS][THREADS];
int main(void)
{
    shared double *rows_ptrs [THREADS]; // Points to the rows
    double *cols_ptr; // Points to the local shared columns
    int i, j, k;
    double d;
    //Initialize the pointers
    for(i=0; i < THREADS; i++)
    {
        rows_ptrs [i] = &A [i][0];
    }
// Privatize (local shared to private)
    cols_ptr = (double *)&A [0][MYTHREAD];
// Matrix squaring computation
    for(i=0; i<THREADS; i++) // for each row
        upc_forall(j=0; j<THREADS; j++; j) // each thread
        { // computes one element
            d = 0;
            for(k=0; k<THREADS; k++)
                d += *(rows_ptrs [i]+k) * *(cols_ptr+k);
            A_Sqr [i][j] = d; // write to A_sqr [row i][col j]
}
return 0;
}                                    

上面是一个计算矩阵平方的程序

cols_ptr = (double *)&A [0][MYTHREAD];//&A[0][i]是一个共享的引用,而cols_ptrs是私有指针,需要进行类型转换

这里使用了一个元素类型为指向共享对象的私有指针的数组,用以访问每一行。也可以用单指针,见下面的例子:
 1 // Matrix Squaring (single pointer)
 2 shared double A [THREADS][THREADS], A_Sqr [THREADS][THREADS];
 3 int main(void)
 4 {
 5     shared double *rows_ptrs;
 6     double *cols_ptr;
 7     int i, j, k;
 8     double d;
 9 // Initialize the pointers
10     rows_ptrs = &A [0][0];
11     cols_ptr = (double *)&A [0][MYTHREAD];
12 // for each row
13     for(i=0; i<THREADS; i++, rows_ptrs+=THREADS)
14         upc_forall(j=0; j<THREADS; j++; j) // for each local
15 // shared column
16         {
17             d = 0;
18            for(k=0; k<THREADS; k++)
19                 d += *(rows_ptrs+k) * *(cols_ptr+k);
20             A_Sqr [i][j] = d; // write to A_sqr [row i][col j]
21         }
22 return 0;
23 }            

如果将这个程序的算法封装到一个函数中,则是:

 1 // Matrix Squaring
 2 shared double A [THREADS][THREADS], A_Sqr [THREADS][THREADS];
 3 void mat_squaring(shared double (*dst)[THREADS],
 4                             shared double (*src)[THREADS])
 5 {
 6     shared double *rows_ptrs [THREADS];
 7     double *cols_ptr;
 8     int i, j, k;
 9     double d;
10 // Initialize the pointers
11     for(i=0; i<THREADS; i++)
12         rows_ptrs[i] = &src[i][0];
13 // Privatize (local shared to private)
14     cols_ptr = (double *)&src[0][MYTHREAD];
15 // Matrix squaring computation
16     for(i=0; i<THREADS; i++) // for each row
17         upc_forall(j=0; j<THREADS; j++; j) // each thread
18         {
19             d = 0;
20             for(k=0; k<THREADS; k++)
21             d += *(rows_ptrs[i]+k) * *(cols_ptr+k);
22            dst[i][j] = d;
23         }
24 }
25 int main(void)
26 {
27     mat_squaring(A_Sqr, A);
28     return 0;
29 }                

注意:共享的对象全都定义在函数外部,以保证他们不再动态范围内(局部变量出了就被销毁)

3.6总结

注:  

本系列所有内容均来自《UPC: Distributed Shared Memory Programming》。这些文章是为了期中考试临时抱佛脚的结果···若有谬误,敬请指教。

All contents in this article are from <UPC: Distributed Shared Memory Programming>, if this has violated your copyright, contact me immediately at [email protected]. Thank you.

02-10 20:48