无法使用MPI数据类型

时间:2015-08-13 00:09:34

标签: mpi

我在发送和接收二维数组的列时遇到问题。

我有2个进程。第一个进程有一个二维数组,我想将它的一部分发送到第二个进程。所以说每个等级都有一个9x9阵列,我喜欢将等级0发送到等级1只是某些列:

示例:

-1--2--3-
-2--3--4-
-5--6--7- ...

我想发送" 1,2,5,..."和" 3,4,7,......"。

我已经编写了代码来发送第一列,并且我已经通读了this answer,并且我相信我已经为该列正确定义了MPI_Type_vector:

MPI_Type_vector(dime,1,dime-1,MPI_INT,&LEFT_SIDE);

这里的dime,9,是数组的大小;我发送了9个1 MPI_INT的块,每个块以8的步幅分开 - 但即使只是发送这一列,也会给我无效的结果。

我的代码如下:

#include <mpi.h>
#include <stdio.h>
#include <stdlib.h>

#define dime 9

int main (int argc, char *argv[])
{
    int size,rank;
    const int ltag=2;

    MPI_Init(&argc,&argv);
    MPI_Comm_size(MPI_COMM_WORLD, &size);       // Get the number of processes
    MPI_Comm_rank(MPI_COMM_WORLD, &rank);       // Get the rank of the process

    int table[dime][dime];
    for (int i=0; i<dime; i++)
        for (int j=0; j<dime; j++)
            table[i][j] = rank;

    int message[dime];

    MPI_Datatype LEFT_SIDE;
    MPI_Type_vector(dime,1,dime-1,MPI_INT,&LEFT_SIDE);
    MPI_Type_commit(&LEFT_SIDE);

    if(rank==0) {
        MPI_Send(table, 1, LEFT_SIDE, 1, ltag, MPI_COMM_WORLD);
    } else if(rank==1){
        MPI_Status status;
        MPI_Recv(message, 1, LEFT_SIDE, 0, ltag, MPI_COMM_WORLD, &status);
    }

    if(rank == 1 ){
        printf("Rank 1's received data: ");

        for(int i=0;i<dime;i++)
            printf("%6d ",*(message+i));

        printf("\n");
    }

    MPI_Finalize();
    return 0;

}

但是当我运行它并查看我收到的数据时,我会得到全部为零或乱码:

$ mpicc -o datatype datatype.c -Wall -g -O3 -std=c99 
$ mpirun -np 2 datatype
Rank 1's received data:      0  32710 64550200      0 1828366128  32765 11780096      0      0 

每次数字都会改变。我做错了什么?

2 个答案:

答案 0 :(得分:4)

@ Mort的回答是正确的,是第一个;我只是想用一些ASCII艺术图来扩展它,试图把他的消息带回家。

MPI数据类型描述了数据在内存中的布局方式。让我们看看你的2d数组是否有更小的dime(比如4)和相应的MPI_Type_vector:

 MPI_Type_vector(count=dime, blocksize=1, stride=dime-1, type=MPI_INT ...
                      = 4             =1        = 3

 data = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,15 };
 Vector:  X  -  -  X  -  -  X  -  -  X -  -

请注意,MPI类型中的步幅是类型的开始之间的距离,而不是它们之间的间隙大小;所以你实际上想要stride = dime,而不是dime-1。这很容易解决,但不是实际问题:

 MPI_Type_vector(count=dime, blocksize=1, stride=dime, type=MPI_INT ...
                      = 4             =1        = 4

 data = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,15 };
 Vector:  X  -  -  -  X  -  -  -  X  -  -  -  X -  -  - 

好的,到目前为止,我们正在选择正确的元素。但我们并没有正确地接收它们;试图将数据接收到大小角钱数组的代码,使用相同的布局

int message[dime];
MPI_Recv(message, 1, LEFT_SIDE, 0, ...

message = { 0, 1, 2, 3 };
Vector:     X  -  -  -  X  -  -  -  X  -  -  -  X -  -  - 

向量远远超出了消息的范围,其中(a)在消息中留下未初始化的数据,这是乱码的来源,并且(b)可能导致超出数组边界的分段错误。

至关重要的是,其中一个MPI_Type_vectors描述了2d矩阵中所需数据的布局,但描述了相同数据的布局,因为它接收到紧凑的1d数组中。

这里有两种选择。将数据作为message发送到dime x MPI_INT数组:

// ....
} else if(rank==1){
    MPI_Status status;
    MPI_Recv(message, dime, MPI_INT, 0, ltag, MPI_COMM_WORLD, &status);
}

//...

$ mpirun -np 2 datatype
Rank 1's received data:      0      0      0      0      0      0      0      0      0 

或直接将数据直接接收到Rank 1的2d矩阵中,覆盖相应的列:

#include <mpi.h>
#include <stdio.h>
#include <stdlib.h>

#define dime 9

