MPI-3 中大的計數及相關函式

weixin_34075268發表於2018-07-11

上一篇中我們介紹了非執行緒安全的 Probe 和 MPI-3 中執行緒安全的 Mprobe,下面將介紹 MPI-3 中大的計數及相關函式。

在 MPI-1 和 MPI-2 標準中,所有的通訊和 I/O 函式/方法中的計數引數都使用的是 C 語言 int 或者 Fortran INTEGER 型別,在大多數系統中,它們都是用 32 位來表示的。一個 32 位的 C int 數所能表示的最大整數是 2147483647,超過該值就會發生溢位,此即一些通訊和 I/O 函式中能夠傳送/接收的最大資料型別個數,如果使用 MPI 中預定義的資料型別,如 MPI.INT 或 MPI.FLOAT,則單次能夠傳送/接收的資料量的上限大概是 2 GB。這對一些資料量比較大的應用是一個比較嚴重的限制。

MPI 論壇也意識到這個問題,一種可行的解決方案是為這些函式都定義一個新的版本,在這些新版本中使用 64 位或更大的整數型別,不過這樣會使得 MPI 標準中的函式數量大為增加。MPI 論壇採用了另外一種解決方案,那就是利用 MPI 對派生資料型別的支援,再額外定義一個 MPI_Count 資料型別及若干使用該資料型別的查詢函式。通過此種方法,如果要傳送/接收計數超過 32 位最大整數的某種型別資料,則可以首先由該資料型別作為基本型別建立派生資料型別,只要所傳送/接收的資料按照派生資料型別計數不超過 32 位最大整數,通訊操作就能成功執行,並且可以用這些新定義的查詢函式來得到大的訊息及資料型別的相關資訊,而不至於發生溢位。

MPI-3 新定義的 MPI_COUNT 資料型別被定義為足夠大的整數以使其足以表示 MPI 支援的記憶體地址和檔案偏移。新定義的查詢函式與已經存在的對應函式完成相同的功能,不過在名稱後面加了 _x 字尾,並且 size,lb,extent 等引數都使用了 MPI_Count 資料型別,其 C 語言介面如下:

MPI_Type_size_x(MPI_Datatype datatype, MPI_Count *size)

MPI_Type_get_extent_x(MPI_Datatype datatype, MPI_Count *lb, MPI_Count *extent)

MPI_Type_get_true_extent_x(MPI_Datatype datatype, MPI_Count *lb, MPI_Count *extent)

另外,新增 status 狀態中設定和獲取大的計數, 其 C 語言介面如下:

MPI_Get_elements_x(const MPI_Status *status, MPI_Datatype datatype, MPI_Count *count)

MPI_Status_set_elements_x(MPI_Status *status, MPI_Datatype datatype, MPI_Count count)

mpi4py 3.0.0 支援 MPI-3 標準,因此以上介紹的新特性也可用,不過因為 Python 並不用宣告資料型別,所以我們不用特別地關心 MPI.COUNT 資料型別,另外由於 Python 中的整型數預設就是 64 位的,因此也與大的計數相容。雖然已經存在的無 _x 字尾版本的函式在 MPI-3 中依然可用,但是卻並不推薦使用,應該儘量使用 MPI-3 所新引進的支援大的計數的相應函式。基於這一思想,且為了簡潔,mpi4py 3.0.0 直接包裝和呼叫了新的帶有 _x 字尾的相關函式。因此在支援 MPI-3 的環境下,以下方法及屬性都支援大的計數;但是在不支援 MPI-3 的環境下,它們會呼叫無 _x 字尾的函式版本。

方法介面

MPI.Datatype.Get_size(self)

MPI.Datatype.Get_extent(self)

MPI.Datatype.Get_true_extent(self)

MPI.Status.Get_elements(self, Datatype datatype)

MPI.Status.Set_elements(self, Datatype datatype, Count count)

屬性

MPI.Datatype.size

MPI.Datatype.extent

MPI.Datatype.lb

MPI.Datatype.ub

MPI.Datatype.true_extent

MPI.Datatype.true_lb

MPI.Datatype.true_ub

以上方法和屬性獲取資料型別的型別圖的相關資訊,型別圖及其相關資訊在前面已經介紹過,此處不再贅述。

限制

以上方案雖然能夠解決很大一部分涉及大的訊息和大的計數的問題,但也有若干限制。

