17.2.3.  Concurrent Map/Reduce Example

[ fromfile: threads.xml id: parallellife ]

Example 17.23. src/threads/life/lifeslice.h

[ . . . . ]
struct LifeSlice {
    LifeSlice() {};
    LifeSlice(QRect r, QImage i) : rect(r), image(i) {}
    QRect rect;
    QImage image;
};
[ . . . . ]

Example 17.24. src/threads/life/lifemainwindow.cpp

[ . . . . ]
struct LifeFunctor : public std::unary_function<LifeSlice, LifeSlice> {
    LifeSlice operator() (LifeSlice slice);
};

LifeSlice LifeFunctor::operator()(LifeSlice slice) {    1
    QRect rect = slice.rect;
    QImage image = slice.image;
    QImage next = QImage(rect.size(), QImage::Format_Mono);
    next.fill(DEAD);
    int h = rect.height();  int w = rect.width();

    for (int c=0; c<w; ++c) {
        for (int r=0; r<h; ++r) {
            int x = c+rect.x();
            int y = r+rect.y();
            bool isAlive = (image.pixelIndex(x, y) == ALIVE);
            int nc = neighborCount(image, x, y);
            if (!isAlive && nc == 3)
                 next.setPixel(c, r, ALIVE);
            if (!isAlive) continue;
            if (nc == 2 || nc == 3)
                next.setPixel(c,r, ALIVE);
        }
    }
    slice.image = next;
    return slice;
}

1

Mapping function


Example 17.25. src/threads/life/lifemainwindow.cpp

[ . . . . ]
void stitchReduce(QImage& next, const LifeSlice &slice) {
    if (next.isNull()) 
        next = QImage(boardSize, QImage::Format_Mono);
    QPainter painter(&next);
    painter.drawImage(slice.rect.topLeft(), slice.image);   1
}

1

Draw one piece of an image onto another.


Example 17.26. src/threads/life/lifemainwindow.cpp

[ . . . . ]

void LifeMainWindow::calculate() {
    int w = boardSize.width();
    // This might not be optimal but it seems to work well...    
    int segments = QThreadPool::globalInstance()->maxThreadCount() * 2; 
    int ws = w/segments;                    1
    LifeFunctor functor;                    2
    while (m_running) {
        qApp->processEvents();              3
        m_numGenerations++;
        QList<LifeSlice> slices;            4
        for (int c=0; c<segments; ++c) {
            int tlx = c*ws;
            QRect rect(tlx, 0, ws, boardSize.height());
            LifeSlice slice(rect, m_current);    
            slices << slice;                5
        }
        m_current = QtConcurrent::blockingMappedReduced(slices, functor,
                    stitchReduce, QtConcurrent::UnorderedReduce );  6
        m_lifeWidget->setImage(m_current);
    }
}

1

Width of a segment.

2

Map functor.

3

Ensure GUI is still responsive.

4

Break up into smaller pieces.

5

Add the pieces to a collection to be processed in parallel.

6

Do the work in parallel. stitchReduce can be called on each piece as it is ready.