int main (int argc, char *argv[])
{
    int size,rank;
    const int ltag=2;

    MPI_Init(&argc,&argv);
    MPI_Comm_size(MPI_COMM_WORLD, &size);       // Get the number of processes
    MPI_Comm_rank(MPI_COMM_WORLD, &rank);       // Get the rank of the process

    int table[dime][dime];
    for (int i=0; i<dime; i++)
        for (int j=0; j<dime; j++)
            table[i][j] = rank;

    MPI_Datatype LEFT_SIDE;
    MPI_Type_vector(dime,1,dime,MPI_INT,&LEFT_SIDE);
    MPI_Type_commit(&LEFT_SIDE);

    if(rank==0) {
        MPI_Send(table, 1, LEFT_SIDE, 1, ltag, MPI_COMM_WORLD);
    } else if(rank==1){
        MPI_Status status;
        MPI_Recv(table, 1, LEFT_SIDE, 0, ltag, MPI_COMM_WORLD, &status);
    }

    if(rank == 1 ){
        printf("Rank 1's new array:\n");

        for(int i=0;i<dime;i++) {
            for(int j=0;j<dime;j++) 
                printf("%6d ",table[i][j]);
            printf("\n");
        }

        printf("\n");
    }

    MPI_Type_free(&LEFT_SIDE);
    MPI_Finalize();
    return 0;

}

跑步给出

$ mpicc -o datatype datatype.c -Wall -g -O3 -std=c99 
$ mpirun -np 2 datatype
Rank 1's new array:
     0      1      1      1      1      1      1      1      1 
     0      1      1      1      1      1      1      1      1 
     0      1      1      1      1      1      1      1      1 
     0      1      1      1      1      1      1      1      1 
     0      1      1      1      1      1      1      1      1 
     0      1      1      1      1      1      1      1      1 
     0      1      1      1      1      1      1      1      1 
     0      1      1      1      1      1      1      1      1 
     0      1      1      1      1      1      1      1      1 

(在更正MPI_Type_vector之后)

关于如何将其扩展到多列的剩余部分可能最好留给另一个问题。

答案 1 :(得分:2)

我不太清楚你的问题究竟是什么(请在你的问题中明确说明,你会得到更好的答案!另见How do I ask good questions。),但你的代码有几个的问题。

  • 您需要使用public class MainActivity extends Activity { private TextView prairText; private AssetFileDescriptor descriptor; private SeekBar seekBar; private MediaPlayer mp = new MediaPlayer(); @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); // Keep screen active getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); findViewById(R.id.play_button).setVisibility(View.INVISIBLE); findViewById(R.id.pauseButton).setVisibility(View.VISIBLE); init(); // Play prayer /*if (mp != null) { mp.start(); }*/ seekBar.setOnSeekBarChangeListener(new OnSeekBarChangeListener() { @Override public void onStopTrackingTouch(SeekBar seekBar) { } @Override public void onStartTrackingTouch(SeekBar seekBar) { } @Override public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { if (mp != null && fromUser) { mp.seekTo(progress * 1000); } } }); findViewById(R.id.play_button).setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { if (mp != null) { mp.start(); final Handler mHandler = new Handler(); //Make sure you update Seekbar on UI thread MainActivity.this.runOnUiThread(new Runnable() { @Override public void run() { if(mp != null){ int mCurrentPosition = mp.getCurrentPosition() / 1000; seekBar.setProgress(mCurrentPosition); } mHandler.postDelayed(this, 1000); } }); findViewById(R.id.play_button).setVisibility(View.INVISIBLE); findViewById(R.id.pauseButton).setVisibility(View.VISIBLE); } } }); findViewById(R.id.pauseButton).setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { if (mp != null) { mp.pause(); findViewById(R.id.pauseButton).setVisibility(View.INVISIBLE); findViewById(R.id.play_button).setVisibility(View.VISIBLE); } mp.pause(); } }); findViewById(R.id.restart_button).setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { if (mp != null) { mp.seekTo(0); mp.start(); findViewById(R.id.play_button).setVisibility(View.INVISIBLE); findViewById(R.id.pauseButton).setVisibility(View.VISIBLE); } } }); private void init() { prairText = (TextView) findViewById(R.id.prairText); seekBar = (SeekBar) findViewById(R.id.seekBar1); try { descriptor = getAssets().openFd("mysong.mp3"); mp.setDataSource(descriptor.getFileDescriptor(), descriptor.getStartOffset(), descriptor.getLength()); descriptor.close(); mp.prepare(); } catch (Exception e) { e.printStackTrace(); } seekBar.setMax(mp.getDuration()); } ,因为您要发送矩阵的每一分钱元素。在C中,2-d数组简单地存储为标准数组,其中元素[i] [j]存储在索引[i * dime + j]。你想发送索引0,角钱,2 *角钱,3 *角钱,...

  • 的元素
  • 如果您使用MPI_Type_vector(dime,1,dime,MPI_INT,&LEFT_SIDE);数据类型来接收数据,MPI将存储数据项与间隔元素的间隙 - 类似于发件人。但是,您的接收缓冲区LEFT_SIDE是一个简单的数组。您需要接收如下数据:message。此操作将接收一角硬币整数并将它们放入您的MPI_Recv(message, dime, MPI_INT, 0, LTAG, newcomm,&status);数组中。

编辑:我更新了我的答案以匹配显着更改的问题。