/*
 This is part of the OTF library. Copyright by ZIH, TU Dresden 2005-2014.
 Authors: Andreas Knuepfer, Robert Dietrich, Matthias Jurenz
*/

#include <cassert>
#include <iostream>
#include <stdio.h>

#include "otfprofile.h"
#include "reduce_data.h"


using namespace std;


/* fence between statistics parts within the buffer for consistency checking */
enum { FENCE= 0xDEADBEEF };

enum {
   PACK_TOTAL_SIZE                   = 0,
   PACK_FUNCTION                     = 1,
   PACK_FUNCTION_DURATION            = 2,
   PACK_COUNTER                      = 3,
   PACK_MSG_GROUP_PAIR               = 4,
   PACK_MSG_GROUP                    = 5,
   PACK_MSG_SPEED                    = 6,
   PACK_COLLECTIVE_GROUP             = 7,
   PACK_FUNCTION_RANK                = 8,
   PACK_FUNCTION_MIN_MAX             = 9,
   PACK_FUNCTION_DURATION_CALLPATH   = 10,
   PACK_FUNCTION_CALLPATH            = 11,
   PACK_MSG_SIZE                     = 12,
   PACK_NUM_PACKS                    = 13
};


/* pack the local alldata into a buffer, return buffer */
static char* pack_worker_data( AllData& alldata, uint32_t sizes[PACK_NUM_PACKS] ) {

    uint64_t fence= FENCE;
    uint32_t num_fences= 1;

    /* get the sizes of all parts that need to be transmitted */

    for ( uint32_t i= 1; i < PACK_NUM_PACKS; i++ ) {

        sizes[i]= 0;
    }

    if ( alldata.params.create_tex ) {

        sizes[PACK_FUNCTION]          = alldata.functionMapGlobal.size(); /* map< uint64_t, FunctionData > functionMapGlobal; */
        num_fences++;
        sizes[PACK_FUNCTION_DURATION] = alldata.functionDurationSectionMapGlobal.size(); /* map< Pair, FunctionData > functionDurationSectionMapGlobal; */
        num_fences++;
        sizes[PACK_COUNTER]           = alldata.counterMapGlobal.size(); /* map< Pair, CounterData, ltPair > counterMapGlobal; */
        num_fences++;
        sizes[PACK_MSG_GROUP_PAIR]    = alldata.messageMapPerGroupPair.size(); /* map< Pair, MessageData, ltPair > messageMapPerGroupPair; */
        num_fences++;
        sizes[PACK_MSG_GROUP]         = alldata.messageMapPerGroup.size(); /* map< uint64_t, MessageData > messageMapPerGroup; */
        num_fences++;
        sizes[PACK_MSG_SPEED]         = alldata.messageSpeedMapPerLength.size(); /* map< Pair, MessageSpeedData, ltPair > messageSpeedMapPerLength; */
        num_fences++;
        sizes[PACK_COLLECTIVE_GROUP]  = alldata.collectiveMapPerGroup.size(); /* map< Pair, CollectiveData, ltPair > collectiveMapPerGroup; */
        num_fences++;
        if(alldata.params.dispersion.mode == DISPERSION_MODE_PERCALLPATH)
        {
            sizes[PACK_FUNCTION_DURATION_CALLPATH] = alldata.functionDurationSectionCallpathMapGlobal.size(); /* map< TripleCallpath, FunctionData, ltTripleCallpath > functionDurationSectionCallpathMapGlobal; */
            num_fences++;
            sizes[PACK_FUNCTION_CALLPATH]          = alldata.functionCallpathMapGlobal.size(); /* map< PairCallpath, FunctionData, ltPairCallpath > */
            num_fences++;
        }
    }

    if ( alldata.params.clustering.enabled ) {

        sizes[PACK_FUNCTION_RANK]= alldata.functionMapPerRank.size(); /* map< Pair, FunctionData, ltPair > */
        num_fences++;
    }

    if ( alldata.params.dispersion.enabled) {
        
        sizes[PACK_FUNCTION_MIN_MAX]= alldata.functionMinMaxLocationMap.size(); /* map< uint64_t, FunctionMinMaxLocactionData > functionMinMaxLocationMap; */
        num_fences++;
    }

    if ( alldata.params.write_csv_msg_sizes ) {
       sizes[PACK_MSG_SIZE] = alldata.messageMapPerSize.size(); /* map< uint64_t, MessageSizeData > */ 
       num_fences++;
    }

    /* get bytesize multiplying all pieces */

    uint32_t bytesize= 0;
    int s1, s2;

    MPI_Pack_size( num_fences, MPI_LONG_LONG_INT, MPI_COMM_WORLD, &s1 );
    bytesize += s1;

    MPI_Pack_size( sizes[PACK_FUNCTION] * 7, MPI_LONG_LONG_INT, MPI_COMM_WORLD, &s1 );
    MPI_Pack_size( sizes[PACK_FUNCTION] * 6, MPI_DOUBLE, MPI_COMM_WORLD, &s2 );
    bytesize += s1 + s2;

    MPI_Pack_size( sizes[PACK_FUNCTION_DURATION] * 8, MPI_LONG_LONG_INT, MPI_COMM_WORLD, &s1 );
    MPI_Pack_size( sizes[PACK_FUNCTION_DURATION] * 6, MPI_DOUBLE, MPI_COMM_WORLD, &s2 );
    bytesize += s1 + s2;
    
    MPI_Pack_size( sizes[PACK_COUNTER] * 8, MPI_LONG_LONG_INT, MPI_COMM_WORLD, &s1 );
    MPI_Pack_size( sizes[PACK_COUNTER] * 6, MPI_DOUBLE, MPI_COMM_WORLD, &s2 );
    bytesize += s1 + s2;

    MPI_Pack_size( sizes[PACK_MSG_GROUP_PAIR] * 20, MPI_LONG_LONG_INT, MPI_COMM_WORLD, &s1 );
    MPI_Pack_size( sizes[PACK_MSG_GROUP_PAIR] * 6, MPI_DOUBLE, MPI_COMM_WORLD, &s2 );
    bytesize += s1 + s2;

    MPI_Pack_size( sizes[PACK_MSG_GROUP] * 19, MPI_LONG_LONG_INT, MPI_COMM_WORLD, &s1 );
    MPI_Pack_size( sizes[PACK_MSG_GROUP] * 6, MPI_DOUBLE, MPI_COMM_WORLD, &s2 );
    bytesize += s1 + s2;

    MPI_Pack_size( sizes[PACK_MSG_SPEED] * 6, MPI_LONG_LONG_INT, MPI_COMM_WORLD, &s1 );
    bytesize += s1;

    MPI_Pack_size( sizes[PACK_COLLECTIVE_GROUP] * 20, MPI_LONG_LONG_INT, MPI_COMM_WORLD, &s1 );
    MPI_Pack_size( sizes[PACK_COLLECTIVE_GROUP] * 6, MPI_DOUBLE, MPI_COMM_WORLD, &s2 );
    bytesize += s1 + s2;

    MPI_Pack_size( sizes[PACK_FUNCTION_RANK] * 8, MPI_LONG_LONG_INT, MPI_COMM_WORLD, &s1 );
    MPI_Pack_size( sizes[PACK_FUNCTION_RANK] * 6, MPI_DOUBLE, MPI_COMM_WORLD, &s2 );
    bytesize += s1 + s2;

    MPI_Pack_size( sizes[PACK_FUNCTION_MIN_MAX] * 7, MPI_LONG_LONG_INT, MPI_COMM_WORLD, &s1 );
    MPI_Pack_size( sizes[PACK_FUNCTION_MIN_MAX] * 0, MPI_DOUBLE, MPI_COMM_WORLD, &s2 );
    bytesize += s1 + s2;


    MPI_Pack_size( sizes[PACK_FUNCTION_DURATION_CALLPATH] * 9, MPI_LONG_LONG_INT, MPI_COMM_WORLD, &s1 );
    MPI_Pack_size( sizes[PACK_FUNCTION_DURATION_CALLPATH] * 6, MPI_DOUBLE, MPI_COMM_WORLD, &s2 );
    bytesize += s1 + s2;

    if(alldata.params.dispersion.mode == DISPERSION_MODE_PERCALLPATH)
    {
        map< TripleCallpath, FunctionData, ltTripleCallpath >::const_iterator it=    alldata.functionDurationSectionCallpathMapGlobal.begin();
        map< TripleCallpath, FunctionData, ltTripleCallpath >::const_iterator itend= alldata.functionDurationSectionCallpathMapGlobal.end();

        for ( ; it != itend; ++it ) {
            MPI_Pack_size( it->first.b.length(), MPI_CHAR, MPI_COMM_WORLD, &s1 );
            bytesize += s1;
        }

        MPI_Pack_size( sizes[PACK_FUNCTION_CALLPATH] * 8, MPI_LONG_LONG_INT, MPI_COMM_WORLD, &s1 );
        MPI_Pack_size( sizes[PACK_FUNCTION_CALLPATH] * 6, MPI_DOUBLE, MPI_COMM_WORLD, &s2 );
        bytesize += s1 + s2;

        {
            map< PairCallpath, FunctionData, ltPairCallpath >::const_iterator it=    alldata.functionCallpathMapGlobal.begin();
            map< PairCallpath, FunctionData, ltPairCallpath >::const_iterator itend= alldata.functionCallpathMapGlobal.end();
            for ( ; it != itend; ++it ) {
                MPI_Pack_size( it->second.callpath.length(), MPI_CHAR, MPI_COMM_WORLD, &s1 );
                bytesize += s1;
            }
        }
    }

    if ( alldata.params.write_csv_msg_sizes ) {
       MPI_Pack_size( sizes[PACK_MSG_SIZE] * 6, MPI_LONG_LONG_INT, MPI_COMM_WORLD, &s1 );
       bytesize += s1;
    }


    /* get the buffer */
    sizes[PACK_TOTAL_SIZE]= bytesize;
    char* buffer= alldata.guaranteePackBuffer( bytesize );

    /* pack parts */
    int position= 0;

    /* extra check that doesn't cost too much */
    MPI_Pack( (void*) &fence, 1, MPI_LONG_LONG_INT, buffer, bytesize, &position, MPI_COMM_WORLD );

    if ( alldata.params.create_tex ) {

        /* pack functionMapGlobal */
        {
            map< uint64_t, FunctionData >::const_iterator it=    alldata.functionMapGlobal.begin();
            map< uint64_t, FunctionData >::const_iterator itend= alldata.functionMapGlobal.end();
            for ( ; it != itend; ++it ) {

                MPI_Pack( (void*) &it->first,                1, MPI_LONG_LONG_INT, buffer, bytesize, &position, MPI_COMM_WORLD );

                MPI_Pack( (void*) &it->second.count.min,     1, MPI_LONG_LONG_INT, buffer, bytesize, &position, MPI_COMM_WORLD );
                MPI_Pack( (void*) &it->second.count.max,     1, MPI_LONG_LONG_INT, buffer, bytesize, &position, MPI_COMM_WORLD );
                MPI_Pack( (void*) &it->second.count.sum,     1, MPI_LONG_LONG_INT, buffer, bytesize, &position, MPI_COMM_WORLD );
                MPI_Pack( (void*) &it->second.count.cnt,     1, MPI_LONG_LONG_INT, buffer, bytesize, &position, MPI_COMM_WORLD );

                MPI_Pack( (void*) &it->second.excl_time.min, 1, MPI_DOUBLE,        buffer, bytesize, &position, MPI_COMM_WORLD );
                MPI_Pack( (void*) &it->second.excl_time.max, 1, MPI_DOUBLE,        buffer, bytesize, &position, MPI_COMM_WORLD );
                MPI_Pack( (void*) &it->second.excl_time.sum, 1, MPI_DOUBLE,        buffer, bytesize, &position, MPI_COMM_WORLD );
                MPI_Pack( (void*) &it->second.excl_time.cnt, 1, MPI_LONG_LONG_INT, buffer, bytesize, &position, MPI_COMM_WORLD );

                MPI_Pack( (void*) &it->second.incl_time.min, 1, MPI_DOUBLE,        buffer, bytesize, &position, MPI_COMM_WORLD );
                MPI_Pack( (void*) &it->second.incl_time.max, 1, MPI_DOUBLE,        buffer, bytesize, &position, MPI_COMM_WORLD );
                MPI_Pack( (void*) &it->second.incl_time.sum, 1, MPI_DOUBLE,        buffer, bytesize, &position, MPI_COMM_WORLD );
                MPI_Pack( (void*) &it->second.incl_time.cnt, 1, MPI_LONG_LONG_INT, buffer, bytesize, &position, MPI_COMM_WORLD );
            }
            alldata.functionMapGlobal.clear();
        }

        /* extra check that doesn't cost too much */
        MPI_Pack( (void*) &fence, 1, MPI_LONG_LONG_INT, buffer, bytesize, &position, MPI_COMM_WORLD );

        if(alldata.params.dispersion.mode == DISPERSION_MODE_PERCALLPATH)
        {
        /* pack functionCallpathMapGlobal */
                {
                    map< PairCallpath, FunctionData, ltPairCallpath >::const_iterator it=    alldata.functionCallpathMapGlobal.begin();
                    map< PairCallpath, FunctionData, ltPairCallpath>::const_iterator itend= alldata.functionCallpathMapGlobal.end();
                    uint64_t len;
                    for ( ; it != itend; ++it ) {
                    	len = it->second.callpath.length();
                        MPI_Pack( (void*) &it->first.a,               1, MPI_LONG_LONG_INT, buffer, bytesize, &position, MPI_COMM_WORLD );
                        MPI_Pack( (void*) &len,                       1, MPI_LONG_LONG_INT, buffer, bytesize, &position, MPI_COMM_WORLD );

                        MPI_Pack( (void*) &it->second.count.min,      1, MPI_LONG_LONG_INT, buffer, bytesize, &position, MPI_COMM_WORLD );
                        MPI_Pack( (void*) &it->second.count.max,      1, MPI_LONG_LONG_INT, buffer, bytesize, &position, MPI_COMM_WORLD );
                        MPI_Pack( (void*) &it->second.count.sum,      1, MPI_LONG_LONG_INT, buffer, bytesize, &position, MPI_COMM_WORLD );
                        MPI_Pack( (void*) &it->second.count.cnt,      1, MPI_LONG_LONG_INT, buffer, bytesize, &position, MPI_COMM_WORLD );

                         MPI_Pack( (void*) &it->second.excl_time.min, 1, MPI_DOUBLE,        buffer, bytesize, &position, MPI_COMM_WORLD );
                         MPI_Pack( (void*) &it->second.excl_time.max, 1, MPI_DOUBLE,        buffer, bytesize, &position, MPI_COMM_WORLD );
                         MPI_Pack( (void*) &it->second.excl_time.sum, 1, MPI_DOUBLE,        buffer, bytesize, &position, MPI_COMM_WORLD );
                         MPI_Pack( (void*) &it->second.excl_time.cnt, 1, MPI_LONG_LONG_INT, buffer, bytesize, &position, MPI_COMM_WORLD );

                         MPI_Pack( (void*) &it->second.incl_time.min, 1, MPI_DOUBLE,        buffer, bytesize, &position, MPI_COMM_WORLD );
                         MPI_Pack( (void*) &it->second.incl_time.max, 1, MPI_DOUBLE,        buffer, bytesize, &position, MPI_COMM_WORLD );
                         MPI_Pack( (void*) &it->second.incl_time.sum, 1, MPI_DOUBLE,        buffer, bytesize, &position, MPI_COMM_WORLD );
                         MPI_Pack( (void*) &it->second.incl_time.cnt, 1, MPI_LONG_LONG_INT, buffer, bytesize, &position, MPI_COMM_WORLD );
                         MPI_Pack( (void*) it->first.b.c_str(), len, MPI_CHAR, buffer, bytesize, &position, MPI_COMM_WORLD );
                     }
                    alldata.functionCallpathMapGlobal.clear();
                }

                /* extra check that doesn't cost too much */
                MPI_Pack( (void*) &fence, 1, MPI_LONG_LONG_INT, buffer, bytesize, &position, MPI_COMM_WORLD );
        }
        /* pack functionDurationSectionMapGlobal */
        {
            map< Pair, FunctionData, ltPair >::const_iterator it=    alldata.functionDurationSectionMapGlobal.begin();
            map< Pair, FunctionData, ltPair >::const_iterator itend= alldata.functionDurationSectionMapGlobal.end();
            for ( ; it != itend; ++it ) {
                
                MPI_Pack( (void*) &it->first.a,              1, MPI_LONG_LONG_INT, buffer, bytesize, &position, MPI_COMM_WORLD );
                MPI_Pack( (void*) &it->first.b,              1, MPI_LONG_LONG_INT, buffer, bytesize, &position, MPI_COMM_WORLD );
                
                MPI_Pack( (void*) &it->second.count.min,     1, MPI_LONG_LONG_INT, buffer, bytesize, &position, MPI_COMM_WORLD );
                MPI_Pack( (void*) &it->second.count.max,     1, MPI_LONG_LONG_INT, buffer, bytesize, &position, MPI_COMM_WORLD );
                MPI_Pack( (void*) &it->second.count.sum,     1, MPI_LONG_LONG_INT, buffer, bytesize, &position, MPI_COMM_WORLD );
                MPI_Pack( (void*) &it->second.count.cnt,     1, MPI_LONG_LONG_INT, buffer, bytesize, &position, MPI_COMM_WORLD );
                
                MPI_Pack( (void*) &it->second.excl_time.min, 1, MPI_DOUBLE,        buffer, bytesize, &position, MPI_COMM_WORLD );
                MPI_Pack( (void*) &it->second.excl_time.max, 1, MPI_DOUBLE,        buffer, bytesize, &position, MPI_COMM_WORLD );
                MPI_Pack( (void*) &it->second.excl_time.sum, 1, MPI_DOUBLE,        buffer, bytesize, &position, MPI_COMM_WORLD );
                MPI_Pack( (void*) &it->second.excl_time.cnt, 1, MPI_LONG_LONG_INT, buffer, bytesize, &position, MPI_COMM_WORLD );
                
                MPI_Pack( (void*) &it->second.incl_time.min, 1, MPI_DOUBLE,        buffer, bytesize, &position, MPI_COMM_WORLD );
                MPI_Pack( (void*) &it->second.incl_time.max, 1, MPI_DOUBLE,        buffer, bytesize, &position, MPI_COMM_WORLD );
                MPI_Pack( (void*) &it->second.incl_time.sum, 1, MPI_DOUBLE,        buffer, bytesize, &position, MPI_COMM_WORLD );
                MPI_Pack( (void*) &it->second.incl_time.cnt, 1, MPI_LONG_LONG_INT, buffer, bytesize, &position, MPI_COMM_WORLD );
            }
            alldata.functionDurationSectionMapGlobal.clear();           
            
        }

        /* extra check that doesn't cost too much */
        MPI_Pack( (void*) &fence, 1, MPI_LONG_LONG_INT, buffer, bytesize, &position, MPI_COMM_WORLD );
        
        /* pack counterMapGlobal */
        {
            map< Pair, CounterData, ltPair >::const_iterator it=    alldata.counterMapGlobal.begin();
            map< Pair, CounterData, ltPair >::const_iterator itend= alldata.counterMapGlobal.end();
            for ( ; it != itend; ++it ) {

                MPI_Pack( (void*) &it->first.a,              1, MPI_LONG_LONG_INT, buffer, bytesize, &position, MPI_COMM_WORLD );
                MPI_Pack( (void*) &it->first.b,              1, MPI_LONG_LONG_INT, buffer, bytesize, &position, MPI_COMM_WORLD );

                MPI_Pack( (void*) &it->second.count.min,     1, MPI_LONG_LONG_INT, buffer, bytesize, &position, MPI_COMM_WORLD );
                MPI_Pack( (void*) &it->second.count.max,     1, MPI_LONG_LONG_INT, buffer, bytesize, &position, MPI_COMM_WORLD );
                MPI_Pack( (void*) &it->second.count.sum,     1, MPI_LONG_LONG_INT, buffer, bytesize, &position, MPI_COMM_WORLD );
                MPI_Pack( (void*) &it->second.count.cnt,     1, MPI_LONG_LONG_INT, buffer, bytesize, &position, MPI_COMM_WORLD );

                MPI_Pack( (void*) &it->second.excl_time.min, 1, MPI_DOUBLE,        buffer, bytesize, &position, MPI_COMM_WORLD );
                MPI_Pack( (void*) &it->second.excl_time.max, 1, MPI_DOUBLE,        buffer, bytesize, &position, MPI_COMM_WORLD );
                MPI_Pack( (void*) &it->second.excl_time.sum, 1, MPI_DOUBLE,        buffer, bytesize, &position, MPI_COMM_WORLD );
                MPI_Pack( (void*) &it->second.excl_time.cnt, 1, MPI_LONG_LONG_INT, buffer, bytesize, &position, MPI_COMM_WORLD );

                MPI_Pack( (void*) &it->second.incl_time.min, 1, MPI_DOUBLE,        buffer, bytesize, &position, MPI_COMM_WORLD );
                MPI_Pack( (void*) &it->second.incl_time.max, 1, MPI_DOUBLE,        buffer, bytesize, &position, MPI_COMM_WORLD );
                MPI_Pack( (void*) &it->second.incl_time.sum, 1, MPI_DOUBLE,        buffer, bytesize, &position, MPI_COMM_WORLD );
                MPI_Pack( (void*) &it->second.incl_time.cnt, 1, MPI_LONG_LONG_INT, buffer, bytesize, &position, MPI_COMM_WORLD );
            }
            alldata.counterMapGlobal.clear();
        }

        /* extra check that doesn't cost too much */
        MPI_Pack( (void*) &fence, 1, MPI_LONG_LONG_INT, buffer, bytesize, &position, MPI_COMM_WORLD );

        /* pack messageMapPerGroupPair  */
        {
            map< Pair, MessageData, ltPair >::const_iterator it=    alldata.messageMapPerGroupPair.begin();
            map< Pair, MessageData, ltPair >::const_iterator itend= alldata.messageMapPerGroupPair.end();
            for ( ; it != itend; ++it ) {

                MPI_Pack( (void*) &it->first.a,                  1, MPI_LONG_LONG_INT, buffer, bytesize, &position, MPI_COMM_WORLD );
                MPI_Pack( (void*) &it->first.b,                  1, MPI_LONG_LONG_INT, buffer, bytesize, &position, MPI_COMM_WORLD );

                MPI_Pack( (void*) &it->second.count_send.min,    1, MPI_LONG_LONG_INT, buffer, bytesize, &position, MPI_COMM_WORLD );
                MPI_Pack( (void*) &it->second.count_send.max,    1, MPI_LONG_LONG_INT, buffer, bytesize, &position, MPI_COMM_WORLD );
                MPI_Pack( (void*) &it->second.count_send.sum,    1, MPI_LONG_LONG_INT, buffer, bytesize, &position, MPI_COMM_WORLD );
                MPI_Pack( (void*) &it->second.count_send.cnt,    1, MPI_LONG_LONG_INT, buffer, bytesize, &position, MPI_COMM_WORLD );

                MPI_Pack( (void*) &it->second.count_recv.min,    1, MPI_LONG_LONG_INT, buffer, bytesize, &position, MPI_COMM_WORLD );
                MPI_Pack( (void*) &it->second.count_recv.max,    1, MPI_LONG_LONG_INT, buffer, bytesize, &position, MPI_COMM_WORLD );
                MPI_Pack( (void*) &it->second.count_recv.sum,    1, MPI_LONG_LONG_INT, buffer, bytesize, &position, MPI_COMM_WORLD );
                MPI_Pack( (void*) &it->second.count_recv.cnt,    1, MPI_LONG_LONG_INT, buffer, bytesize, &position, MPI_COMM_WORLD );

                MPI_Pack( (void*) &it->second.bytes_send.min,    1, MPI_LONG_LONG_INT, buffer, bytesize, &position, MPI_COMM_WORLD );
                MPI_Pack( (void*) &it->second.bytes_send.max,    1, MPI_LONG_LONG_INT, buffer, bytesize, &position, MPI_COMM_WORLD );
                MPI_Pack( (void*) &it->second.bytes_send.sum,    1, MPI_LONG_LONG_INT, buffer, bytesize, &position, MPI_COMM_WORLD );
                MPI_Pack( (void*) &it->second.bytes_send.cnt,    1, MPI_LONG_LONG_INT, buffer, bytesize, &position, MPI_COMM_WORLD );

                MPI_Pack( (void*) &it->second.bytes_recv.min,    1, MPI_LONG_LONG_INT, buffer, bytesize, &position, MPI_COMM_WORLD );
                MPI_Pack( (void*) &it->second.bytes_recv.max,    1, MPI_LONG_LONG_INT, buffer, bytesize, &position, MPI_COMM_WORLD );
                MPI_Pack( (void*) &it->second.bytes_recv.sum,    1, MPI_LONG_LONG_INT, buffer, bytesize, &position, MPI_COMM_WORLD );
                MPI_Pack( (void*) &it->second.bytes_recv.cnt,    1, MPI_LONG_LONG_INT, buffer, bytesize, &position, MPI_COMM_WORLD );

                MPI_Pack( (void*) &it->second.duration_send.min, 1, MPI_DOUBLE,        buffer, bytesize, &position, MPI_COMM_WORLD );
                MPI_Pack( (void*) &it->second.duration_send.max, 1, MPI_DOUBLE,        buffer, bytesize, &position, MPI_COMM_WORLD );
                MPI_Pack( (void*) &it->second.duration_send.sum, 1, MPI_DOUBLE,        buffer, bytesize, &position, MPI_COMM_WORLD );
                MPI_Pack( (void*) &it->second.duration_send.cnt, 1, MPI_LONG_LONG_INT, buffer, bytesize, &position, MPI_COMM_WORLD );

                MPI_Pack( (void*) &it->second.duration_recv.min, 1, MPI_DOUBLE,        buffer, bytesize, &position, MPI_COMM_WORLD );
                MPI_Pack( (void*) &it->second.duration_recv.max, 1, MPI_DOUBLE,        buffer, bytesize, &position, MPI_COMM_WORLD );
                MPI_Pack( (void*) &it->second.duration_recv.sum, 1, MPI_DOUBLE,        buffer, bytesize, &position, MPI_COMM_WORLD );
                MPI_Pack( (void*) &it->second.duration_recv.cnt, 1, MPI_LONG_LONG_INT, buffer, bytesize, &position, MPI_COMM_WORLD );
            }
            alldata.messageMapPerGroupPair.clear();
        }

        /* extra check that doesn't cost too much */
        MPI_Pack( (void*) &fence, 1, MPI_LONG_LONG_INT, buffer, bytesize, &position, MPI_COMM_WORLD );

        /* pack messageMapPerGroup  */
        {
            map< uint64_t, MessageData >::const_iterator it=    alldata.messageMapPerGroup.begin();
            map< uint64_t, MessageData >::const_iterator itend= alldata.messageMapPerGroup.end();
            for ( ; it != itend; ++it ) {

                MPI_Pack( (void*) &it->first,                    1, MPI_LONG_LONG_INT, buffer, bytesize, &position, MPI_COMM_WORLD );

                MPI_Pack( (void*) &it->second.count_send.min,    1, MPI_LONG_LONG_INT, buffer, bytesize, &position, MPI_COMM_WORLD );
                MPI_Pack( (void*) &it->second.count_send.max,    1, MPI_LONG_LONG_INT, buffer, bytesize, &position, MPI_COMM_WORLD );
                MPI_Pack( (void*) &it->second.count_send.sum,    1, MPI_LONG_LONG_INT, buffer, bytesize, &position, MPI_COMM_WORLD );
                MPI_Pack( (void*) &it->second.count_send.cnt,    1, MPI_LONG_LONG_INT, buffer, bytesize, &position, MPI_COMM_WORLD );

                MPI_Pack( (void*) &it->second.count_recv.min,    1, MPI_LONG_LONG_INT, buffer, bytesize, &position, MPI_COMM_WORLD );
                MPI_Pack( (void*) &it->second.count_recv.max,    1, MPI_LONG_LONG_INT, buffer, bytesize, &position, MPI_COMM_WORLD );
                MPI_Pack( (void*) &it->second.count_recv.sum,    1, MPI_LONG_LONG_INT, buffer, bytesize, &position, MPI_COMM_WORLD );
                MPI_Pack( (void*) &it->second.count_recv.cnt,    1, MPI_LONG_LONG_INT, buffer, bytesize, &position, MPI_COMM_WORLD );

                MPI_Pack( (void*) &it->second.bytes_send.min,    1, MPI_LONG_LONG_INT, buffer, bytesize, &position, MPI_COMM_WORLD );
                MPI_Pack( (void*) &it->second.bytes_send.max,    1, MPI_LONG_LONG_INT, buffer, bytesize, &position, MPI_COMM_WORLD );
                MPI_Pack( (void*) &it->second.bytes_send.sum,    1, MPI_LONG_LONG_INT, buffer, bytesize, &position, MPI_COMM_WORLD );
                MPI_Pack( (void*) &it->second.bytes_send.cnt,    1, MPI_LONG_LONG_INT, buffer, bytesize, &position, MPI_COMM_WORLD );

                MPI_Pack( (void*) &it->second.bytes_recv.min,    1, MPI_LONG_LONG_INT, buffer, bytesize, &position, MPI_COMM_WORLD );
                MPI_Pack( (void*) &it->second.bytes_recv.max,    1, MPI_LONG_LONG_INT, buffer, bytesize, &position, MPI_COMM_WORLD );
                MPI_Pack( (void*) &it->second.bytes_recv.sum,    1, MPI_LONG_LONG_INT, buffer, bytesize, &position, MPI_COMM_WORLD );
                MPI_Pack( (void*) &it->second.bytes_recv.cnt,    1, MPI_LONG_LONG_INT, buffer, bytesize, &position, MPI_COMM_WORLD );

                MPI_Pack( (void*) &it->second.duration_send.min, 1, MPI_DOUBLE,        buffer, bytesize, &position, MPI_COMM_WORLD );
                MPI_Pack( (void*) &it->second.duration_send.max, 1, MPI_DOUBLE,        buffer, bytesize, &position, MPI_COMM_WORLD );
                MPI_Pack( (void*) &it->second.duration_send.sum, 1, MPI_DOUBLE,        buffer, bytesize, &position, MPI_COMM_WORLD );
                MPI_Pack( (void*) &it->second.duration_send.cnt, 1, MPI_LONG_LONG_INT, buffer, bytesize, &position, MPI_COMM_WORLD );

                MPI_Pack( (void*) &it->second.duration_recv.min, 1, MPI_DOUBLE,        buffer, bytesize, &position, MPI_COMM_WORLD );
                MPI_Pack( (void*) &it->second.duration_recv.max, 1, MPI_DOUBLE,        buffer, bytesize, &position, MPI_COMM_WORLD );
                MPI_Pack( (void*) &it->second.duration_recv.sum, 1, MPI_DOUBLE,        buffer, bytesize, &position, MPI_COMM_WORLD );
                MPI_Pack( (void*) &it->second.duration_recv.cnt, 1, MPI_LONG_LONG_INT, buffer, bytesize, &position, MPI_COMM_WORLD );
            }
            alldata.messageMapPerGroup.clear();
        }

        /* extra check that doesn't cost too much */
        MPI_Pack( (void*) &fence, 1, MPI_LONG_LONG_INT, buffer, bytesize, &position, MPI_COMM_WORLD );

        /* pack messageSpeedMapPerLength */
        {
            map< Pair, MessageSpeedData, ltPair >::const_iterator it=    alldata.messageSpeedMapPerLength.begin();
            map< Pair, MessageSpeedData, ltPair >::const_iterator itend= alldata.messageSpeedMapPerLength.end();
            for ( ; it != itend; ++it ) {

                MPI_Pack( (void*) &it->first.a,          1, MPI_LONG_LONG_INT, buffer, bytesize, &position, MPI_COMM_WORLD );
                MPI_Pack( (void*) &it->first.b,          1, MPI_LONG_LONG_INT, buffer, bytesize, &position, MPI_COMM_WORLD );

                MPI_Pack( (void*) &it->second.count.min, 1, MPI_LONG_LONG_INT, buffer, bytesize, &position, MPI_COMM_WORLD );
                MPI_Pack( (void*) &it->second.count.max, 1, MPI_LONG_LONG_INT, buffer, bytesize, &position, MPI_COMM_WORLD );
                MPI_Pack( (void*) &it->second.count.sum, 1, MPI_LONG_LONG_INT, buffer, bytesize, &position, MPI_COMM_WORLD );
                MPI_Pack( (void*) &it->second.count.cnt, 1, MPI_LONG_LONG_INT, buffer, bytesize, &position, MPI_COMM_WORLD );
            }
            alldata.messageSpeedMapPerLength.clear();
        }

        /* extra check that doesn't cost too much */
        MPI_Pack( (void*) &fence, 1, MPI_LONG_LONG_INT, buffer, bytesize, &position, MPI_COMM_WORLD );

        /* pack collectiveMapPerGroup */
        {
            map< Pair, CollectiveData, ltPair >::const_iterator it=    alldata.collectiveMapPerGroup.begin();
            map< Pair, CollectiveData, ltPair >::const_iterator itend= alldata.collectiveMapPerGroup.end();
            for ( ; it != itend; ++it ) {

                MPI_Pack( (void*) &it->first.a,                  1, MPI_LONG_LONG_INT, buffer, bytesize, &position, MPI_COMM_WORLD );
                MPI_Pack( (void*) &it->first.b,                  1, MPI_LONG_LONG_INT, buffer, bytesize, &position, MPI_COMM_WORLD );

                MPI_Pack( (void*) &it->second.count_send.min,    1, MPI_LONG_LONG_INT, buffer, bytesize, &position, MPI_COMM_WORLD );
                MPI_Pack( (void*) &it->second.count_send.max,    1, MPI_LONG_LONG_INT, buffer, bytesize, &position, MPI_COMM_WORLD );
                MPI_Pack( (void*) &it->second.count_send.sum,    1, MPI_LONG_LONG_INT, buffer, bytesize, &position, MPI_COMM_WORLD );
                MPI_Pack( (void*) &it->second.count_send.cnt,    1, MPI_LONG_LONG_INT, buffer, bytesize, &position, MPI_COMM_WORLD );

                MPI_Pack( (void*) &it->second.count_recv.min,    1, MPI_LONG_LONG_INT, buffer, bytesize, &position, MPI_COMM_WORLD );
                MPI_Pack( (void*) &it->second.count_recv.max,    1, MPI_LONG_LONG_INT, buffer, bytesize, &position, MPI_COMM_WORLD );
                MPI_Pack( (void*) &it->second.count_recv.sum,    1, MPI_LONG_LONG_INT, buffer, bytesize, &position, MPI_COMM_WORLD );
                MPI_Pack( (void*) &it->second.count_recv.cnt,    1, MPI_LONG_LONG_INT, buffer, bytesize, &position, MPI_COMM_WORLD );

                MPI_Pack( (void*) &it->second.bytes_send.min,    1, MPI_LONG_LONG_INT, buffer, bytesize, &position, MPI_COMM_WORLD );
                MPI_Pack( (void*) &it->second.bytes_send.max,    1, MPI_LONG_LONG_INT, buffer, bytesize, &position, MPI_COMM_WORLD );
                MPI_Pack( (void*) &it->second.bytes_send.sum,    1, MPI_LONG_LONG_INT, buffer, bytesize, &position, MPI_COMM_WORLD );
                MPI_Pack( (void*) &it->second.bytes_send.cnt,    1, MPI_LONG_LONG_INT, buffer, bytesize, &position, MPI_COMM_WORLD );

                MPI_Pack( (void*) &it->second.bytes_recv.min,    1, MPI_LONG_LONG_INT, buffer, bytesize, &position, MPI_COMM_WORLD );
                MPI_Pack( (void*) &it->second.bytes_recv.max,    1, MPI_LONG_LONG_INT, buffer, bytesize, &position, MPI_COMM_WORLD );
                MPI_Pack( (void*) &it->second.bytes_recv.sum,    1, MPI_LONG_LONG_INT, buffer, bytesize, &position, MPI_COMM_WORLD );
                MPI_Pack( (void*) &it->second.bytes_recv.cnt,    1, MPI_LONG_LONG_INT, buffer, bytesize, &position, MPI_COMM_WORLD );

                MPI_Pack( (void*) &it->second.duration_send.min, 1, MPI_DOUBLE, buffer, bytesize, &position, MPI_COMM_WORLD );
                MPI_Pack( (void*) &it->second.duration_send.max, 1, MPI_DOUBLE, buffer, bytesize, &position, MPI_COMM_WORLD );
                MPI_Pack( (void*) &it->second.duration_send.sum, 1, MPI_DOUBLE, buffer, bytesize, &position, MPI_COMM_WORLD );
                MPI_Pack( (void*) &it->second.duration_send.cnt, 1, MPI_LONG_LONG_INT, buffer, bytesize, &position, MPI_COMM_WORLD );

                MPI_Pack( (void*) &it->second.duration_recv.min, 1, MPI_DOUBLE, buffer, bytesize, &position, MPI_COMM_WORLD );
                MPI_Pack( (void*) &it->second.duration_recv.max, 1, MPI_DOUBLE, buffer, bytesize, &position, MPI_COMM_WORLD );
                MPI_Pack( (void*) &it->second.duration_recv.sum, 1, MPI_DOUBLE, buffer, bytesize, &position, MPI_COMM_WORLD );
                MPI_Pack( (void*) &it->second.duration_recv.cnt, 1, MPI_LONG_LONG_INT, buffer, bytesize, &position, MPI_COMM_WORLD );
            }
            alldata.collectiveMapPerGroup.clear();
        }

        /* extra check that doesn't cost too much */
        MPI_Pack( (void*) &fence, 1, MPI_LONG_LONG_INT, buffer, bytesize, &position, MPI_COMM_WORLD );


        if(alldata.params.dispersion.mode == DISPERSION_MODE_PERCALLPATH)
        {
            /* pack functionDurationSectionCallpathMapGlobal*/

            {
                map< TripleCallpath, FunctionData, ltTripleCallpath >::const_iterator it=    alldata.functionDurationSectionCallpathMapGlobal.begin();
                map< TripleCallpath, FunctionData, ltTripleCallpath >::const_iterator itend= alldata.functionDurationSectionCallpathMapGlobal.end();
                uint64_t len = 0;

                for ( ; it != itend; ++it ) {
                    len = it->second.callpath.length();
                    MPI_Pack( (void*) &it->first.a,              1, MPI_LONG_LONG_INT, buffer, bytesize, &position, MPI_COMM_WORLD );
                    MPI_Pack( (void*) &len,                      1, MPI_LONG_LONG_INT, buffer, bytesize, &position, MPI_COMM_WORLD );
                    MPI_Pack( (void*) &it->first.c,              1, MPI_LONG_LONG_INT, buffer, bytesize, &position, MPI_COMM_WORLD );

                    MPI_Pack( (void*) &it->second.count.min,     1, MPI_LONG_LONG_INT, buffer, bytesize, &position, MPI_COMM_WORLD );
                    MPI_Pack( (void*) &it->second.count.max,     1, MPI_LONG_LONG_INT, buffer, bytesize, &position, MPI_COMM_WORLD );
                    MPI_Pack( (void*) &it->second.count.sum,     1, MPI_LONG_LONG_INT, buffer, bytesize, &position, MPI_COMM_WORLD );
                    MPI_Pack( (void*) &it->second.count.cnt,     1, MPI_LONG_LONG_INT, buffer, bytesize, &position, MPI_COMM_WORLD );

                    MPI_Pack( (void*) &it->second.excl_time.min, 1, MPI_DOUBLE,        buffer, bytesize, &position, MPI_COMM_WORLD );
                    MPI_Pack( (void*) &it->second.excl_time.max, 1, MPI_DOUBLE,        buffer, bytesize, &position, MPI_COMM_WORLD );
                    MPI_Pack( (void*) &it->second.excl_time.sum, 1, MPI_DOUBLE,        buffer, bytesize, &position, MPI_COMM_WORLD );
                    MPI_Pack( (void*) &it->second.excl_time.cnt, 1, MPI_LONG_LONG_INT, buffer, bytesize, &position, MPI_COMM_WORLD );

                    MPI_Pack( (void*) &it->second.incl_time.min, 1, MPI_DOUBLE,        buffer, bytesize, &position, MPI_COMM_WORLD );
                    MPI_Pack( (void*) &it->second.incl_time.max, 1, MPI_DOUBLE,        buffer, bytesize, &position, MPI_COMM_WORLD );
                    MPI_Pack( (void*) &it->second.incl_time.sum, 1, MPI_DOUBLE,        buffer, bytesize, &position, MPI_COMM_WORLD );
                    MPI_Pack( (void*) &it->second.incl_time.cnt, 1, MPI_LONG_LONG_INT, buffer, bytesize, &position, MPI_COMM_WORLD );
                    MPI_Pack( (void*) it->second.callpath.c_str(), len, MPI_CHAR, buffer, bytesize, &position, MPI_COMM_WORLD );
                }
                alldata.functionDurationSectionMapGlobal.clear();

            }

            /* extra check that doesn't cost too much */
            MPI_Pack( (void*) &fence, 1, MPI_LONG_LONG_INT, buffer, bytesize, &position, MPI_COMM_WORLD );
        }
    }

    if ( alldata.params.clustering.enabled ) {

        /* pack functionMapPerRank */

        map< Pair, FunctionData, ltPair >::const_iterator it= alldata.functionMapPerRank.begin();
        map< Pair, FunctionData, ltPair >::const_iterator itend= alldata.functionMapPerRank.end();
        for ( ; it != itend; ++it ) {

            MPI_Pack( (void*) &it->first.a,              1, MPI_LONG_LONG_INT, buffer, bytesize, &position, MPI_COMM_WORLD );
            MPI_Pack( (void*) &it->first.b,              1, MPI_LONG_LONG_INT, buffer, bytesize, &position, MPI_COMM_WORLD );

            MPI_Pack( (void*) &it->second.count.min,     1, MPI_LONG_LONG_INT, buffer, bytesize, &position, MPI_COMM_WORLD );
            MPI_Pack( (void*) &it->second.count.max,     1, MPI_LONG_LONG_INT, buffer, bytesize, &position, MPI_COMM_WORLD );
            MPI_Pack( (void*) &it->second.count.sum,     1, MPI_LONG_LONG_INT, buffer, bytesize, &position, MPI_COMM_WORLD );
            MPI_Pack( (void*) &it->second.count.cnt,     1, MPI_LONG_LONG_INT, buffer, bytesize, &position, MPI_COMM_WORLD );

            MPI_Pack( (void*) &it->second.excl_time.min, 1, MPI_DOUBLE,        buffer, bytesize, &position, MPI_COMM_WORLD );
            MPI_Pack( (void*) &it->second.excl_time.max, 1, MPI_DOUBLE,        buffer, bytesize, &position, MPI_COMM_WORLD );
            MPI_Pack( (void*) &it->second.excl_time.sum, 1, MPI_DOUBLE,        buffer, bytesize, &position, MPI_COMM_WORLD );
            MPI_Pack( (void*) &it->second.excl_time.cnt, 1, MPI_LONG_LONG_INT, buffer, bytesize, &position, MPI_COMM_WORLD );

            MPI_Pack( (void*) &it->second.incl_time.min, 1, MPI_DOUBLE,        buffer, bytesize, &position, MPI_COMM_WORLD );
            MPI_Pack( (void*) &it->second.incl_time.max, 1, MPI_DOUBLE,        buffer, bytesize, &position, MPI_COMM_WORLD );
            MPI_Pack( (void*) &it->second.incl_time.sum, 1, MPI_DOUBLE,        buffer, bytesize, &position, MPI_COMM_WORLD );
            MPI_Pack( (void*) &it->second.incl_time.cnt, 1, MPI_LONG_LONG_INT, buffer, bytesize, &position, MPI_COMM_WORLD );
        }

        /* in case of producing CSV output do not clear map because it is
        needed later */
        if ( !alldata.params.create_csv || 
            ( alldata.params.clustering.enabled && alldata.myRank > 0 ) ) {

            alldata.functionMapPerRank.clear();
        }

        /* extra check that doesn't cost too much */
        MPI_Pack( (void*) &fence, 1, MPI_LONG_LONG_INT, buffer, bytesize, &position, MPI_COMM_WORLD );
    }

    if ( alldata.params.dispersion.mode == DISPERSION_MODE_PERCALLPATH ) {

        /* pack functionCallpathMapPerRank */

        map< TripleCallpath, FunctionData, ltTripleCallpath >::const_iterator it= alldata.functionCallpathMapPerRank.begin();
        map< TripleCallpath, FunctionData, ltTripleCallpath >::const_iterator itend= alldata.functionCallpathMapPerRank.end();
        uint64_t len=0;
        for ( ; it != itend; ++it ) {
        	len = it->first.b.length();
            MPI_Pack( (void*) &it->first.a,              1, MPI_LONG_LONG_INT, buffer, bytesize, &position, MPI_COMM_WORLD );
            MPI_Pack( (void*) &len,              1, MPI_LONG_LONG_INT, buffer, bytesize, &position, MPI_COMM_WORLD );
            MPI_Pack( (void*) &it->first.c,              1, MPI_LONG_LONG_INT, buffer, bytesize, &position, MPI_COMM_WORLD );

            MPI_Pack( (void*) &it->second.count.min,     1, MPI_LONG_LONG_INT, buffer, bytesize, &position, MPI_COMM_WORLD );
            MPI_Pack( (void*) &it->second.count.max,     1, MPI_LONG_LONG_INT, buffer, bytesize, &position, MPI_COMM_WORLD );
            MPI_Pack( (void*) &it->second.count.sum,     1, MPI_LONG_LONG_INT, buffer, bytesize, &position, MPI_COMM_WORLD );
            MPI_Pack( (void*) &it->second.count.cnt,     1, MPI_LONG_LONG_INT, buffer, bytesize, &position, MPI_COMM_WORLD );

            MPI_Pack( (void*) &it->second.excl_time.min, 1, MPI_DOUBLE,        buffer, bytesize, &position, MPI_COMM_WORLD );
            MPI_Pack( (void*) &it->second.excl_time.max, 1, MPI_DOUBLE,        buffer, bytesize, &position, MPI_COMM_WORLD );
            MPI_Pack( (void*) &it->second.excl_time.sum, 1, MPI_DOUBLE,        buffer, bytesize, &position, MPI_COMM_WORLD );
            MPI_Pack( (void*) &it->second.excl_time.cnt, 1, MPI_LONG_LONG_INT, buffer, bytesize, &position, MPI_COMM_WORLD );

            MPI_Pack( (void*) &it->second.incl_time.min, 1, MPI_DOUBLE,        buffer, bytesize, &position, MPI_COMM_WORLD );
            MPI_Pack( (void*) &it->second.incl_time.max, 1, MPI_DOUBLE,        buffer, bytesize, &position, MPI_COMM_WORLD );
            MPI_Pack( (void*) &it->second.incl_time.sum, 1, MPI_DOUBLE,        buffer, bytesize, &position, MPI_COMM_WORLD );
            MPI_Pack( (void*) &it->second.incl_time.cnt, 1, MPI_LONG_LONG_INT, buffer, bytesize, &position, MPI_COMM_WORLD );
            MPI_Pack( (void*) it->first.b.c_str(), len, MPI_CHAR, buffer, bytesize, &position, MPI_COMM_WORLD );

        }

        /* in case of producing CSV output do not clear map because it is
        needed later */
        if ( !alldata.params.create_csv ) {

            alldata.functionCallpathMapPerRank.clear();
        }

        /* extra check that doesn't cost too much */
        MPI_Pack( (void*) &fence, 1, MPI_LONG_LONG_INT, buffer, bytesize, &position, MPI_COMM_WORLD );
    }
    
    if ( alldata.params.dispersion.enabled ) {
        
        /* pack functionMinMaxLocationMap */
        
        map<uint64_t, FunctionMinMaxLocationData>::const_iterator it= alldata.functionMinMaxLocationMap.begin();
        map<uint64_t, FunctionMinMaxLocationData>::const_iterator itend= alldata.functionMinMaxLocationMap.end();
        
        for ( ; it != itend ; ++it ) {
            
            MPI_Pack( (void*) &it->first,                    1, MPI_LONG_LONG_INT, buffer, bytesize, &position, MPI_COMM_WORLD );
            MPI_Pack( (void*) &it->second.location.min,      1, MPI_LONG_LONG_INT, buffer, bytesize, &position, MPI_COMM_WORLD );
            MPI_Pack( (void*) &it->second.location.max,      1, MPI_LONG_LONG_INT, buffer, bytesize, &position, MPI_COMM_WORLD );
            MPI_Pack( (void*) &it->second.location.loc_min,  1, MPI_LONG_LONG_INT, buffer, bytesize, &position, MPI_COMM_WORLD );
            MPI_Pack( (void*) &it->second.location.loc_max,  1, MPI_LONG_LONG_INT, buffer, bytesize, &position, MPI_COMM_WORLD );
            MPI_Pack( (void*) &it->second.location.time_max, 1, MPI_LONG_LONG_INT, buffer, bytesize, &position, MPI_COMM_WORLD );
            MPI_Pack( (void*) &it->second.location.time_max, 1, MPI_LONG_LONG_INT, buffer, bytesize, &position, MPI_COMM_WORLD );
        }
        
        /* extra check that doesn't cost too much */
        MPI_Pack( (void*) &fence, 1, MPI_LONG_LONG_INT, buffer, bytesize, &position, MPI_COMM_WORLD );
    }

    if ( alldata.params.write_csv_msg_sizes ) {
        /* pack messageMapPerSize */

        for ( map<uint64_t, MessageSizeData>::const_iterator it = alldata.messageMapPerSize.begin();
              it != alldata.messageMapPerSize.end();
              it++ )
        {
           MPI_Pack( (void*) &it->first,           1, MPI_LONG_LONG_INT, buffer, bytesize, &position, MPI_COMM_WORLD );
           MPI_Pack( (void*) &it->second.count,    1, MPI_LONG_LONG_INT, buffer, bytesize, &position, MPI_COMM_WORLD );
           MPI_Pack( (void*) &it->second.time.min, 1, MPI_LONG_LONG_INT, buffer, bytesize, &position, MPI_COMM_WORLD );
           MPI_Pack( (void*) &it->second.time.max, 1, MPI_LONG_LONG_INT, buffer, bytesize, &position, MPI_COMM_WORLD );
           MPI_Pack( (void*) &it->second.time.sum, 1, MPI_LONG_LONG_INT, buffer, bytesize, &position, MPI_COMM_WORLD );
           MPI_Pack( (void*) &it->second.time.cnt, 1, MPI_LONG_LONG_INT, buffer, bytesize, &position, MPI_COMM_WORLD );
        }

        /* extra check that doesn't cost too much */
        MPI_Pack( (void*) &fence, 1, MPI_LONG_LONG_INT, buffer, bytesize, &position, MPI_COMM_WORLD );
    }

    return buffer;
}


