Commit d9dce0b9 authored by Andrey Kamakin's avatar Andrey Kamakin

Merge branch 'akamakin/T8628-multithreading-optimization'

parents 20ee6156 e4c03b2f
......@@ -169,6 +169,10 @@ option(HIDE_SAFE_ASSERTS "Don't show message box for \"safe\" asserts, just igno
configure_file(config-hide-safe-asserts.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config-hide-safe-asserts.h)
add_feature_info("Safe Asserts" HIDE_SAFE_ASSERTS "Don't show message box for \"safe\" asserts, just ignore them automatically and dump a message to the terminal.")
option(USE_LOCK_FREE_HASH_TABLE "Use lock free hash table instead of blocking." OFF)
configure_file(config-hash-table-implementaion.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config-hash-table-implementaion.h)
add_feature_info("Lock free hash table" USE_LOCK_FREE_HASH_TABLE "Use lock free hash table instead of blocking.")
option(FOUNDATION_BUILD "A Foundation build is a binary release build that can package some extra things like color themes. Linux distributions that build and install Krita into a default system location should not define this option to true." OFF)
add_feature_info("Foundation Build" FOUNDATION_BUILD "A Foundation build is a binary release build that can package some extra things like color themes. Linux distributions that build and install Krita into a default system location should not define this option to true.")
......
/* config-hash-table-implementation.h. Generated by cmake from config-hash-table-implementation.h.cmake */
#cmakedefine USE_LOCK_FREE_HASH_TABLE 1
......@@ -187,7 +187,7 @@ public:
inline bool isNull() const {
return (d == 0);
}
private:
inline static void ref(const KisSharedPtr<T>* sp, T* t)
{
#ifndef HAVE_MEMORY_LEAK_TRACKER
......@@ -199,10 +199,7 @@ private:
t->ref();
}
}
inline void ref() const
{
ref(this, d);
}
inline static bool deref(const KisSharedPtr<T>* sp, T* t)
{
#ifndef HAVE_MEMORY_LEAK_TRACKER
......@@ -216,6 +213,13 @@ private:
}
return true;
}
private:
inline void ref() const
{
ref(this, d);
}
inline void deref() const
{
bool v = deref(this, d);
......@@ -227,6 +231,7 @@ private:
Q_UNUSED(v);
#endif
}
private:
mutable T* d;
};
......
/*------------------------------------------------------------------------
Junction: Concurrent data structures in C++
Copyright (c) 2016 Jeff Preshing
Distributed under the Simplified BSD License.
Original location: https://github.com/preshing/junction
This software is distributed WITHOUT ANY WARRANTY; without even the
implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the LICENSE file for more information.
------------------------------------------------------------------------*/
#ifndef ATOMIC_H
#define ATOMIC_H
#include <atomic>
inline void signalFenceConsume()
{
std::atomic_signal_fence(std::memory_order_acquire);
}
inline void signalFenceAcquire()
{
std::atomic_signal_fence(std::memory_order_acquire);
}
inline void signalFenceRelease()
{
std::atomic_signal_fence(std::memory_order_release);
}
inline void signalFenceSeqCst()
{
std::atomic_signal_fence(std::memory_order_seq_cst);
}
inline void threadFenceConsume()
{
std::atomic_thread_fence(std::memory_order_acquire);
}
inline void threadFenceAcquire()
{
std::atomic_thread_fence(std::memory_order_acquire);
}
inline void threadFenceRelease()
{
std::atomic_thread_fence(std::memory_order_release);
}
inline void threadFenceSeqCst()
{
std::atomic_thread_fence(std::memory_order_seq_cst);
}
enum MemoryOrder {
Relaxed = std::memory_order_relaxed,
Consume = std::memory_order_consume,
Acquire = std::memory_order_acquire,
Release = std::memory_order_release,
ConsumeRelease = std::memory_order_acq_rel,
AcquireRelease = std::memory_order_acq_rel,
};
template <typename T>
class Atomic : protected std::atomic<T>
{
private:
T operator=(T value);
public:
Atomic()
{
}
Atomic(T value) : std::atomic<T>(value)
{
}
Atomic(const Atomic& other) : std::atomic<T>(other.loadNonatomic())
{
}
T loadNonatomic() const
{
return std::atomic<T>::load(std::memory_order_relaxed);
}
T load(MemoryOrder memoryOrder) const
{
return std::atomic<T>::load((std::memory_order) memoryOrder);
}
void storeNonatomic(T value)
{
return std::atomic<T>::store(value, std::memory_order_relaxed);
}
void store(T value, MemoryOrder memoryOrder)
{
return std::atomic<T>::store(value, (std::memory_order) memoryOrder);
}
T compareExchange(T expected, T desired, MemoryOrder memoryOrder)
{
std::atomic<T>::compare_exchange_strong(expected, desired, (std::memory_order) memoryOrder);
return expected; // modified by reference by compare_exchange_strong
}
bool compareExchangeStrong(T& expected, T desired, MemoryOrder memoryOrder)
{
return std::atomic<T>::compare_exchange_strong(expected, desired, (std::memory_order) memoryOrder);
}
bool compareExchangeWeak(T& expected, T desired, MemoryOrder success, MemoryOrder failure)
{
return std::atomic<T>::compare_exchange_weak(expected, desired, (std::memory_order) success, (std::memory_order) failure);
}
T exchange(T desired, MemoryOrder memoryOrder)
{
return std::atomic<T>::exchange(desired, (std::memory_order) memoryOrder);
}
T fetchAdd(T operand, MemoryOrder memoryOrder)
{
return std::atomic<T>::fetch_add(operand, (std::memory_order) memoryOrder);
}
T fetchSub(T operand, MemoryOrder memoryOrder)
{
return std::atomic<T>::fetch_sub(operand, (std::memory_order) memoryOrder);
}
T fetchAnd(T operand, MemoryOrder memoryOrder)
{
return std::atomic<T>::fetch_and(operand, (std::memory_order) memoryOrder);
}
T fetchOr(T operand, MemoryOrder memoryOrder)
{
return std::atomic<T>::fetch_or(operand, (std::memory_order) memoryOrder);
}
};
#endif // ATOMIC_H
This diff is collapsed.
This diff is collapsed.
/*------------------------------------------------------------------------
Junction: Concurrent data structures in C++
Copyright (c) 2016 Jeff Preshing
Distributed under the Simplified BSD License.
Original location: https://github.com/preshing/junction
This software is distributed WITHOUT ANY WARRANTY; without even the
implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the LICENSE file for more information.
------------------------------------------------------------------------*/
#ifndef MAPTRAITS_H
#define MAPTRAITS_H
#include <QtCore>
inline quint64 roundUpPowerOf2(quint64 v)
{
v--;
v |= v >> 1;
v |= v >> 2;
v |= v >> 4;
v |= v >> 8;
v |= v >> 16;
v |= v >> 32;
v++;
return v;
}
inline bool isPowerOf2(quint64 v)
{
return (v & (v - 1)) == 0;
}
inline quint32 avalanche(quint32 h)
{
h ^= h >> 16;
h *= 0x85ebca6b;
h ^= h >> 13;
h *= 0xc2b2ae35;
h ^= h >> 16;
return h;
}
inline quint32 deavalanche(quint32 h)
{
h ^= h >> 16;
h *= 0x7ed1b41d;
h ^= (h ^ (h >> 13)) >> 13;
h *= 0xa5cb9243;
h ^= h >> 16;
return h;
}
template <class T>
struct DefaultKeyTraits {
typedef T Key;
typedef quint32 Hash;
static const Key NullKey = Key(0);
static const Hash NullHash = Hash(0);
static Hash hash(T key)
{
return avalanche(Hash(key));
}
static Key dehash(Hash hash)
{
return (T) deavalanche(hash);
}
};
template <class T>
struct DefaultValueTraits {
typedef T Value;
typedef quint32 IntType;
static const IntType NullValue = 0;
static const IntType Redirect = 1;
};
#endif // MAPTRAITS_H
/*------------------------------------------------------------------------
Junction: Concurrent data structures in C++
Copyright (c) 2016 Jeff Preshing
Distributed under the Simplified BSD License.
Original location: https://github.com/preshing/junction
This software is distributed WITHOUT ANY WARRANTY; without even the
implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the LICENSE file for more information.
------------------------------------------------------------------------*/
#ifndef QSBR_H
#define QSBR_H
#include <QVector>
#include <QMutex>
#include <QMutexLocker>
#define CALL_MEMBER(obj, pmf) ((obj).*(pmf))
class QSBR
{
private:
struct Action {
void (*func)(void*);
quint64 param[4]; // Size limit found experimentally. Verified by assert below.
Action() = default;
Action(void (*f)(void*), void* p, quint64 paramSize) : func(f)
{
Q_ASSERT(paramSize <= sizeof(param)); // Verify size limit.
memcpy(&param, p, paramSize);
}
void operator()()
{
func(&param);
}
};
QMutex m_mutex;
QVector<Action> m_pendingActions;
QVector<Action> m_deferedActions;
std::atomic_flag m_isProcessing = ATOMIC_FLAG_INIT;
public:
template <class T>
void enqueue(void (T::*pmf)(), T* target, bool migration = false)
{
struct Closure {
void (T::*pmf)();
T* target;
static void thunk(void* param)
{
Closure* self = (Closure*) param;
CALL_MEMBER(*self->target, self->pmf)();
}
};
Closure closure = {pmf, target};
while (m_isProcessing.test_and_set(std::memory_order_acquire)) {
}
if (migration) {
m_deferedActions.append(Action(Closure::thunk, &closure, sizeof(closure)));
} else {
m_pendingActions.append(Action(Closure::thunk, &closure, sizeof(closure)));
}
m_isProcessing.clear(std::memory_order_release);
}
void update(bool migration)
{
if (!m_isProcessing.test_and_set(std::memory_order_acquire)) {
QVector<Action> actions;
actions.swap(m_pendingActions);
if (!migration) {
m_pendingActions.swap(m_deferedActions);
}
m_isProcessing.clear(std::memory_order_release);
for (auto &action : actions) {
action();
}
}
}
void flush()
{
if (!m_isProcessing.test_and_set(std::memory_order_acquire)) {
for (auto &action : m_pendingActions) {
action();
}
for (auto &action : m_deferedActions) {
action();
}
m_isProcessing.clear(std::memory_order_release);
}
}
};
#endif // QSBR_H
/*------------------------------------------------------------------------
Junction: Concurrent data structures in C++
Copyright (c) 2016 Jeff Preshing
Distributed under the Simplified BSD License.
Original location: https://github.com/preshing/junction
This software is distributed WITHOUT ANY WARRANTY; without even the
implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the LICENSE file for more information.
------------------------------------------------------------------------*/
#ifndef SIMPLEJOBCOORDINATOR_H
#define SIMPLEJOBCOORDINATOR_H
#include <QMutex>
#include <QWaitCondition>
#include <QMutexLocker>
#include "kis_assert.h"
#include "atomic.h"
#define SANITY_CHECK
class SimpleJobCoordinator
{
public:
struct Job {
virtual ~Job()
{
}
virtual void run() = 0;
};
private:
Atomic<quint64> m_job;
QMutex mutex;
QWaitCondition condVar;
public:
SimpleJobCoordinator() : m_job(quint64(NULL))
{
}
Job* loadConsume() const
{
return (Job*) m_job.load(Consume);
}
void storeRelease(Job* job)
{
{
QMutexLocker guard(&mutex);
m_job.store(quint64(job), Release);
}
condVar.wakeAll();
}
void participate()
{
quint64 prevJob = quint64(NULL);
for (;;) {
quint64 job = m_job.load(Consume);
if (job == prevJob) {
QMutexLocker guard(&mutex);
for (;;) {
job = m_job.loadNonatomic(); // No concurrent writes inside lock
if (job != prevJob) {
break;
}
condVar.wait(&mutex);
}
}
if (job == 1) {
return;
}
reinterpret_cast<Job*>(job)->run();
prevJob = job;
}
}
void runOne(Job* job)
{
#ifdef SANITY_CHECK
KIS_ASSERT_RECOVER_NOOP(job != (Job*) m_job.load(Relaxed));
#endif // SANITY_CHECK
storeRelease(job);
job->run();
}
void end()
{
{
QMutexLocker guard(&mutex);
m_job.store(1, Release);
}
condVar.wakeAll();
}
};
#endif // SIMPLEJOBCOORDINATOR_H
......@@ -22,7 +22,7 @@
#include <QList>
#include "kis_memento_item.h"
#include "kis_tile_hash_table.h"
#include "config-hash-table-implementaion.h"
typedef QList<KisMementoItemSP> KisMementoItemList;
typedef QListIterator<KisMementoItemSP> KisMementoItemListIterator;
......@@ -38,9 +38,19 @@ typedef QList<KisHistoryItem> KisHistoryList;
class KisMemento;
typedef KisSharedPtr<KisMemento> KisMementoSP;
#ifdef USE_LOCK_FREE_HASH_TABLE
#include "kis_tile_hash_table2.h"
typedef KisTileHashTableTraits2<KisMementoItem> KisMementoItemHashTable;
typedef KisTileHashTableIteratorTraits2<KisMementoItem> KisMementoItemHashTableIterator;
typedef KisTileHashTableIteratorTraits2<KisMementoItem> KisMementoItemHashTableIteratorConst;
#else
#include "kis_tile_hash_table.h"
typedef KisTileHashTableTraits<KisMementoItem> KisMementoItemHashTable;
typedef KisTileHashTableIteratorTraits<KisMementoItem, QWriteLocker> KisMementoItemHashTableIterator;
typedef KisTileHashTableIteratorTraits<KisMementoItem, QReadLocker> KisMementoItemHashTableIteratorConst;
#endif // USE_LOCK_FREE_HASH_TABLE
class KRITAIMAGE_EXPORT KisMementoManager
......
This diff is collapsed.
......@@ -25,11 +25,17 @@
#include <kis_shared.h>
#include <kis_shared_ptr.h>
#include "config-hash-table-implementaion.h"
//#include "kis_debug.h"
#include "kritaimage_export.h"
#ifdef USE_LOCK_FREE_HASH_TABLE
#include "kis_tile_hash_table2.h"
#else
#include "kis_tile_hash_table.h"
#endif // USE_LOCK_FREE_HASH_TABLE
#include "kis_memento_manager.h"
#include "kis_memento.h"
#include "KisTiledExtentManager.h"
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment