Execution policy (multithreading)

This commit is contained in:
Jakub Melka
2020-01-18 14:55:26 +01:00
parent db493db3cc
commit 1fd01c14fd
14 changed files with 266 additions and 34 deletions

View File

@@ -22,6 +22,7 @@
#include "pdfutils.h"
#include "pdfpattern.h"
#include "pdfcms.h"
#include "pdfexecutionpolicy.h"
#include <QCryptographicHash>
@@ -222,7 +223,7 @@ QImage PDFAbstractColorSpace::getImage(const PDFImageData& imageData,
};
auto range = PDFIntegerRange<unsigned int>(0, imageHeight);
std::for_each(std::execution::parallel_policy(), range.begin(), range.end(), transformPixelLine);
PDFExecutionPolicy::execute(PDFExecutionPolicy::Scope::Content, range.begin(), range.end(), transformPixelLine);
if (exception)
{

View File

@@ -19,6 +19,7 @@
#include "pdfcms.h"
#include "pdfdrawspacecontroller.h"
#include "pdfprogress.h"
#include "pdfexecutionpolicy.h"
#include <QtConcurrent/QtConcurrent>
@@ -411,7 +412,7 @@ void PDFAsynchronousTextLayoutCompiler::makeTextLayout()
};
auto pageRange = PDFIntegerRange<PDFInteger>(0, catalog->getPageCount());
std::for_each(std::execution::parallel_policy(), pageRange.begin(), pageRange.end(), generateTextLayout);
PDFExecutionPolicy::execute(PDFExecutionPolicy::Scope::Page, pageRange.begin(), pageRange.end(), generateTextLayout);
return result;
};

View File

@@ -22,6 +22,7 @@
#include "pdfexception.h"
#include "pdfparser.h"
#include "pdfstreamfilters.h"
#include "pdfexecutionpolicy.h"
#include <QFile>
@@ -270,7 +271,7 @@ PDFDocument PDFDocumentReader::readFromBuffer(const QByteArray& buffer)
// Now, we are ready to scan all objects
progressStart(occupiedEntries.size(), PDFTranslationContext::tr("Reading contents of document..."));
std::for_each(std::execution::parallel_policy(), occupiedEntries.cbegin(), occupiedEntries.cend(), processEntry);
PDFExecutionPolicy::execute(PDFExecutionPolicy::Scope::Unknown, occupiedEntries.cbegin(), occupiedEntries.cend(), processEntry);
progressFinish();
if (m_result != Result::OK)
@@ -370,7 +371,7 @@ PDFDocument PDFDocumentReader::readFromBuffer(const QByteArray& buffer)
};
progressStart(occupiedEntries.size(), PDFTranslationContext::tr("Decrypting encrypted contents of document..."));
std::for_each(std::execution::parallel_policy(), occupiedEntries.cbegin(), occupiedEntries.cend(), decryptEntry);
PDFExecutionPolicy::execute(PDFExecutionPolicy::Scope::Unknown, occupiedEntries.cbegin(), occupiedEntries.cend(), decryptEntry);
progressFinish();
}
@@ -478,7 +479,7 @@ PDFDocument PDFDocumentReader::readFromBuffer(const QByteArray& buffer)
};
// Now, we are ready to scan all object streams
std::for_each(std::execution::parallel_policy(), objectStreams.cbegin(), objectStreams.cend(), processObjectStream);
PDFExecutionPolicy::execute(PDFExecutionPolicy::Scope::Unknown, objectStreams.cbegin(), objectStreams.cend(), processObjectStream);
PDFObjectStorage storage(std::move(objects), PDFObject(xrefTable.getTrailerDictionary()), std::move(securityHandler));
return PDFDocument(std::move(storage), m_version);

View File

@@ -0,0 +1,92 @@
// Copyright (C) 2020 Jakub Melka
//
// This file is part of PdfForQt.
//
// PdfForQt is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// PdfForQt is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with PDFForQt. If not, see <https://www.gnu.org/licenses/>.
#include "pdfexecutionpolicy.h"
#include <QThread>
namespace pdf
{
struct PDFExecutionPolicyHolder
{
PDFExecutionPolicy policy;
} s_execution_policy;
void PDFExecutionPolicy::setStrategy(Strategy strategy)
{
s_execution_policy.policy.m_strategy.store(strategy, std::memory_order_relaxed);
}
bool PDFExecutionPolicy::isParallelizing(Scope scope)
{
const Strategy strategy = s_execution_policy.policy.m_strategy.load(std::memory_order_relaxed);
switch (strategy)
{
case Strategy::SingleThreaded:
return false;
case Strategy::PageMultithreaded:
{
switch (scope)
{
case Scope::Page:
case Scope::Unknown:
return true; // We are parallelizing pages...
case Scope::Content:
{
// Jakub Melka: this is a bit complicated. We must count number of content streams
// being processed and if it is large enough, then do not parallelize.
const size_t threadLimit = s_execution_policy.policy.m_threadLimit.load(std::memory_order_relaxed);
const size_t contentStreamsCount = s_execution_policy.policy.m_contentStreamsCount.load(std::memory_order_seq_cst);
return contentStreamsCount < threadLimit;
}
}
break;
}
case Strategy::AlwaysMultithreaded:
return true;
}
// It should never go here...
Q_ASSERT(false);
return false;
}
void PDFExecutionPolicy::startProcessingContentStream()
{
++s_execution_policy.policy.m_contentStreamsCount;
}
void PDFExecutionPolicy::endProcessingContentStream()
{
--s_execution_policy.policy.m_contentStreamsCount;
}
PDFExecutionPolicy::PDFExecutionPolicy() :
m_contentStreamsCount(0),
m_threadLimit(QThread::idealThreadCount()),
m_strategy(Strategy::PageMultithreaded)
{
}
} // namespace pdf