/* prepare alldata for unpack, return buffer of sufficient size */
static char* prepare_worker_data( AllData& alldata, uint32_t sizes[PACK_NUM_PACKS] ) {

    uint32_t bytesize= sizes[PACK_TOTAL_SIZE];

    return alldata.guaranteePackBuffer( bytesize );
}

/* unpack the received worker data and add it to the local alldata */
static void unpack_worker_data( AllData& alldata, uint32_t sizes[PACK_NUM_PACKS] ) {

    uint64_t fence;

    /* unpack parts */
    int position= 0;
    char* buffer= alldata.getPackBuffer( );

    /* extra check that doesn't cost too much */
    fence= 0;
    MPI_Unpack( buffer, sizes[PACK_TOTAL_SIZE], &position, &fence, 1, MPI_LONG_LONG_INT, MPI_COMM_WORLD );
    assert( FENCE == fence );


    /* chararray for unpacking the callpath */
    char* callpath = (char*) malloc(alldata.maxCallpathLength * sizeof(char));

    if ( alldata.params.create_tex ) {

        /* unpack functionMapGlobal */
        for ( uint32_t i= 0; i < sizes[PACK_FUNCTION]; i++ ) {

            uint64_t func;
            FunctionData tmp;

            MPI_Unpack( buffer, sizes[PACK_TOTAL_SIZE], &position, &func,              1, MPI_LONG_LONG_INT, MPI_COMM_WORLD );

            MPI_Unpack( buffer, sizes[PACK_TOTAL_SIZE], &position, &tmp.count.min,     1, MPI_LONG_LONG_INT, MPI_COMM_WORLD );
            MPI_Unpack( buffer, sizes[PACK_TOTAL_SIZE], &position, &tmp.count.max,     1, MPI_LONG_LONG_INT, MPI_COMM_WORLD );
            MPI_Unpack( buffer, sizes[PACK_TOTAL_SIZE], &position, &tmp.count.sum,     1, MPI_LONG_LONG_INT, MPI_COMM_WORLD );
            MPI_Unpack( buffer, sizes[PACK_TOTAL_SIZE], &position, &tmp.count.cnt,     1, MPI_LONG_LONG_INT, MPI_COMM_WORLD );

            MPI_Unpack( buffer, sizes[PACK_TOTAL_SIZE], &position, &tmp.excl_time.min, 1, MPI_DOUBLE,        MPI_COMM_WORLD );
            MPI_Unpack( buffer, sizes[PACK_TOTAL_SIZE], &position, &tmp.excl_time.max, 1, MPI_DOUBLE,        MPI_COMM_WORLD );
            MPI_Unpack( buffer, sizes[PACK_TOTAL_SIZE], &position, &tmp.excl_time.sum, 1, MPI_DOUBLE,        MPI_COMM_WORLD );
            MPI_Unpack( buffer, sizes[PACK_TOTAL_SIZE], &position, &tmp.excl_time.cnt, 1, MPI_LONG_LONG_INT, MPI_COMM_WORLD );

            MPI_Unpack( buffer, sizes[PACK_TOTAL_SIZE], &position, &tmp.incl_time.min, 1, MPI_DOUBLE,        MPI_COMM_WORLD );
            MPI_Unpack( buffer, sizes[PACK_TOTAL_SIZE], &position, &tmp.incl_time.max, 1, MPI_DOUBLE,        MPI_COMM_WORLD );
            MPI_Unpack( buffer, sizes[PACK_TOTAL_SIZE], &position, &tmp.incl_time.sum, 1, MPI_DOUBLE,        MPI_COMM_WORLD );
            MPI_Unpack( buffer, sizes[PACK_TOTAL_SIZE], &position, &tmp.incl_time.cnt, 1, MPI_LONG_LONG_INT, MPI_COMM_WORLD );

            alldata.functionMapGlobal[ func ].add( tmp );
        }
        /* extra check that doesn't cost too much */
        fence= 0;
        MPI_Unpack( buffer, sizes[PACK_TOTAL_SIZE], &position, &fence, 1, MPI_LONG_LONG_INT, MPI_COMM_WORLD );
        assert( FENCE == fence );
        if(alldata.params.dispersion.mode == DISPERSION_MODE_PERCALLPATH)
        {
            /* unpack functionCallpathMapGlobal */
               for ( uint32_t i= 0; i < sizes[PACK_FUNCTION_CALLPATH]; i++ ) {

                    uint64_t func;
                    uint64_t len;
                    FunctionData tmp;

                      MPI_Unpack( buffer, sizes[PACK_TOTAL_SIZE], &position, &func,              1, MPI_LONG_LONG_INT, MPI_COMM_WORLD );
                      MPI_Unpack( buffer, sizes[PACK_TOTAL_SIZE], &position, &len,              1, MPI_LONG_LONG_INT, MPI_COMM_WORLD );
                      MPI_Unpack( buffer, sizes[PACK_TOTAL_SIZE], &position, &tmp.count.min,     1, MPI_LONG_LONG_INT, MPI_COMM_WORLD );
                      MPI_Unpack( buffer, sizes[PACK_TOTAL_SIZE], &position, &tmp.count.max,     1, MPI_LONG_LONG_INT, MPI_COMM_WORLD );
                      MPI_Unpack( buffer, sizes[PACK_TOTAL_SIZE], &position, &tmp.count.sum,     1, MPI_LONG_LONG_INT, MPI_COMM_WORLD );
                      MPI_Unpack( buffer, sizes[PACK_TOTAL_SIZE], &position, &tmp.count.cnt,     1, MPI_LONG_LONG_INT, MPI_COMM_WORLD );

                      MPI_Unpack( buffer, sizes[PACK_TOTAL_SIZE], &position, &tmp.excl_time.min, 1, MPI_DOUBLE,        MPI_COMM_WORLD );
                      MPI_Unpack( buffer, sizes[PACK_TOTAL_SIZE], &position, &tmp.excl_time.max, 1, MPI_DOUBLE,        MPI_COMM_WORLD );
                      MPI_Unpack( buffer, sizes[PACK_TOTAL_SIZE], &position, &tmp.excl_time.sum, 1, MPI_DOUBLE,        MPI_COMM_WORLD );
                      MPI_Unpack( buffer, sizes[PACK_TOTAL_SIZE], &position, &tmp.excl_time.cnt, 1, MPI_LONG_LONG_INT, MPI_COMM_WORLD );

                      MPI_Unpack( buffer, sizes[PACK_TOTAL_SIZE], &position, &tmp.incl_time.min, 1, MPI_DOUBLE,        MPI_COMM_WORLD );
                      MPI_Unpack( buffer, sizes[PACK_TOTAL_SIZE], &position, &tmp.incl_time.max, 1, MPI_DOUBLE,        MPI_COMM_WORLD );
                      MPI_Unpack( buffer, sizes[PACK_TOTAL_SIZE], &position, &tmp.incl_time.sum, 1, MPI_DOUBLE,        MPI_COMM_WORLD );
                      MPI_Unpack( buffer, sizes[PACK_TOTAL_SIZE], &position, &tmp.incl_time.cnt, 1, MPI_LONG_LONG_INT, MPI_COMM_WORLD );

                      MPI_Unpack( buffer, sizes[PACK_TOTAL_SIZE], &position, callpath, len, MPI_CHAR, MPI_COMM_WORLD );
                    tmp.callpath = callpath;
                    tmp.callpath = tmp.callpath.substr (0,len);
                    alldata.functionCallpathMapGlobal[ PairCallpath(func,tmp.callpath) ].add( tmp );
                }
                /* extra check that doesn't cost too much */
                fence= 0;
                MPI_Unpack( buffer, sizes[PACK_TOTAL_SIZE], &position, &fence, 1, MPI_LONG_LONG_INT, MPI_COMM_WORLD );
                assert( FENCE == fence );
        }
        /* unpack functionDurationSectionMapGlobal */
        for ( uint32_t i= 0; i < sizes[PACK_FUNCTION_DURATION]; i++ ) {
            
            uint64_t func;
            uint64_t bin;
            FunctionData tmp;
            
            MPI_Unpack( buffer, sizes[PACK_TOTAL_SIZE], &position, &func,              1, MPI_LONG_LONG_INT, MPI_COMM_WORLD );
            MPI_Unpack( buffer, sizes[PACK_TOTAL_SIZE], &position, &bin,               1, MPI_LONG_LONG_INT, MPI_COMM_WORLD );
            
            MPI_Unpack( buffer, sizes[PACK_TOTAL_SIZE], &position, &tmp.count.min,     1, MPI_LONG_LONG_INT, MPI_COMM_WORLD );
            MPI_Unpack( buffer, sizes[PACK_TOTAL_SIZE], &position, &tmp.count.max,     1, MPI_LONG_LONG_INT, MPI_COMM_WORLD );
            MPI_Unpack( buffer, sizes[PACK_TOTAL_SIZE], &position, &tmp.count.sum,     1, MPI_LONG_LONG_INT, MPI_COMM_WORLD );
            MPI_Unpack( buffer, sizes[PACK_TOTAL_SIZE], &position, &tmp.count.cnt,     1, MPI_LONG_LONG_INT, MPI_COMM_WORLD );
            
            MPI_Unpack( buffer, sizes[PACK_TOTAL_SIZE], &position, &tmp.excl_time.min, 1, MPI_DOUBLE,        MPI_COMM_WORLD );
            MPI_Unpack( buffer, sizes[PACK_TOTAL_SIZE], &position, &tmp.excl_time.max, 1, MPI_DOUBLE,        MPI_COMM_WORLD );
            MPI_Unpack( buffer, sizes[PACK_TOTAL_SIZE], &position, &tmp.excl_time.sum, 1, MPI_DOUBLE,        MPI_COMM_WORLD );
            MPI_Unpack( buffer, sizes[PACK_TOTAL_SIZE], &position, &tmp.excl_time.cnt, 1, MPI_LONG_LONG_INT, MPI_COMM_WORLD );
            
            MPI_Unpack( buffer, sizes[PACK_TOTAL_SIZE], &position, &tmp.incl_time.min, 1, MPI_DOUBLE,        MPI_COMM_WORLD );
            MPI_Unpack( buffer, sizes[PACK_TOTAL_SIZE], &position, &tmp.incl_time.max, 1, MPI_DOUBLE,        MPI_COMM_WORLD );
            MPI_Unpack( buffer, sizes[PACK_TOTAL_SIZE], &position, &tmp.incl_time.sum, 1, MPI_DOUBLE,        MPI_COMM_WORLD );
            MPI_Unpack( buffer, sizes[PACK_TOTAL_SIZE], &position, &tmp.incl_time.cnt, 1, MPI_LONG_LONG_INT, MPI_COMM_WORLD );
            
            alldata.functionDurationSectionMapGlobal[ Pair( func, bin ) ].add( tmp );
        }
        
        /* extra check that doesn't cost too much */
        fence= 0;
        MPI_Unpack( buffer, sizes[PACK_TOTAL_SIZE], &position, &fence, 1, MPI_LONG_LONG_INT, MPI_COMM_WORLD );
        assert( FENCE == fence );
        
        /* unpack counterMapGlobal */
        for ( uint32_t i= 0; i < sizes[PACK_COUNTER]; i++ ) {

            uint64_t a;
            uint64_t b;
            CounterData tmp;

            MPI_Unpack( buffer, sizes[PACK_TOTAL_SIZE], &position, &a,                 1, MPI_LONG_LONG_INT, MPI_COMM_WORLD );
            MPI_Unpack( buffer, sizes[PACK_TOTAL_SIZE], &position, &b,                 1, MPI_LONG_LONG_INT, MPI_COMM_WORLD );

            MPI_Unpack( buffer, sizes[PACK_TOTAL_SIZE], &position, &tmp.count.min,     1, MPI_LONG_LONG_INT, MPI_COMM_WORLD );
            MPI_Unpack( buffer, sizes[PACK_TOTAL_SIZE], &position, &tmp.count.max,     1, MPI_LONG_LONG_INT, MPI_COMM_WORLD );
            MPI_Unpack( buffer, sizes[PACK_TOTAL_SIZE], &position, &tmp.count.sum,     1, MPI_LONG_LONG_INT, MPI_COMM_WORLD );
            MPI_Unpack( buffer, sizes[PACK_TOTAL_SIZE], &position, &tmp.count.cnt,     1, MPI_LONG_LONG_INT, MPI_COMM_WORLD );

            MPI_Unpack( buffer, sizes[PACK_TOTAL_SIZE], &position, &tmp.excl_time.min, 1, MPI_DOUBLE,        MPI_COMM_WORLD );
            MPI_Unpack( buffer, sizes[PACK_TOTAL_SIZE], &position, &tmp.excl_time.max, 1, MPI_DOUBLE,        MPI_COMM_WORLD );
            MPI_Unpack( buffer, sizes[PACK_TOTAL_SIZE], &position, &tmp.excl_time.sum, 1, MPI_DOUBLE,        MPI_COMM_WORLD );
            MPI_Unpack( buffer, sizes[PACK_TOTAL_SIZE], &position, &tmp.excl_time.cnt, 1, MPI_LONG_LONG_INT, MPI_COMM_WORLD );

            MPI_Unpack( buffer, sizes[PACK_TOTAL_SIZE], &position, &tmp.incl_time.min, 1, MPI_DOUBLE,        MPI_COMM_WORLD );
            MPI_Unpack( buffer, sizes[PACK_TOTAL_SIZE], &position, &tmp.incl_time.max, 1, MPI_DOUBLE,        MPI_COMM_WORLD );
            MPI_Unpack( buffer, sizes[PACK_TOTAL_SIZE], &position, &tmp.incl_time.sum, 1, MPI_DOUBLE,        MPI_COMM_WORLD );
            MPI_Unpack( buffer, sizes[PACK_TOTAL_SIZE], &position, &tmp.incl_time.cnt, 1, MPI_LONG_LONG_INT, MPI_COMM_WORLD );

            alldata.counterMapGlobal[ Pair( a, b ) ].add( tmp );
        }

        /* extra check that doesn't cost too much */
        fence= 0;
        MPI_Unpack( buffer, sizes[PACK_TOTAL_SIZE], &position, &fence, 1, MPI_LONG_LONG_INT, MPI_COMM_WORLD );
        assert( FENCE == fence );

        /* unpack messageMapPerGroupPair */
        for ( uint32_t i= 0; i < sizes[PACK_MSG_GROUP_PAIR]; i++ ) {

            uint64_t a;
            uint64_t b;
            MessageData tmp;

            MPI_Unpack( buffer, sizes[PACK_TOTAL_SIZE], &position,  (void*) &a,                     1, MPI_LONG_LONG_INT, MPI_COMM_WORLD );
            MPI_Unpack( buffer, sizes[PACK_TOTAL_SIZE], &position,  (void*) &b,                     1, MPI_LONG_LONG_INT, MPI_COMM_WORLD );

            MPI_Unpack( buffer, sizes[PACK_TOTAL_SIZE], &position,  (void*) &tmp.count_send.min,    1, MPI_LONG_LONG_INT, MPI_COMM_WORLD );
            MPI_Unpack( buffer, sizes[PACK_TOTAL_SIZE], &position,  (void*) &tmp.count_send.max,    1, MPI_LONG_LONG_INT, MPI_COMM_WORLD );
            MPI_Unpack( buffer, sizes[PACK_TOTAL_SIZE], &position,  (void*) &tmp.count_send.sum,    1, MPI_LONG_LONG_INT, MPI_COMM_WORLD );
            MPI_Unpack( buffer, sizes[PACK_TOTAL_SIZE], &position,  (void*) &tmp.count_send.cnt,    1, MPI_LONG_LONG_INT, MPI_COMM_WORLD );

            MPI_Unpack( buffer, sizes[PACK_TOTAL_SIZE], &position,  (void*) &tmp.count_recv.min,    1, MPI_LONG_LONG_INT, MPI_COMM_WORLD );
            MPI_Unpack( buffer, sizes[PACK_TOTAL_SIZE], &position,  (void*) &tmp.count_recv.max,    1, MPI_LONG_LONG_INT, MPI_COMM_WORLD );
            MPI_Unpack( buffer, sizes[PACK_TOTAL_SIZE], &position,  (void*) &tmp.count_recv.sum,    1, MPI_LONG_LONG_INT, MPI_COMM_WORLD );
            MPI_Unpack( buffer, sizes[PACK_TOTAL_SIZE], &position,  (void*) &tmp.count_recv.cnt,    1, MPI_LONG_LONG_INT, MPI_COMM_WORLD );

            MPI_Unpack( buffer, sizes[PACK_TOTAL_SIZE], &position,  (void*) &tmp.bytes_send.min,    1, MPI_LONG_LONG_INT, MPI_COMM_WORLD );
            MPI_Unpack( buffer, sizes[PACK_TOTAL_SIZE], &position,  (void*) &tmp.bytes_send.max,    1, MPI_LONG_LONG_INT, MPI_COMM_WORLD );
            MPI_Unpack( buffer, sizes[PACK_TOTAL_SIZE], &position,  (void*) &tmp.bytes_send.sum,    1, MPI_LONG_LONG_INT, MPI_COMM_WORLD );
            MPI_Unpack( buffer, sizes[PACK_TOTAL_SIZE], &position,  (void*) &tmp.bytes_send.cnt,    1, MPI_LONG_LONG_INT, MPI_COMM_WORLD );

            MPI_Unpack( buffer, sizes[PACK_TOTAL_SIZE], &position,  (void*) &tmp.bytes_recv.min,    1, MPI_LONG_LONG_INT, MPI_COMM_WORLD );
            MPI_Unpack( buffer, sizes[PACK_TOTAL_SIZE], &position,  (void*) &tmp.bytes_recv.max,    1, MPI_LONG_LONG_INT, MPI_COMM_WORLD );
            MPI_Unpack( buffer, sizes[PACK_TOTAL_SIZE], &position,  (void*) &tmp.bytes_recv.sum,    1, MPI_LONG_LONG_INT, MPI_COMM_WORLD );
            MPI_Unpack( buffer, sizes[PACK_TOTAL_SIZE], &position,  (void*) &tmp.bytes_recv.cnt,    1, MPI_LONG_LONG_INT, MPI_COMM_WORLD );

            MPI_Unpack( buffer, sizes[PACK_TOTAL_SIZE], &position,  (void*) &tmp.duration_send.min, 1, MPI_DOUBLE,        MPI_COMM_WORLD );
            MPI_Unpack( buffer, sizes[PACK_TOTAL_SIZE], &position,  (void*) &tmp.duration_send.max, 1, MPI_DOUBLE,        MPI_COMM_WORLD );
            MPI_Unpack( buffer, sizes[PACK_TOTAL_SIZE], &position,  (void*) &tmp.duration_send.sum, 1, MPI_DOUBLE,        MPI_COMM_WORLD );
            MPI_Unpack( buffer, sizes[PACK_TOTAL_SIZE], &position,  (void*) &tmp.duration_send.cnt, 1, MPI_LONG_LONG_INT, MPI_COMM_WORLD );

            MPI_Unpack( buffer, sizes[PACK_TOTAL_SIZE], &position,  (void*) &tmp.duration_recv.min, 1, MPI_DOUBLE,        MPI_COMM_WORLD );
            MPI_Unpack( buffer, sizes[PACK_TOTAL_SIZE], &position,  (void*) &tmp.duration_recv.max, 1, MPI_DOUBLE,        MPI_COMM_WORLD );
            MPI_Unpack( buffer, sizes[PACK_TOTAL_SIZE], &position,  (void*) &tmp.duration_recv.sum, 1, MPI_DOUBLE,        MPI_COMM_WORLD );
            MPI_Unpack( buffer, sizes[PACK_TOTAL_SIZE], &position,  (void*) &tmp.duration_recv.cnt, 1, MPI_LONG_LONG_INT, MPI_COMM_WORLD );

            alldata.messageMapPerGroupPair[ Pair(a,b) ].add( tmp );
        }

        /* extra check that doesn't cost too much */
        fence= 0;
        MPI_Unpack( buffer, sizes[PACK_TOTAL_SIZE], &position, &fence, 1, MPI_LONG_LONG_INT, MPI_COMM_WORLD );
        assert( FENCE == fence );

        /* unpack messageMapPerGroup */
        for ( uint32_t i= 0; i < sizes[PACK_MSG_GROUP]; i++ ) {

            uint64_t a;
            MessageData tmp;

            MPI_Unpack( buffer, sizes[PACK_TOTAL_SIZE], &position,  (void*) &a,                     1, MPI_LONG_LONG_INT, MPI_COMM_WORLD );

            MPI_Unpack( buffer, sizes[PACK_TOTAL_SIZE], &position,  (void*) &tmp.count_send.min,    1, MPI_LONG_LONG_INT, MPI_COMM_WORLD );
            MPI_Unpack( buffer, sizes[PACK_TOTAL_SIZE], &position,  (void*) &tmp.count_send.max,    1, MPI_LONG_LONG_INT, MPI_COMM_WORLD );
            MPI_Unpack( buffer, sizes[PACK_TOTAL_SIZE], &position,  (void*) &tmp.count_send.sum,    1, MPI_LONG_LONG_INT, MPI_COMM_WORLD );
            MPI_Unpack( buffer, sizes[PACK_TOTAL_SIZE], &position,  (void*) &tmp.count_send.cnt,    1, MPI_LONG_LONG_INT, MPI_COMM_WORLD );

            MPI_Unpack( buffer, sizes[PACK_TOTAL_SIZE], &position,  (void*) &tmp.count_recv.min,    1, MPI_LONG_LONG_INT, MPI_COMM_WORLD );
            MPI_Unpack( buffer, sizes[PACK_TOTAL_SIZE], &position,  (void*) &tmp.count_recv.max,    1, MPI_LONG_LONG_INT, MPI_COMM_WORLD );
            MPI_Unpack( buffer, sizes[PACK_TOTAL_SIZE], &position,  (void*) &tmp.count_recv.sum,    1, MPI_LONG_LONG_INT, MPI_COMM_WORLD );
            MPI_Unpack( buffer, sizes[PACK_TOTAL_SIZE], &position,  (void*) &tmp.count_recv.cnt,    1, MPI_LONG_LONG_INT, MPI_COMM_WORLD );

            MPI_Unpack( buffer, sizes[PACK_TOTAL_SIZE], &position,  (void*) &tmp.bytes_send.min,    1, MPI_LONG_LONG_INT, MPI_COMM_WORLD );
            MPI_Unpack( buffer, sizes[PACK_TOTAL_SIZE], &position,  (void*) &tmp.bytes_send.max,    1, MPI_LONG_LONG_INT, MPI_COMM_WORLD );
            MPI_Unpack( buffer, sizes[PACK_TOTAL_SIZE], &position,  (void*) &tmp.bytes_send.sum,    1, MPI_LONG_LONG_INT, MPI_COMM_WORLD );
            MPI_Unpack( buffer, sizes[PACK_TOTAL_SIZE], &position,  (void*) &tmp.bytes_send.cnt,    1, MPI_LONG_LONG_INT, MPI_COMM_WORLD );

            MPI_Unpack( buffer, sizes[PACK_TOTAL_SIZE], &position,  (void*) &tmp.bytes_recv.min,    1, MPI_LONG_LONG_INT, MPI_COMM_WORLD );
            MPI_Unpack( buffer, sizes[PACK_TOTAL_SIZE], &position,  (void*) &tmp.bytes_recv.max,    1, MPI_LONG_LONG_INT, MPI_COMM_WORLD );
            MPI_Unpack( buffer, sizes[PACK_TOTAL_SIZE], &position,  (void*) &tmp.bytes_recv.sum,    1, MPI_LONG_LONG_INT, MPI_COMM_WORLD );
            MPI_Unpack( buffer, sizes[PACK_TOTAL_SIZE], &position,  (void*) &tmp.bytes_recv.cnt,    1, MPI_LONG_LONG_INT, MPI_COMM_WORLD );

            MPI_Unpack( buffer, sizes[PACK_TOTAL_SIZE], &position,  (void*) &tmp.duration_send.min, 1, MPI_DOUBLE,        MPI_COMM_WORLD );
            MPI_Unpack( buffer, sizes[PACK_TOTAL_SIZE], &position,  (void*) &tmp.duration_send.max, 1, MPI_DOUBLE,        MPI_COMM_WORLD );
            MPI_Unpack( buffer, sizes[PACK_TOTAL_SIZE], &position,  (void*) &tmp.duration_send.sum, 1, MPI_DOUBLE,        MPI_COMM_WORLD );
            MPI_Unpack( buffer, sizes[PACK_TOTAL_SIZE], &position,  (void*) &tmp.duration_send.cnt, 1, MPI_LONG_LONG_INT, MPI_COMM_WORLD );

            MPI_Unpack( buffer, sizes[PACK_TOTAL_SIZE], &position,  (void*) &tmp.duration_recv.min, 1, MPI_DOUBLE,        MPI_COMM_WORLD );
            MPI_Unpack( buffer, sizes[PACK_TOTAL_SIZE], &position,  (void*) &tmp.duration_recv.max, 1, MPI_DOUBLE,        MPI_COMM_WORLD );
            MPI_Unpack( buffer, sizes[PACK_TOTAL_SIZE], &position,  (void*) &tmp.duration_recv.sum, 1, MPI_DOUBLE,        MPI_COMM_WORLD );
            MPI_Unpack( buffer, sizes[PACK_TOTAL_SIZE], &position,  (void*) &tmp.duration_recv.cnt, 1, MPI_LONG_LONG_INT, MPI_COMM_WORLD );

            alldata.messageMapPerGroup[ a ].add( tmp );
        }

        /* extra check that doesn't cost too much */
        fence= 0;
        MPI_Unpack( buffer, sizes[PACK_TOTAL_SIZE], &position, &fence, 1, MPI_LONG_LONG_INT, MPI_COMM_WORLD );
        assert( FENCE == fence );

        /* unpack messageSpeedMapPerLength */
        for ( uint32_t i= 0; i < sizes[PACK_MSG_SPEED]; i++ ) {

            uint64_t a;
            uint64_t b;
            MessageSpeedData tmp;

            MPI_Unpack( buffer, sizes[PACK_TOTAL_SIZE], &position,  (void*) &a,             1, MPI_LONG_LONG_INT, MPI_COMM_WORLD );
            MPI_Unpack( buffer, sizes[PACK_TOTAL_SIZE], &position,  (void*) &b,             1, MPI_LONG_LONG_INT, MPI_COMM_WORLD );

            MPI_Unpack( buffer, sizes[PACK_TOTAL_SIZE], &position,  (void*) &tmp.count.min, 1, MPI_LONG_LONG_INT, MPI_COMM_WORLD );
            MPI_Unpack( buffer, sizes[PACK_TOTAL_SIZE], &position,  (void*) &tmp.count.max, 1, MPI_LONG_LONG_INT, MPI_COMM_WORLD );
            MPI_Unpack( buffer, sizes[PACK_TOTAL_SIZE], &position,  (void*) &tmp.count.sum, 1, MPI_LONG_LONG_INT, MPI_COMM_WORLD );
            MPI_Unpack( buffer, sizes[PACK_TOTAL_SIZE], &position,  (void*) &tmp.count.cnt, 1, MPI_LONG_LONG_INT, MPI_COMM_WORLD );

            alldata.messageSpeedMapPerLength[ Pair(a,b) ].add( tmp );
        }

        /* extra check that doesn't cost too much */
        fence= 0;
        MPI_Unpack( buffer, sizes[PACK_TOTAL_SIZE], &position, &fence, 1, MPI_LONG_LONG_INT, MPI_COMM_WORLD );
        assert( FENCE == fence );

        /* unpack collectiveMapPerGroup */
        for ( uint32_t i= 0; i < sizes[PACK_COLLECTIVE_GROUP]; i++ ) {

            uint64_t a;
            uint64_t b;
            CollectiveData tmp;

            MPI_Unpack( buffer, sizes[PACK_TOTAL_SIZE], &position,  (void*) &a,                     1, MPI_LONG_LONG_INT, MPI_COMM_WORLD );
            MPI_Unpack( buffer, sizes[PACK_TOTAL_SIZE], &position,  (void*) &b,                     1, MPI_LONG_LONG_INT, MPI_COMM_WORLD );

            MPI_Unpack( buffer, sizes[PACK_TOTAL_SIZE], &position,  (void*) &tmp.count_send.min,    1, MPI_LONG_LONG_INT, MPI_COMM_WORLD );
            MPI_Unpack( buffer, sizes[PACK_TOTAL_SIZE], &position,  (void*) &tmp.count_send.max,    1, MPI_LONG_LONG_INT, MPI_COMM_WORLD );
            MPI_Unpack( buffer, sizes[PACK_TOTAL_SIZE], &position,  (void*) &tmp.count_send.sum,    1, MPI_LONG_LONG_INT, MPI_COMM_WORLD );
            MPI_Unpack( buffer, sizes[PACK_TOTAL_SIZE], &position,  (void*) &tmp.count_send.cnt,    1, MPI_LONG_LONG_INT, MPI_COMM_WORLD );

            MPI_Unpack( buffer, sizes[PACK_TOTAL_SIZE], &position,  (void*) &tmp.count_recv.min,    1, MPI_LONG_LONG_INT, MPI_COMM_WORLD );
            MPI_Unpack( buffer, sizes[PACK_TOTAL_SIZE], &position,  (void*) &tmp.count_recv.max,    1, MPI_LONG_LONG_INT, MPI_COMM_WORLD );
            MPI_Unpack( buffer, sizes[PACK_TOTAL_SIZE], &position,  (void*) &tmp.count_recv.sum,    1, MPI_LONG_LONG_INT, MPI_COMM_WORLD );
            MPI_Unpack( buffer, sizes[PACK_TOTAL_SIZE], &position,  (void*) &tmp.count_recv.cnt,    1, MPI_LONG_LONG_INT, MPI_COMM_WORLD );

            MPI_Unpack( buffer, sizes[PACK_TOTAL_SIZE], &position,  (void*) &tmp.bytes_send.min,    1, MPI_LONG_LONG_INT, MPI_COMM_WORLD );
            MPI_Unpack( buffer, sizes[PACK_TOTAL_SIZE], &position,  (void*) &tmp.bytes_send.max,    1, MPI_LONG_LONG_INT, MPI_COMM_WORLD );
            MPI_Unpack( buffer, sizes[PACK_TOTAL_SIZE], &position,  (void*) &tmp.bytes_send.sum,    1, MPI_LONG_LONG_INT, MPI_COMM_WORLD );
            MPI_Unpack( buffer, sizes[PACK_TOTAL_SIZE], &position,  (void*) &tmp.bytes_send.cnt,    1, MPI_LONG_LONG_INT, MPI_COMM_WORLD );

            MPI_Unpack( buffer, sizes[PACK_TOTAL_SIZE], &position,  (void*) &tmp.bytes_recv.min,    1, MPI_LONG_LONG_INT, MPI_COMM_WORLD );
            MPI_Unpack( buffer, sizes[PACK_TOTAL_SIZE], &position,  (void*) &tmp.bytes_recv.max,    1, MPI_LONG_LONG_INT, MPI_COMM_WORLD );
            MPI_Unpack( buffer, sizes[PACK_TOTAL_SIZE], &position,  (void*) &tmp.bytes_recv.sum,    1, MPI_LONG_LONG_INT, MPI_COMM_WORLD );
            MPI_Unpack( buffer, sizes[PACK_TOTAL_SIZE], &position,  (void*) &tmp.bytes_recv.cnt,    1, MPI_LONG_LONG_INT, MPI_COMM_WORLD );

            MPI_Unpack( buffer, sizes[PACK_TOTAL_SIZE], &position,  (void*) &tmp.duration_send.min, 1, MPI_DOUBLE,        MPI_COMM_WORLD );
            MPI_Unpack( buffer, sizes[PACK_TOTAL_SIZE], &position,  (void*) &tmp.duration_send.max, 1, MPI_DOUBLE,        MPI_COMM_WORLD );
            MPI_Unpack( buffer, sizes[PACK_TOTAL_SIZE], &position,  (void*) &tmp.duration_send.sum, 1, MPI_DOUBLE,        MPI_COMM_WORLD );
            MPI_Unpack( buffer, sizes[PACK_TOTAL_SIZE], &position,  (void*) &tmp.duration_send.cnt, 1, MPI_LONG_LONG_INT, MPI_COMM_WORLD );

            MPI_Unpack( buffer, sizes[PACK_TOTAL_SIZE], &position,  (void*) &tmp.duration_recv.min, 1, MPI_DOUBLE,        MPI_COMM_WORLD );
            MPI_Unpack( buffer, sizes[PACK_TOTAL_SIZE], &position,  (void*) &tmp.duration_recv.max, 1, MPI_DOUBLE,        MPI_COMM_WORLD );
            MPI_Unpack( buffer, sizes[PACK_TOTAL_SIZE], &position,  (void*) &tmp.duration_recv.sum, 1, MPI_DOUBLE,        MPI_COMM_WORLD );
            MPI_Unpack( buffer, sizes[PACK_TOTAL_SIZE], &position,  (void*) &tmp.duration_recv.cnt, 1, MPI_LONG_LONG_INT, MPI_COMM_WORLD );

            alldata.collectiveMapPerGroup[ Pair(a,b) ].add( tmp );
        }

        /* extra check that doesn't cost too much */
        fence= 0;
        MPI_Unpack( buffer, sizes[PACK_TOTAL_SIZE], &position, &fence, 1, MPI_LONG_LONG_INT, MPI_COMM_WORLD );
        assert( FENCE == fence );

        if(alldata.params.dispersion.mode == DISPERSION_MODE_PERCALLPATH)
        {
        /* unpack functionDurationSectionCallpathMapGlobal */
                for ( uint32_t i= 0; i < sizes[PACK_FUNCTION_DURATION_CALLPATH]; i++ ) {

                    uint64_t func;
                    uint64_t bin;
                    uint64_t len;
                    FunctionData tmp;

                    MPI_Unpack( buffer, sizes[PACK_TOTAL_SIZE], &position, &func,              1, MPI_LONG_LONG_INT, MPI_COMM_WORLD );
                    MPI_Unpack( buffer, sizes[PACK_TOTAL_SIZE], &position, &len,              1, MPI_LONG_LONG_INT, MPI_COMM_WORLD );
                    MPI_Unpack( buffer, sizes[PACK_TOTAL_SIZE], &position, &bin,               1, MPI_LONG_LONG_INT, MPI_COMM_WORLD );

                    MPI_Unpack( buffer, sizes[PACK_TOTAL_SIZE], &position, &tmp.count.min,     1, MPI_LONG_LONG_INT, MPI_COMM_WORLD );
                    MPI_Unpack( buffer, sizes[PACK_TOTAL_SIZE], &position, &tmp.count.max,     1, MPI_LONG_LONG_INT, MPI_COMM_WORLD );
                    MPI_Unpack( buffer, sizes[PACK_TOTAL_SIZE], &position, &tmp.count.sum,     1, MPI_LONG_LONG_INT, MPI_COMM_WORLD );
                    MPI_Unpack( buffer, sizes[PACK_TOTAL_SIZE], &position, &tmp.count.cnt,     1, MPI_LONG_LONG_INT, MPI_COMM_WORLD );

                    MPI_Unpack( buffer, sizes[PACK_TOTAL_SIZE], &position, &tmp.excl_time.min, 1, MPI_DOUBLE,        MPI_COMM_WORLD );
                    MPI_Unpack( buffer, sizes[PACK_TOTAL_SIZE], &position, &tmp.excl_time.max, 1, MPI_DOUBLE,        MPI_COMM_WORLD );
                    MPI_Unpack( buffer, sizes[PACK_TOTAL_SIZE], &position, &tmp.excl_time.sum, 1, MPI_DOUBLE,        MPI_COMM_WORLD );
                    MPI_Unpack( buffer, sizes[PACK_TOTAL_SIZE], &position, &tmp.excl_time.cnt, 1, MPI_LONG_LONG_INT, MPI_COMM_WORLD );

                    MPI_Unpack( buffer, sizes[PACK_TOTAL_SIZE], &position, &tmp.incl_time.min, 1, MPI_DOUBLE,        MPI_COMM_WORLD );
                    MPI_Unpack( buffer, sizes[PACK_TOTAL_SIZE], &position, &tmp.incl_time.max, 1, MPI_DOUBLE,        MPI_COMM_WORLD );
                    MPI_Unpack( buffer, sizes[PACK_TOTAL_SIZE], &position, &tmp.incl_time.sum, 1, MPI_DOUBLE,        MPI_COMM_WORLD );
                    MPI_Unpack( buffer, sizes[PACK_TOTAL_SIZE], &position, &tmp.incl_time.cnt, 1, MPI_LONG_LONG_INT, MPI_COMM_WORLD );

                    MPI_Unpack( buffer, sizes[PACK_TOTAL_SIZE], &position, callpath, len, MPI_CHAR, MPI_COMM_WORLD );
                    tmp.callpath = callpath;
                    tmp.callpath = tmp.callpath.substr (0,len);
                    alldata.functionDurationSectionCallpathMapGlobal[ TripleCallpath( func, tmp.callpath,bin ) ].add( tmp );

                }

                fence= 0;
                MPI_Unpack( buffer, sizes[PACK_TOTAL_SIZE], &position, &fence, 1, MPI_LONG_LONG_INT, MPI_COMM_WORLD );
                assert( FENCE == fence );
        }
    }

    if ( alldata.params.clustering.enabled ) {

        /* unpack functionMapPerRank */
        for ( uint32_t i= 0; i < sizes[PACK_FUNCTION_RANK]; i++ ) {

            uint64_t a;
            uint64_t b;
            FunctionData tmp;

            MPI_Unpack( buffer, sizes[PACK_TOTAL_SIZE], &position,  (void*) &a,        1, MPI_LONG_LONG_INT, MPI_COMM_WORLD );
            MPI_Unpack( buffer, sizes[PACK_TOTAL_SIZE], &position,  (void*) &b,        1, MPI_LONG_LONG_INT, MPI_COMM_WORLD );

            MPI_Unpack( buffer, sizes[PACK_TOTAL_SIZE], &position, &tmp.count.min,     1, MPI_LONG_LONG_INT, MPI_COMM_WORLD );
            MPI_Unpack( buffer, sizes[PACK_TOTAL_SIZE], &position, &tmp.count.max,     1, MPI_LONG_LONG_INT, MPI_COMM_WORLD );
            MPI_Unpack( buffer, sizes[PACK_TOTAL_SIZE], &position, &tmp.count.sum,     1, MPI_LONG_LONG_INT, MPI_COMM_WORLD );
            MPI_Unpack( buffer, sizes[PACK_TOTAL_SIZE], &position, &tmp.count.cnt,     1, MPI_LONG_LONG_INT, MPI_COMM_WORLD );

            MPI_Unpack( buffer, sizes[PACK_TOTAL_SIZE], &position, &tmp.excl_time.min, 1, MPI_DOUBLE,        MPI_COMM_WORLD );
            MPI_Unpack( buffer, sizes[PACK_TOTAL_SIZE], &position, &tmp.excl_time.max, 1, MPI_DOUBLE,        MPI_COMM_WORLD );
            MPI_Unpack( buffer, sizes[PACK_TOTAL_SIZE], &position, &tmp.excl_time.sum, 1, MPI_DOUBLE,        MPI_COMM_WORLD );
            MPI_Unpack( buffer, sizes[PACK_TOTAL_SIZE], &position, &tmp.excl_time.cnt, 1, MPI_LONG_LONG_INT, MPI_COMM_WORLD );

            MPI_Unpack( buffer, sizes[PACK_TOTAL_SIZE], &position, &tmp.incl_time.min, 1, MPI_DOUBLE,        MPI_COMM_WORLD );
            MPI_Unpack( buffer, sizes[PACK_TOTAL_SIZE], &position, &tmp.incl_time.max, 1, MPI_DOUBLE,        MPI_COMM_WORLD );
            MPI_Unpack( buffer, sizes[PACK_TOTAL_SIZE], &position, &tmp.incl_time.sum, 1, MPI_DOUBLE,        MPI_COMM_WORLD );
            MPI_Unpack( buffer, sizes[PACK_TOTAL_SIZE], &position, &tmp.incl_time.cnt, 1, MPI_LONG_LONG_INT, MPI_COMM_WORLD );

            alldata.functionMapPerRank[ Pair(a,b) ].add( tmp );
        }

        /* extra check that doesn't cost too much */
        fence= 0;
        MPI_Unpack( buffer, sizes[PACK_TOTAL_SIZE], &position, &fence, 1, MPI_LONG_LONG_INT, MPI_COMM_WORLD );
        assert( FENCE == fence );
    }

    if ( alldata.params.dispersion.enabled) {
        
        /* unpack functionMinMaxLocationMap */
        for ( uint32_t i= 0;  i < sizes[PACK_FUNCTION_MIN_MAX]; i++) {
            
            uint64_t a;
            FunctionMinMaxLocationData tmp;
            
            MPI_Unpack( buffer, sizes[PACK_TOTAL_SIZE], &position, (void*) &a,                     1, MPI_LONG_LONG_INT, MPI_COMM_WORLD );
            MPI_Unpack( buffer, sizes[PACK_TOTAL_SIZE], &position, (void*) &tmp.location.min,      1, MPI_LONG_LONG_INT, MPI_COMM_WORLD );
            MPI_Unpack( buffer, sizes[PACK_TOTAL_SIZE], &position, (void*) &tmp.location.max,      1, MPI_LONG_LONG_INT, MPI_COMM_WORLD );
            MPI_Unpack( buffer, sizes[PACK_TOTAL_SIZE], &position, (void*) &tmp.location.loc_min,  1, MPI_LONG_LONG_INT, MPI_COMM_WORLD );
            MPI_Unpack( buffer, sizes[PACK_TOTAL_SIZE], &position, (void*) &tmp.location.loc_max,  1, MPI_LONG_LONG_INT, MPI_COMM_WORLD );
            MPI_Unpack( buffer, sizes[PACK_TOTAL_SIZE], &position, (void*) &tmp.location.time_min, 1, MPI_LONG_LONG_INT, MPI_COMM_WORLD );
            MPI_Unpack( buffer, sizes[PACK_TOTAL_SIZE], &position, (void*) &tmp.location.time_max, 1, MPI_LONG_LONG_INT, MPI_COMM_WORLD );
            
            alldata.functionMinMaxLocationMap[ a ].add(tmp);
        }
        
        /* extra check that doesn't cost too much */
        fence= 0;
        MPI_Unpack( buffer, sizes[PACK_TOTAL_SIZE], &position, &fence, 1, MPI_LONG_LONG_INT, MPI_COMM_WORLD );
        assert( FENCE == fence );      
    }

    
    if ( alldata.params.write_csv_msg_sizes ) {

       /* unpack messageMapPerSize */
       for ( uint32_t i = 0; i < sizes[PACK_MSG_SIZE]; i++ ){
          uint64_t size, count;
          min_max_avg<uint64_t> time;
          MPI_Unpack( buffer, sizes[PACK_TOTAL_SIZE], &position, (void*) &size,       1, MPI_LONG_LONG_INT, MPI_COMM_WORLD );
          MPI_Unpack( buffer, sizes[PACK_TOTAL_SIZE], &position, (void*) &count,      1, MPI_LONG_LONG_INT, MPI_COMM_WORLD );
          MPI_Unpack( buffer, sizes[PACK_TOTAL_SIZE], &position, (void*) &time.min,   1, MPI_LONG_LONG_INT, MPI_COMM_WORLD );
          MPI_Unpack( buffer, sizes[PACK_TOTAL_SIZE], &position, (void*) &time.max,   1, MPI_LONG_LONG_INT, MPI_COMM_WORLD );
          MPI_Unpack( buffer, sizes[PACK_TOTAL_SIZE], &position, (void*) &time.sum,   1, MPI_LONG_LONG_INT, MPI_COMM_WORLD );
          MPI_Unpack( buffer, sizes[PACK_TOTAL_SIZE], &position, (void*) &time.cnt, 1, MPI_LONG_LONG_INT, MPI_COMM_WORLD );

          alldata.messageMapPerSize[size].count += count;
          alldata.messageMapPerSize[size].time.add(time);
       }

        /* extra check that doesn't cost too much */
        fence= 0;
        MPI_Unpack( buffer, sizes[PACK_TOTAL_SIZE], &position, &fence, 1, MPI_LONG_LONG_INT, MPI_COMM_WORLD );
        assert( FENCE == fence );      

       
    }

        /* free the callpath chararray */
        delete callpath;
}


