mirror of
https://github.com/JakubMelka/PDF4QT.git
synced 2025-06-05 21:59:17 +02:00
Execution policy (multithreading)
This commit is contained in:
@@ -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)
|
||||
{
|
||||
|
@@ -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;
|
||||
};
|
||||
|
||||
|
@@ -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);
|
||||
|
92
PdfForQtLib/sources/pdfexecutionpolicy.cpp
Normal file
92
PdfForQtLib/sources/pdfexecutionpolicy.cpp
Normal 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
|
103
PdfForQtLib/sources/pdfexecutionpolicy.h
Normal file
103
PdfForQtLib/sources/pdfexecutionpolicy.h
Normal 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
|
@@ -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()
|
||||
|
@@ -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;
|
||||
|
@@ -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;
|
||||
|
Reference in New Issue
Block a user