View File

@@ -0,0 +1,103 @@
// Copyright (C) 2020 Jakub Melka
//
// This file is part of PdfForQt.
//
// PdfForQt is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// PdfForQt is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with PDFForQt. If not, see <https://www.gnu.org/licenses/>.
#ifndef PDFEXECUTIONPOLICY_H
#define PDFEXECUTIONPOLICY_H
#include "pdfglobal.h"
#include <atomic>
#include <execution>
namespace pdf
{
struct PDFExecutionPolicyHolder;
/// Defines thread execution policy based on settings and actual number of page content
/// streams being processed. It can regulate number of threads executed at each
/// point, where execution policy is used.
class PDFFORQTLIBSHARED_EXPORT PDFExecutionPolicy
{
public:
enum class Scope
{
Page, ///< Used, when we are processing page objects
Content, ///< Used, when we are processing objects from page content streams
Unknown ///< Unknown scope, usually tries to parallelize
};
enum class Strategy
{
SingleThreaded,
PageMultithreaded,
AlwaysMultithreaded
};
/// Sets multithreading strategy
/// \param strategy Strategy
static void setStrategy(Strategy strategy);
/// Determines, if we should parallelize for scope
/// \param scope Scope for which we want to determine exectution policy
static bool isParallelizing(Scope scope);
template<typename ForwardIt, typename UnaryFunction>
static void execute(Scope scope, ForwardIt first, ForwardIt last, UnaryFunction f)
{
if (isParallelizing(scope))
{
std::for_each(std::execution::parallel_policy(), first, last, f);
}
else
{
std::for_each(std::execution::sequenced_policy(), first, last, f);
}
}
template<typename ForwardIt, typename Comparator>
static void sort(Scope scope, ForwardIt first, ForwardIt last, Comparator f)
{
if (isParallelizing(scope))
{
std::sort(std::execution::parallel_policy(), first, last, f);
}
else
{
std::sort(std::execution::sequenced_policy(), first, last, f);
}
}
/// Starts processing content stream
static void startProcessingContentStream();
/// Ends processing content stream
static void endProcessingContentStream();
private:
friend struct PDFExecutionPolicyHolder;
explicit PDFExecutionPolicy();
std::atomic<size_t> m_contentStreamsCount;
std::atomic<size_t> m_threadLimit;
std::atomic<Strategy> m_strategy;
};
} // namespace pdf
#endif // PDFEXECUTIONPOLICY_H

View File