bool ReduceData( AllData& alldata ) {

    bool error= false;

    assert( 1 < alldata.numRanks );

    /* start runtime measurement for reducing data */
    StartMeasurement( alldata, 1, true, "reduce data" );

    VerbosePrint( alldata, 1, true, "reducing data\n" );

    /* implement reduction myself because MPI and C++ STL don't play with
    each other */

    /* how many rounds until master has all the data? */
    uint32_t num_rounds= Logi( alldata.numRanks ) -1;
    uint32_t round_no= 0;
    uint32_t round= 1;
    while ( round < alldata.numRanks ) {

        round_no++;

            VerbosePrint( alldata, 1, true, " round %u / %u\n",
                              round_no, num_rounds );

        uint32_t peer= alldata.myRank ^ round;

        /* if peer rank is not there, do nothing but go on */
        if ( peer >= alldata.numRanks ) {

            round= round << 1;
            continue;
        }

        /* send to smaller peer, receive from larger one */
        uint32_t sizes[PACK_NUM_PACKS];
        char* buffer;

        if ( alldata.myRank < peer ) {

            MPI_Status status;

            MPI_Recv( sizes, PACK_NUM_PACKS, MPI_UNSIGNED, peer, 4, MPI_COMM_WORLD,
                      &status );

            // DEBUG
            //cout << "    round " << round << " recv " << peer << "--> " <<
            //alldata.myRank << " with " <<
            //sizes[PACK_TOTAL_SIZE] << " bytes, " <<
            //sizes[PACK_FUNCTION] << ", " <<
            //sizes[PACK_FUNCTION_DURATION] << ", " <<
            //sizes[PACK_COUNTER] << ", " <<
            //sizes[PACK_MSG_GROUP_PAIR] << "" << endl << flush;

            buffer= prepare_worker_data( alldata, sizes );

            VerbosePrint( alldata, 2, false,
                          "round %u / %u: receiving %u bytes from rank %u\n",
                          round_no, num_rounds, sizes[PACK_TOTAL_SIZE], peer );

            MPI_Recv( buffer, sizes[PACK_TOTAL_SIZE], MPI_PACKED, peer, 5, MPI_COMM_WORLD,
                      &status );

            unpack_worker_data( alldata, sizes );

        } else {

            buffer= pack_worker_data( alldata, sizes );

            // DEBUG
            //cout << "    round " << round << " send " << alldata.myRank <<
            //" --> " << peer << " with " <<
            //sizes[PACK_TOTAL_SIZE] << " bytes, " <<
            //sizes[PACK_FUNCTION] << ", " <<
            //sizes[PACK_FUNCTION_DURATION] << ", " <<
            //sizes[PACK_COUNTER] << ", " <<
            //sizes[PACK_MSG_GROUP_PAIR] << "" << endl << flush;

            VerbosePrint( alldata, 2, false,
                          "round %u / %u: sending %u bytes to rank %u\n",
                          round_no, num_rounds, sizes[PACK_TOTAL_SIZE], peer );

            MPI_Send( sizes, PACK_NUM_PACKS, MPI_UNSIGNED, peer, 4, MPI_COMM_WORLD );

            MPI_Send( buffer, sizes[PACK_TOTAL_SIZE], MPI_PACKED, peer, 5,
                      MPI_COMM_WORLD );

            /* every work has to send off its data at most once,
            after that, break from the collective reduction operation */
            break;
        }

        round= round << 1;
    }

    alldata.freePackBuffer();

    /* clear MessagePerSize except for root to prevent sending it again */
    if ( alldata.params.write_csv_msg_sizes && alldata.myRank != 0 )
    {
       alldata.messageMapPerSize.clear();
    }

    /* synchronize error indicator with workers */
    /*SyncError( alldata, error );*/

    if ( !error ) {

        /* stop runtime measurement for reducing data */
        StopMeasurement( alldata, true, "reduce data" );
    }

    return !error;
}