集合規約操作

以上解決方案的一個限制是執行大量資料的規約操作,如 MPI.Comm.Reduce 和 MPI.Comm.Allreduce。MPI 預定義的規約算符 只能使用在預定義資料型別上,不能使用在派生資料型別上。因此對大量資料的規約操作,如果使用派生資料型別,則必須自己定義對派生資料型別的規約操作算符(可以通過 MPI.Op.Create 來定義)。

不規則集合操作

另一個限制是在對大量資料的不規則集合操作上,如 MPI.Comm.Gatherv,MPI.Comm.Alltoallv,MPI.Comm.Alltoallw。對 MPI.Comm.Gatherv 和 MPI.Comm.Alltoallv,每個程式可以傳送不同數量但是相同型別的資料。在一些情況下,可能沒法定義一個能滿足所有程式的派生資料型別,比如說各個程式所傳送的資料量不存在一個公約數時。在此種情況下,對大量資料的 MPI.Comm.Gatherv,MPI.Comm.Alltoallv 操作將無法進行。MPI.Comm.Alltoallw 雖然可以使用多個資料型別,但是它的以位元組為單位的偏移引數卻用的是 C 語言 int 型別,這就限制了最大可用的偏移範圍。

對這些不規則的集合操作,如果要對大量的資料操作,一種方法是採用一系列的點到點操作來替換,雖然這樣可能達不到最好的執行效能。另一種方法是使用 MPI-3 新引進的近鄰集合操作,如 MPI.Topocomm.Neighbor_alltoallw,該方法不同於 MPI.Comm.Alltoallw 的地方在於其偏移引數使用的是 MPI_Aint 而不是 C 語言 int,MPI_Aint 可以表示的數值範圍比 C 語言 int 型別更大。

例程

下面給出使用例程。

# large_count.py

"""
Demonstrates the large counts in MPI-3.

Run this with 2 processes like:
$ mpiexec -n 2 python large_count.py
"""

import numpy as np
from mpi4py import MPI


comm = MPI.COMM_WORLD
rank = comm.rank

MAX_INT32 = np.iinfo(np.int32).max
print 'Max value of a 32 bits int: %d' % MAX_INT32

# create a derived datatype conposed of MAX_INT32 MPI.INT
LARGE_TYPE = MPI.INT.Create_contiguous(MAX_INT32)
print 'LARGE_TYPE:', LARGE_TYPE.lb, LARGE_TYPE.ub, LARGE_TYPE.size, LARGE_TYPE.extent
# create a derived datatype conposed of 2*MAX_INT32 MPI.INT
LARGE_TYPE2 = LARGE_TYPE.Create_contiguous(2)
print 'LARGE_TYPE2:', LARGE_TYPE2.lb, LARGE_TYPE2.ub, LARGE_TYPE2.size, LARGE_TYPE2.extent

# commit the derived datatypes
LARGE_TYPE.Commit()
LARGE_TYPE2.Commit()

if rank == 0:
    large_array = np.ones(2*MAX_INT32, dtype=np.int32)
    print 'rank 0 sends a large message of', 1.0 * large_array.nbytes / 2**32, 'GB'
    # comm.Send([large_array, 1, LARGE_TYPE2], dest=1, tag=11)
    # or
    comm.Send([large_array, 2, LARGE_TYPE], dest=1, tag=11)
else:
    recv_buf = np.empty(2*MAX_INT32, dtype=np.int32)
    comm.Recv([recv_buf, 1, LARGE_TYPE2], source=0, tag=11)
    # or
    # comm.Recv([recv_buf, 2, LARGE_TYPE], source=0, tag=11)
    print 'rank 1 successfully received the large message.'

執行結果如下:

$ mpiexec -n 2 python large_count.py
Max value of a 32 bits int: 2147483647
LARGE_TYPE: 0 8589934588 8589934588 8589934588
LARGE_TYPE2: 0 17179869176 17179869176 17179869176
Max value of a 32 bits int: 2147483647
LARGE_TYPE: 0 8589934588 8589934588 8589934588
LARGE_TYPE2: 0 17179869176 17179869176 17179869176
rank 0 sends a large message of 3.99999999814 GB
rank 1 successfully received the large message.

以上介紹了 MPI-3 中大的計數及相關函式,在下一篇中我們將介紹 MPI 3.1 新增的若干功能。

相關文章