@@ -20,6 +20,7 @@
#include "pdfexception.h"
#include "pdfimage.h"
#include "pdfpattern.h"
#include "pdfexecutionpolicy.h"
#include <QPainterPathStroker>
@@ -209,6 +210,8 @@ PDFPageContentProcessor::PDFPageContentProcessor(const PDFPage* page,
Q_ASSERT(page);
Q_ASSERT(document);
PDFExecutionPolicy::startProcessingContentStream();
QPainterPath pageRectPath;
pageRectPath.addRect(m_page->getRotatedMediaBox());
m_pageBoundingRectDeviceSpace = pagePointToDevicePointMatrix.map(pageRectPath).boundingRect();
@@ -218,7 +221,7 @@ PDFPageContentProcessor::PDFPageContentProcessor(const PDFPage* page,
PDFPageContentProcessor::~PDFPageContentProcessor()
{
PDFExecutionPolicy::endProcessingContentStream();
}
QList<PDFRenderError> PDFPageContentProcessor::processContents()

View File

@@ -20,6 +20,7 @@
#include "pdfexception.h"
#include "pdfutils.h"
#include "pdfcolorspaces.h"
#include "pdfexecutionpolicy.h"
#include <QPainter>
@@ -618,7 +619,7 @@ PDFMesh PDFFunctionShading::createMesh(const PDFMeshQualitySettings& settings, c
}
};
std::for_each(std::execution::parallel_policy(), indices.cbegin(), indices.cend(), setColor);
PDFExecutionPolicy::execute(PDFExecutionPolicy::Scope::Content, indices.cbegin(), indices.cend(), setColor);
if (!functionError)
{
@@ -664,7 +665,7 @@ PDFMesh PDFFunctionShading::createMesh(const PDFMeshQualitySettings& settings, c
}
}
};
std::for_each(std::execution::parallel_policy(), indices.cbegin(), indices.cend(), validateMesh);
PDFExecutionPolicy::execute(PDFExecutionPolicy::Scope::Content, indices.cbegin(), indices.cend(), validateMesh);
if (!isMeshOK && resolution != settings.minimalMeshResolution)
{
continue;
@@ -750,7 +751,7 @@ PDFMesh PDFFunctionShading::createMesh(const PDFMeshQualitySettings& settings, c
triangles[triangleIndex1] = triangle1;
triangles[triangleIndex2] = triangle2;
};
std::for_each(std::execution::parallel_policy(), indices.cbegin(), indices.cend(), generateTriangle);
PDFExecutionPolicy::execute(PDFExecutionPolicy::Scope::Content, indices.cbegin(), indices.cend(), generateTriangle);
mesh.setTriangles(qMove(triangles));
if (!functionError)
@@ -1475,7 +1476,7 @@ PDFMesh PDFFreeFormGouradTriangleShading::createMesh(const PDFMeshQualitySetting
};
PDFIntegerRange indices(size_t(0), vertexCount);
std::for_each(std::execution::parallel_policy(), indices.begin(), indices.end(), readVertex);
PDFExecutionPolicy::execute(PDFExecutionPolicy::Scope::Content, indices.begin(), indices.end(), readVertex);
mesh.setVertices(qMove(meshVertices));
vertices.front().flags = 0;
@@ -1624,7 +1625,7 @@ PDFMesh PDFLatticeFormGouradTriangleShading::createMesh(const PDFMeshQualitySett
};
PDFIntegerRange indices(size_t(0), vertexCount);
std::for_each(std::execution::parallel_policy(), indices.begin(), indices.end(), readVertex);
PDFExecutionPolicy::execute(PDFExecutionPolicy::Scope::Content, indices.begin(), indices.end(), readVertex);
mesh.setVertices(qMove(meshVertices));
auto getVertexIndex = [columnCount](size_t row, size_t column) -> size_t
@@ -2253,7 +2254,7 @@ void PDFTensorProductPatchShadingBase::fillMesh(PDFMesh& mesh,
PDFReal previousCurvature = maximalCurvature;
while (previousCurvature < curvature && !maximalCurvature.compare_exchange_weak(previousCurvature, curvature)) { }
};
std::for_each(std::execution::parallel_policy(), range.begin(), range.end(), updateCurvature);
PDFExecutionPolicy::execute(PDFExecutionPolicy::Scope::Content, range.begin(), range.end(), updateCurvature);
auto getColorForUV = [&](PDFReal u, PDFReal v)
{
@@ -2364,7 +2365,7 @@ void PDFTensorProductPatchShadingBase::fillMesh(PDFMesh& mesh,
QPointF rightCenter = right.getCenter();
return std::pair(leftCenter.y(), leftCenter.x()) < std::pair(rightCenter.y(), rightCenter.x());
};
std::sort(std::execution::parallel_policy(), finishedTriangles.begin(), finishedTriangles.end(), comparator);
PDFExecutionPolicy::sort(PDFExecutionPolicy::Scope::Content, finishedTriangles.begin(), finishedTriangles.end(), comparator);
std::vector<QPointF> vertices;
std::vector<PDFMesh::Triangle> triangles;

View File

@@ -17,6 +17,7 @@
#include "pdftextlayout.h"
#include "pdfutils.h"
#include "pdfexecutionpolicy.h"
#include <QPainter>
@@ -456,7 +457,7 @@ void PDFTextLayout::performDoLayout(PDFReal angle)
};
auto range = PDFIntegerRange<size_t>(0, characterCount);
std::for_each(std::execution::parallel_policy(), range.begin(), range.end(), findNearestCharacters);
PDFExecutionPolicy::execute(PDFExecutionPolicy::Scope::Content, range.begin(), range.end(), findNearestCharacters);
// Step 3) - detect lines
PDFUnionFindAlgorithm<size_t> textLinesUF(characterCount);
@@ -847,7 +848,7 @@ PDFFindResults PDFTextLayoutStorage::find(const QString& text, Qt::CaseSensitivi
};
auto range = PDFIntegerRange<size_t>(0, m_offsets.size());
std::for_each(std::execution::parallel_policy(), range.begin(), range.end(), findImpl);
PDFExecutionPolicy::execute(PDFExecutionPolicy::Scope::Page, range.begin(), range.end(), findImpl);
std::sort(results.begin(), results.end());
return results;
@@ -876,7 +877,7 @@ PDFFindResults PDFTextLayoutStorage::find(const QRegularExpression& expression,
};
auto range = PDFIntegerRange<size_t>(0, m_offsets.size());
std::for_each(std::execution::parallel_policy(), range.begin(), range.end(), findImpl);
PDFExecutionPolicy::execute(PDFExecutionPolicy::Scope::Page, range.begin(), range.end(), findImpl);
std::sort(results.begin(), results.end());
return results;