bool ReduceDataDispersion( AllData& alldata ) {
    
    bool error= false;

    assert( 1 < alldata.numRanks );
    
    /* start runtime measurement for reducing data */
    StartMeasurement( alldata, 1, true, "reduce data dispersion" );
    
    VerbosePrint( alldata, 1, true, "reducing data dispersion\n" );


    /* implement reduction myself because MPI and C++ STL don't play with
     each other */
    
    /* how many rounds until master has all the data? */
    uint32_t num_rounds= Logi( alldata.numRanks ) -1;
    uint32_t round_no= 0;
    uint32_t round= 1;

    while ( round < alldata.numRanks ) {
        
        round_no++;
        
        if ( 1 == alldata.params.verbose_level ) {
            
            VerbosePrint( alldata, 1, true, " round %u / %u\n",
                         round_no, num_rounds );
        }
        
        uint32_t peer= alldata.myRank ^ round;
        
        /* if peer rank is not there, do nothing but go on */
        if ( peer >= alldata.numRanks ) {
            
            round= round << 1;
            continue;
        }
        
        /* send to smaller peer, receive from larger one */
        uint32_t sizes[PACK_NUM_PACKS];
        char* buffer;
        
        if ( alldata.myRank < peer ) {
            
            MPI_Status status;
            
            MPI_Recv( sizes, PACK_NUM_PACKS, MPI_UNSIGNED, peer, 4, MPI_COMM_WORLD,
                     &status );
            
            // DEBUG
      /*      cout << "    round " << round << " recv " << peer << "--> " <<
            alldata.myRank << " with " <<
            sizes[PACK_TOTAL_SIZE] << " bytes, " <<
            sizes[PACK_FUNCTION] << ", " <<
            sizes[PACK_FUNCTION_DURATION] << ", " <<
            sizes[PACK_COUNTER] << ", " <<
            sizes[PACK_MSG_GROUP_PAIR] << "" << endl << flush;
        */
            buffer= prepare_worker_data( alldata, sizes );
            
            VerbosePrint( alldata, 2, false,
                         "round %u / %u: receiving %u bytes from rank %u\n",
                         round_no, num_rounds, sizes[PACK_TOTAL_SIZE], peer );
            
            MPI_Recv( buffer, sizes[PACK_TOTAL_SIZE], MPI_PACKED, peer, 5, MPI_COMM_WORLD,
                     &status );
            
            unpack_worker_data( alldata, sizes );
            
        } else {
            
            /* don't reduce function map global twice */ 
            alldata.functionMapGlobal.clear();
            if(alldata.params.dispersion.mode == DISPERSION_MODE_PERCALLPATH)
                alldata.functionCallpathMapGlobal.clear();
            
            buffer= pack_worker_data( alldata, sizes );
            
            // DEBUG
            //cout << "    round " << round << " send " << alldata.myRank <<
            //" --> " << peer << " with " <<
            //sizes[PACK_TOTAL_SIZE] << " bytes, " <<
            //sizes[PACK_FUNCTION] << ", " <<
            //sizes[PACK_FUNCTION_DURATION] << ", " <<
            //sizes[PACK_COUNTER] << ", " <<
            //sizes[PACK_MSG_GROUP_PAIR] << "" << endl << flush;
            
            VerbosePrint( alldata, 2, false,
                         "round %u / %u: sending %u bytes to rank %u\n",
                         round_no, num_rounds, sizes[PACK_TOTAL_SIZE], peer );
            
            MPI_Send( sizes, PACK_NUM_PACKS, MPI_UNSIGNED, peer, 4, MPI_COMM_WORLD );
            
            MPI_Send( buffer, sizes[PACK_TOTAL_SIZE], MPI_PACKED, peer, 5,
                     MPI_COMM_WORLD );
            
            /* every work has to send off its data at most once,
             after that, break from the collective reduction operation */
            break;
        }
        
        round= round << 1;
    }
    alldata.freePackBuffer();
    
    /* synchronize error indicator with workers */
    /*SyncError( alldata, error );*/
    
    if ( !error ) {
        
        /* stop runtime measurement for reducing data */
        StopMeasurement( alldata, true, "reduce data dispersion" );
    }
    
    return !error;
}
