Netlist: code maintenance and bug fixes. (nw)

- solver now uses dynamic allocation on systems larger than 512x512
- fixed osx build
- moved nl_lists.h classes to plists.h
- fixed netlist makefile clint section
- readability and typos
This commit is contained in:
couriersud 2019-10-17 10:21:00 +02:00
parent da35541e84
commit db318046c4
21 changed files with 1784 additions and 1878 deletions

View File

@ -5579,7 +5579,10 @@ BX_TRACE("%d, %d, %d, %s", _array, _srgb, _mipAutogen, getName(_format) );
version = 0 == bx::strCmp(code, "#version 430", 12) ? 430 : version;
bx::write(&writer, &err, "#version %d\n", version);
if (version < 130)
bx::write(&writer, &err, "#version %d\n", 130);
else
bx::write(&writer, &err, "#version %d\n", version);
if (430 > version && usesTextureLod)
{

View File

@ -3935,8 +3935,8 @@ VK_IMPORT_DEVICE
{
attachments[mrt].colorAttachment = mrt;
attachments[mrt].aspectMask = 0;
attachments[mrt].aspectMask |= (_clear.m_flags & BGFX_CLEAR_DEPTH ) ? VK_IMAGE_ASPECT_DEPTH_BIT : 0;
attachments[mrt].aspectMask |= (_clear.m_flags & BGFX_CLEAR_STENCIL) ? VK_IMAGE_ASPECT_STENCIL_BIT : 0;
//attachments[mrt].aspectMask |= (_clear.m_flags & BGFX_CLEAR_DEPTH ) ? VK_IMAGE_ASPECT_DEPTH_BIT : 0;
//attachments[mrt].aspectMask |= (_clear.m_flags & BGFX_CLEAR_STENCIL) ? VK_IMAGE_ASPECT_STENCIL_BIT : 0;
attachments[mrt].clearValue.depthStencil.stencil = _clear.m_stencil;
attachments[mrt].clearValue.depthStencil.depth = _clear.m_depth;

View File

@ -43,7 +43,6 @@ project "netlist"
MAME_DIR .. "src/lib/netlist/nl_dice_compat.h",
MAME_DIR .. "src/lib/netlist/nl_factory.cpp",
MAME_DIR .. "src/lib/netlist/nl_factory.h",
MAME_DIR .. "src/lib/netlist/nl_lists.h",
MAME_DIR .. "src/lib/netlist/nl_parser.cpp",
MAME_DIR .. "src/lib/netlist/nl_parser.h",
MAME_DIR .. "src/lib/netlist/nl_setup.cpp",

View File

@ -23,10 +23,9 @@ TIDY_FLAGSX += -cppcoreguidelines-avoid-magic-numbers,
TIDY_FLAGSX += -cppcoreguidelines-macro-usage,
TIDY_FLAGSX += -cppcoreguidelines-non-private-member-variables-in-classes,-misc-non-private-member-variables-in-classes,
TIDY_FLAGSX += -bugprone-macro-parentheses,-misc-macro-parentheses,
TIDY_FLAGSX += -modernize-use-trailing-return-type
TIDY_FLAGSX += -bugprone-too-small-loop-variable
TIDY_FLAGSX += -bugprone-too-small-loop-variable,
TIDY_FLAGSX += -modernize-use-trailing-return-type,
TIDY_FLAGSX += -cppcoreguidelines-pro-bounds-array-to-pointer-decay
TIDY_FLAGSX += -modernize-use-trailing-return-type
space :=
space +=
@ -60,7 +59,7 @@ LD = @g++
MD = @mkdir
RM = @rm
DOXYGEN = @doxygen
CLANG_TIDY = clang-tidy-10
CLANG_TIDY = clang-tidy-9
TARGETS = nltool nlwav

File diff suppressed because it is too large Load Diff

View File

@ -16,15 +16,15 @@
#include "plib/palloc.h" // owned_ptr
#include "plib/pdynlib.h"
#include "plib/pfmtlog.h"
#include "plib/plists.h"
#include "plib/pmempool.h"
#include "plib/ppmf.h"
#include "plib/pstate.h"
#include "plib/pstream.h"
#include "plib/ptime.h"
#include "nl_errstr.h"
#include "nl_lists.h"
#include "nltypes.h"
#include "plib/ptime.h"
#include <unordered_map>
#include <vector>
@ -311,7 +311,6 @@ namespace netlist
* state_var<unsigned> m_var;
* }
*/
template <typename T>
struct state_var
{
@ -591,14 +590,14 @@ namespace netlist
nldelegate m_delegate;
#if USE_COPY_INSTEAD_OF_REFERENCE
void set_copied_input(netlist_sig_t val)
void set_copied_input(netlist_sig_t val) noexcept
{
m_Q = val;
}
state_var_sig m_Q;
#else
void set_copied_input(netlist_sig_t val) const { plib::unused_var(val); }
void set_copied_input(netlist_sig_t val) const noexcept { plib::unused_var(val); }
#endif
private:
@ -669,13 +668,13 @@ namespace netlist
std::vector<core_terminal_t *> &core_terms() { return m_core_terms; }
#if USE_COPY_INSTEAD_OF_REFERENCE
void update_inputs()
void update_inputs() noexcept
{
for (auto & term : m_core_terms)
term->m_Q = m_cur_Q;
}
#else
void update_inputs() const
void update_inputs() const noexcept
{
/* nothing needs to be done */
}
@ -810,9 +809,9 @@ namespace netlist
logic_t(core_device_t &dev, const pstring &aname,
const state_e state, nldelegate delegate = nldelegate());
bool has_proxy() const { return (m_proxy != nullptr); }
devices::nld_base_proxy *get_proxy() const { return m_proxy; }
void set_proxy(devices::nld_base_proxy *proxy) { m_proxy = proxy; }
bool has_proxy() const noexcept { return (m_proxy != nullptr); }
devices::nld_base_proxy *get_proxy() const noexcept { return m_proxy; }
void set_proxy(devices::nld_base_proxy *proxy) noexcept { m_proxy = proxy; }
logic_net_t & net() NL_NOEXCEPT;
const logic_net_t & net() const NL_NOEXCEPT;
@ -901,13 +900,13 @@ namespace netlist
analog_net_t(netlist_state_t &nl, const pstring &aname, detail::core_terminal_t *mr = nullptr);
nl_double Q_Analog() const NL_NOEXCEPT { return m_cur_Analog; }
void set_Q_Analog(const nl_double v) NL_NOEXCEPT { m_cur_Analog = v; }
nl_double Q_Analog() const noexcept { return m_cur_Analog; }
void set_Q_Analog(const nl_double v) noexcept { m_cur_Analog = v; }
nl_double *Q_Analog_state_ptr() NL_NOEXCEPT { return m_cur_Analog.ptr(); }
//FIXME: needed by current solver code
devices::matrix_solver_t *solver() const NL_NOEXCEPT { return m_solver; }
void set_solver(devices::matrix_solver_t *solver) NL_NOEXCEPT { m_solver = solver; }
devices::matrix_solver_t *solver() const noexcept { return m_solver; }
void set_solver(devices::matrix_solver_t *solver) noexcept { m_solver = solver; }
private:
state_var<nl_double> m_cur_Analog;
@ -1200,9 +1199,9 @@ namespace netlist
struct stats_t
{
// NL_KEEP_STATISTICS
nperftime_t<true> m_stat_total_time;
nperfcount_t<true> m_stat_call_count;
nperfcount_t<true> m_stat_inc_active;
plib::pperftime_t<true> m_stat_total_time;
plib::pperfcount_t<true> m_stat_call_count;
plib::pperfcount_t<true> m_stat_inc_active;
};
unique_pool_ptr<stats_t> m_stats;
@ -1245,8 +1244,8 @@ namespace netlist
~device_t() noexcept override = default;
setup_t &setup();
const setup_t &setup() const;
setup_t &setup() noexcept;
const setup_t &setup() const noexcept;
template<class C, typename... Args>
void create_and_register_subdevice(const pstring &name, unique_pool_ptr<C> &dev, Args&&... args)
@ -1283,6 +1282,16 @@ namespace netlist
family_setter_t(core_device_t &dev, const logic_family_desc_t *desc);
};
template <class T, bool TS>
using timed_queue = plib::timed_queue_linear<T, TS>;
/* Use timed_queue_heap to use stdc++ heap functions instead of linear processing.
* This slows down processing by about 25% on a Kaby Lake.
*/
//template <class T, bool TS>
//using timed_queue = timed_queue_heap<T, TS>;
// -----------------------------------------------------------------------------
// queue_t
// -----------------------------------------------------------------------------
@ -1292,13 +1301,13 @@ namespace netlist
*/
class detail::queue_t :
//public timed_queue<pqentry_t<net_t *, netlist_time>, false, NL_KEEP_STATISTICS>,
public timed_queue<pqentry_t<net_t *, netlist_time>, false>,
public timed_queue<plib::pqentry_t<net_t *, netlist_time>, false>,
public detail::netlist_ref,
public plib::state_manager_t::callback_t
{
public:
using base_queue = timed_queue<pqentry_t<net_t *, netlist_time>, false>;
using entry_t = pqentry_t<net_t *, netlist_time>;
using base_queue = timed_queue<plib::pqentry_t<net_t *, netlist_time>, false>;
using entry_t = plib::pqentry_t<net_t *, netlist_time>;
explicit queue_t(netlist_state_t &nl);
virtual ~queue_t() noexcept = default;
@ -1342,7 +1351,7 @@ namespace netlist
friend class netlist_t; // allow access to private members
template<class C>
static bool check_class(core_device_t *p)
static bool check_class(core_device_t *p) noexcept
{
return dynamic_cast<C *>(p) != nullptr;
}
@ -1385,12 +1394,12 @@ namespace netlist
/* logging and name */
pstring name() const { return m_name; }
pstring name() const noexcept { return m_name; }
log_type & log() { return m_log; }
const log_type &log() const { return m_log; }
log_type & log() noexcept { return m_log; }
const log_type &log() const noexcept { return m_log; }
plib::dynlib &lib() { return *m_lib; }
plib::dynlib &lib() const noexcept { return *m_lib; }
netlist_t &exec() { return m_netlist; }
const netlist_t &exec() const { return m_netlist; }
@ -1486,16 +1495,19 @@ namespace netlist
}
}
setup_t &setup() NL_NOEXCEPT { return *m_setup; }
const setup_t &setup() const NL_NOEXCEPT { return *m_setup; }
setup_t &setup() noexcept { return *m_setup; }
const setup_t &setup() const noexcept { return *m_setup; }
// FIXME: make a postload member and include code there
void rebuild_lists(); /* must be called after post_load ! */
static void compile_defines(std::vector<std::pair<pstring, pstring>> &defs);
nets_collection_type & nets() { return m_nets; }
devices_collection_type & devices() { return m_devices; }
nets_collection_type & nets() noexcept { return m_nets; }
const nets_collection_type & nets() const noexcept { return m_nets; }
devices_collection_type & devices() noexcept { return m_devices; }
const devices_collection_type & devices() const noexcept { return m_devices; }
/* sole use is to manage lifetime of family objects */
std::unordered_map<pstring, plib::unique_ptr<logic_family_desc_t>> m_family_cache;
@ -1549,12 +1561,13 @@ namespace netlist
const detail::queue_t &queue() const NL_NOEXCEPT { return m_queue; }
void qpush(detail::queue_t::entry_t && e) noexcept
template <typename E>
void qpush(E && e) noexcept
{
if (!USE_QUEUE_STATS || !m_use_stats)
m_queue.push<false>(std::move(e)); // NOLINT(performance-move-const-arg)
m_queue.push<false>(std::forward<E>(e)); // NOLINT(performance-move-const-arg)
else
m_queue.push<true>(std::move(e)); // NOLINT(performance-move-const-arg)
m_queue.push<true>(std::forward<E>(e)); // NOLINT(performance-move-const-arg)
}
template <class R>
@ -1573,10 +1586,10 @@ namespace netlist
/* state handling */
plib::state_manager_t &run_state_manager() { return m_state->run_state_manager(); }
plib::state_manager_t &run_state_manager() noexcept { return m_state->run_state_manager(); }
/* only used by nltool to create static c-code */
devices::NETLIB_NAME(solver) *solver() const NL_NOEXCEPT { return m_solver; }
devices::NETLIB_NAME(solver) *solver() const noexcept { return m_solver; }
/* force late type resolution */
template <typename X = devices::NETLIB_NAME(solver)>
@ -1586,21 +1599,21 @@ namespace netlist
return static_cast<X *>(m_solver)->gmin();
}
netlist_state_t &nlstate() NL_NOEXCEPT { return *m_state; }
const netlist_state_t &nlstate() const { return *m_state; }
netlist_state_t &nlstate() noexcept { return *m_state; }
const netlist_state_t &nlstate() const noexcept { return *m_state; }
log_type & log() { return m_state->log(); }
const log_type &log() const { return m_state->log(); }
log_type & log() NL_NOEXCEPT { return m_state->log(); }
const log_type &log() const NL_NOEXCEPT { return m_state->log(); }
void print_stats() const;
void print_stats() const NL_NOEXCEPT;
bool stats_enabled() const { return m_use_stats; }
void enable_stats(bool val) { m_use_stats = val; }
bool stats_enabled() const noexcept { return m_use_stats; }
void enable_stats(bool val) noexcept { m_use_stats = val; }
private:
template <bool KEEP_STATS>
void process_queue_stats(netlist_time delta) NL_NOEXCEPT;
template <bool KEEP_STATS, typename MCT>
void process_queue_stats(netlist_time delta, MCT *mainclock) NL_NOEXCEPT;
plib::unique_ptr<netlist_state_t> m_state;
devices::NETLIB_NAME(solver) * m_solver;
@ -1615,8 +1628,8 @@ namespace netlist
bool m_use_stats;
// performance
nperftime_t<true> m_stat_mainloop;
nperfcount_t<true> m_perf_out_processed;
plib::pperftime_t<true> m_stat_mainloop;
plib::pperfcount_t<true> m_perf_out_processed;
};
// -----------------------------------------------------------------------------
@ -1782,7 +1795,7 @@ namespace netlist
if (m_next_scheduled_time > exec().time())
{
m_in_queue = queue_status::QUEUED; /* pending */
exec().qpush({m_next_scheduled_time, this});
exec().qpush(detail::queue_t::entry_t(m_next_scheduled_time, this));
}
else
{
@ -1904,6 +1917,106 @@ namespace netlist
for (std::size_t i=0; i<N; i++)
(*this)[i] = value;
}
// -----------------------------------------------------------------------------
// Hot section
//
// Any changes below will impact performance.
// -----------------------------------------------------------------------------
template <bool KEEP_STATS, typename T>
inline void detail::net_t::process(const T mask, netlist_sig_t sig)
{
m_cur_Q = sig;
if (KEEP_STATS)
{
for (auto & p : m_list_active)
{
p.set_copied_input(sig);
auto *stats = p.device().m_stats.get();
stats->m_stat_call_count.inc();
if ((p.terminal_state() & mask))
{
auto g(stats->m_stat_total_time.guard());
p.m_delegate();
}
}
}
else
{
for (auto &p : m_list_active)
{
p.set_copied_input(sig);
if ((p.terminal_state() & mask))
p.m_delegate();
}
}
}
template <bool KEEP_STATS>
inline void detail::net_t::update_devs() NL_NOEXCEPT
{
nl_assert(this->isRailNet());
m_in_queue = queue_status::DELIVERED; /* mark as taken ... */
if (m_new_Q ^ m_cur_Q)
process<KEEP_STATS>((m_new_Q << core_terminal_t::INP_LH_SHIFT)
| (m_cur_Q << core_terminal_t::INP_HL_SHIFT), m_new_Q);
}
template <bool KEEP_STATS, typename MCT>
inline void netlist_t::process_queue_stats(const netlist_time delta, MCT *mainclock) NL_NOEXCEPT
{
netlist_time stop(m_time + delta);
qpush(detail::queue_t::entry_t(stop, nullptr));
if (m_mainclock == nullptr)
{
detail::queue_t::entry_t e(m_queue.pop());
m_time = e.m_exec_time;
while (e.m_object != nullptr)
{
e.m_object->template update_devs<KEEP_STATS>();
if (KEEP_STATS)
m_perf_out_processed.inc();
e = m_queue.pop();
m_time = e.m_exec_time;
}
}
else
{
logic_net_t &mc_net(mainclock->m_Q.net());
const netlist_time inc(mainclock->m_inc);
netlist_time mc_time(mc_net.next_scheduled_time());
do
{
while (m_queue.top().m_exec_time > mc_time)
{
m_time = mc_time;
mc_net.toggle_new_Q();
mc_net.update_devs<KEEP_STATS>();
mc_time += inc;
}
detail::queue_t::entry_t e(m_queue.pop());
m_time = e.m_exec_time;
if (e.m_object != nullptr)
{
e.m_object->template update_devs<KEEP_STATS>();
if (KEEP_STATS)
m_perf_out_processed.inc();
}
else
break;
} while (true); //while (e.m_object != nullptr);
mc_net.set_next_scheduled_time(mc_time);
}
}
} // namespace netlist
namespace plib

View File

@ -14,7 +14,7 @@
#define PERRMSG(name, str) \
struct name \
{ \
operator pstring() const { return str; } \
operator pstring() const noexcept { return str; } \
};
#define PERRMSGV(name, narg, str) \
@ -23,7 +23,7 @@
template<typename... Args> name(Args&&... args) \
: m_m(plib::pfmt(str)(std::forward<Args>(args)...)) \
{ static_assert(narg == sizeof...(args), "Argument count mismatch"); } \
operator pstring() const { return m_m; } \
operator pstring() const noexcept { return m_m; } \
pstring m_m; \
};

View File

@ -1,335 +0,0 @@
// license:GPL-2.0+
// copyright-holders:Couriersud
/*
* nllists.h
*
*/
#pragma once
#ifndef NLLISTS_H_
#define NLLISTS_H_
#include "plib/pchrono.h"
#include "plib/plists.h"
#include "plib/ptypes.h"
#include "plib/parray.h"
#include "nl_config.h"
#include "nltypes.h"
#include <algorithm>
#include <atomic>
#include <mutex>
#include <thread>
#include <utility>
// ----------------------------------------------------------------------------------------
// timed queue
// ----------------------------------------------------------------------------------------
namespace netlist
{
//FIXME: move to an appropriate place
template<bool enabled_ = true>
class pspin_mutex
{
public:
pspin_mutex() noexcept = default;
void lock() noexcept{ while (m_lock.test_and_set(std::memory_order_acquire)) { } }
void unlock() noexcept { m_lock.clear(std::memory_order_release); }
private:
PALIGNAS_CACHELINE()
std::atomic_flag m_lock = ATOMIC_FLAG_INIT;
};
template<>
class pspin_mutex<false>
{
public:
void lock() const noexcept { }
void unlock() const noexcept { }
};
template <class Element, class Time>
struct pqentry_t final
{
constexpr pqentry_t() noexcept : m_exec_time(), m_object(nullptr) { }
constexpr pqentry_t(const Time t, const Element o) noexcept : m_exec_time(t), m_object(o) { }
#if 0
~pqentry_t() = default;
constexpr pqentry_t(const pqentry_t &e) noexcept = default;
constexpr pqentry_t(pqentry_t &&e) noexcept = default;
pqentry_t& operator=(pqentry_t && other) noexcept = default;
pqentry_t& operator=(const pqentry_t &other) noexcept = default;
void swap(pqentry_t &other) noexcept
{
std::swap(m_exec_time, other.m_exec_time);
std::swap(m_object, other.m_object);
}
#endif
inline bool operator ==(const pqentry_t &rhs) const noexcept
{
return m_object == rhs.m_object;
}
inline bool operator ==(const Element &rhs) const noexcept
{
return m_object == rhs;
}
inline bool operator <=(const pqentry_t &rhs) const noexcept
{
return (m_exec_time <= rhs.m_exec_time);
}
inline bool operator <(const pqentry_t &rhs) const noexcept
{
return (m_exec_time < rhs.m_exec_time);
}
inline static constexpr pqentry_t never() noexcept { return pqentry_t(Time::never(), nullptr); }
Time m_exec_time;
Element m_object;
};
/* Use TS = true for a threadsafe queue */
template <class T, bool TS>
class timed_queue_linear : plib::nocopyassignmove
{
public:
explicit timed_queue_linear(const std::size_t list_size)
: m_list(list_size)
{
clear();
}
std::size_t capacity() const noexcept { return m_list.capacity() - 1; }
bool empty() const noexcept { return (m_end == &m_list[1]); }
template<bool KEEPSTAT>
void push(T && e) noexcept
{
/* Lock */
lock_guard_type lck(m_lock);
T * i(m_end-1);
for (; *i < e; --i)
{
*(i+1) = *(i);
if (KEEPSTAT)
m_prof_sortmove.inc();
}
*(i+1) = std::move(e);
++m_end;
if (KEEPSTAT)
m_prof_call.inc();
}
T pop() noexcept { return *(--m_end); }
const T &top() const noexcept { return *(m_end-1); }
template <bool KEEPSTAT, class R>
void remove(const R &elem) noexcept
{
/* Lock */
lock_guard_type lck(m_lock);
if (KEEPSTAT)
m_prof_remove.inc();
for (T * i = m_end - 1; i > &m_list[0]; --i)
{
// == operator ignores time!
if (*i == elem)
{
std::copy(i+1, m_end--, i);
return;
}
}
}
template <bool KEEPSTAT, class R>
void retime(R && elem) noexcept
{
/* Lock */
lock_guard_type lck(m_lock);
if (KEEPSTAT)
m_prof_retime.inc();
for (R * i = m_end - 1; i > &m_list[0]; --i)
{
if (*i == elem) // partial equal!
{
*i = std::move(elem);
while (*(i-1) < *i)
{
std::swap(*(i-1), *i);
--i;
}
while (i < m_end && *i < *(i+1))
{
std::swap(*(i+1), *i);
++i;
}
return;
}
}
}
void clear() noexcept
{
lock_guard_type lck(m_lock);
m_end = &m_list[0];
/* put an empty element with maximum time into the queue.
* the insert algo above will run into this element and doesn't
* need a comparison with queue start.
*/
m_list[0] = T::never();
m_end++;
}
// save state support & mame disasm
const T *listptr() const noexcept { return &m_list[1]; }
std::size_t size() const noexcept { return static_cast<std::size_t>(m_end - &m_list[1]); }
const T & operator[](const std::size_t index) const noexcept { return m_list[ 1 + index]; }
private:
using mutex_type = pspin_mutex<TS>;
using lock_guard_type = std::lock_guard<mutex_type>;
mutex_type m_lock;
PALIGNAS_CACHELINE()
T * m_end;
plib::aligned_vector<T> m_list;
public:
// profiling
nperfcount_t<true> m_prof_sortmove;
nperfcount_t<true> m_prof_call;
nperfcount_t<true> m_prof_remove;
nperfcount_t<true> m_prof_retime;
};
template <class T, bool TS>
class timed_queue_heap : plib::nocopyassignmove
{
public:
struct compare
{
constexpr bool operator()(const T &a, const T &b) const { return b <= a; }
};
explicit timed_queue_heap(const std::size_t list_size)
: m_list(list_size)
{
clear();
}
std::size_t capacity() const noexcept { return m_list.capacity(); }
bool empty() const noexcept { return &m_list[0] == m_end; }
template <bool KEEPSTAT>
void push(T &&e) noexcept
{
/* Lock */
lock_guard_type lck(m_lock);
*m_end++ = e;
std::push_heap(&m_list[0], m_end, compare());
if (KEEPSTAT)
m_prof_call.inc();
}
T pop() noexcept
{
T ret(m_list[0]);
std::pop_heap(&m_list[0], m_end, compare());
m_end--;
return ret;
}
const T &top() const noexcept { return m_list[0]; }
template <bool KEEPSTAT, class R>
void remove(const R &elem) noexcept
{
/* Lock */
lock_guard_type lck(m_lock);
if (KEEPSTAT)
m_prof_remove.inc();
for (T * i = m_end - 1; i >= &m_list[0]; i--)
{
if (*i == elem)
{
m_end--;
for (;i < m_end; i++)
*i = std::move(*(i+1));
std::make_heap(&m_list[0], m_end, compare());
return;
}
}
}
template <bool KEEPSTAT>
void retime(const T &elem) noexcept
{
/* Lock */
lock_guard_type lck(m_lock);
if (KEEPSTAT)
m_prof_retime.inc();
for (T * i = m_end - 1; i >= &m_list[0]; i--)
{
if (*i == elem) // partial equal!
{
*i = elem;
std::make_heap(&m_list[0], m_end, compare());
return;
}
}
}
void clear()
{
lock_guard_type lck(m_lock);
m_list.clear();
m_end = &m_list[0];
}
// save state support & mame disasm
constexpr const T *listptr() const { return &m_list[0]; }
constexpr std::size_t size() const noexcept { return m_list.size(); }
constexpr const T & operator[](const std::size_t index) const { return m_list[ 0 + index]; }
private:
using mutex_type = pspin_mutex<TS>;
using lock_guard_type = std::lock_guard<mutex_type>;
mutex_type m_lock;
std::vector<T> m_list;
T *m_end;
public:
// profiling
nperfcount_t<true> m_prof_sortmove;
nperfcount_t<true> m_prof_call;
nperfcount_t<true> m_prof_remove;
nperfcount_t<true> m_prof_retime;
};
template <class T, bool TS>
using timed_queue = timed_queue_linear<T, TS>;
/*
* Use timed_queue_heap to use stdc++ heap functions instead of linear processing.
*
* This slows down processing by about 25% on a Kaby Lake.
*/
//template <class T, bool TS>
//using timed_queue = timed_queue_heap<T, TS>;
} // namespace netlist
#endif /* NLLISTS_H_ */

View File

@ -68,17 +68,6 @@ namespace netlist
using log_type = plib::plog_base<callbacks_t, NL_DEBUG>;
//============================================================
// Performance tracking
//============================================================
template<bool enabled_>
using nperftime_t = plib::chrono::timer<plib::chrono::exact_ticks, enabled_>;
template<bool enabled_>
using nperfcount_t = plib::chrono::counter<enabled_>;
//============================================================
// Types needed by various includes
//============================================================

View File

@ -34,8 +34,10 @@ namespace plib {
{
//using arena_storage_type = P *;
using arena_storage_type = typename std::conditional<P::is_stateless, P, P *>::type;
template <typename X, typename Y = void>
typename std::enable_if<!X::is_stateless, X&>::type getref(X *x) { return *x;}
template <typename X, typename Y = void *>
typename std::enable_if<std::remove_pointer<X>::type::is_stateless, X&>::type
getref(X &x, Y y = nullptr)
@ -44,13 +46,18 @@ namespace plib {
return x;
}
constexpr arena_deleter(arena_storage_type a = arena_storage_type()) noexcept
constexpr arena_deleter(arena_storage_type a = arena_storage_type())
: m_a(a) { }
#if 1
template<typename U, typename = typename
std::enable_if<std::is_convertible< U*, T*>::value>::type>
arena_deleter(const arena_deleter<P, U> &rhs) : m_a(rhs.m_a) { }
#else
template<typename PU, typename U, typename = typename
std::enable_if<std::is_convertible< U*, T*>::value>::type>
arena_deleter(const arena_deleter<PU, U> &rhs) noexcept : m_a(rhs.m_a) { }
arena_deleter(const arena_deleter<PU, U> &rhs) : m_a(rhs.m_a) { }
#endif
void operator()(T *p) //const
{
/* call destructor */
@ -80,11 +87,11 @@ namespace plib {
template <typename, typename>
friend class owned_ptr;
owned_ptr(pointer p, bool owned) noexcept
owned_ptr(pointer p, bool owned)
: m_ptr(p), m_deleter(), m_is_owned(owned)
{ }
owned_ptr(pointer p, bool owned, D deleter) noexcept
owned_ptr(pointer p, bool owned, D deleter)
: m_ptr(p), m_deleter(deleter), m_is_owned(owned)
{ }
@ -93,7 +100,7 @@ namespace plib {
owned_ptr & operator =(owned_ptr &r) = delete;
template<typename DC, typename DC_D>
owned_ptr & operator =(owned_ptr<DC, DC_D> &&r)
owned_ptr & operator =(owned_ptr<DC, DC_D> &&r) noexcept
{
if (m_is_owned && (m_ptr != nullptr))
//delete m_ptr;
@ -122,7 +129,7 @@ namespace plib {
m_deleter(m_ptr);
m_is_owned = r.m_is_owned;
m_ptr = r.m_ptr;
m_deleter = std::move(r.m_deleter);
m_deleter = r.m_deleter;
r.m_is_owned = false;
r.m_ptr = nullptr;
return *this;
@ -197,18 +204,12 @@ namespace plib {
~arena_allocator() noexcept = default;
arena_allocator(const arena_allocator &rhs) noexcept = default;
arena_allocator& operator=(const arena_allocator&) noexcept = delete;
arena_allocator(arena_allocator&&) noexcept = default;
arena_allocator& operator=(arena_allocator&&) = delete;
arena_allocator(arena_type & a) noexcept : m_a(a)
{
}
template <class U>
arena_allocator(const arena_allocator<ARENA, U, ALIGN>& rhs) noexcept
arena_allocator(const arena_allocator<ARENA, U, ALIGN>& rhs)
: m_a(rhs.m_a)
{
}
@ -223,7 +224,7 @@ namespace plib {
return reinterpret_cast<T *>(m_a.allocate(ALIGN, sizeof(T) * n));
}
void deallocate(T* p, std::size_t n) noexcept
void deallocate(T* p, std::size_t n)
{
unused_var(n);
m_a.deallocate(p);

View File

@ -32,6 +32,7 @@ namespace plib {
struct sizeabs<FT, 0>
{
static constexpr std::size_t ABS() { return 0; }
//using container = typename std::vector<FT, arena_allocator<mempool, FT, 64>>;
using container = typename std::vector<FT, aligned_allocator<FT, PALIGN_VECTOROPT>>;
};
@ -75,6 +76,15 @@ namespace plib {
{
}
// osx clang doesn't like COPYASSIGNMOVE(parray, default)
// it will generate some weird error messages about move assignment
// constructor having a different noexcept status.
parray(const parray &rhs) : m_a(rhs.m_a), m_size(rhs.m_size) {}
parray(parray &&rhs) noexcept : m_a(std::move(rhs.m_a)), m_size(std::move(rhs.m_size)) {}
parray &operator=(const parray &rhs) { m_a = rhs.m_a; m_size = rhs.m_size; return *this; }
parray &operator=(parray &&rhs) noexcept { std::swap(m_a,rhs.m_a); std::swap(m_size, rhs.m_size); return *this; }
template <int X = SIZE >
parray(size_type size, typename std::enable_if<(X != 0), int>::type = 0)
: m_size(size)
@ -125,6 +135,8 @@ namespace plib {
(*this)[i] = parray<FT, SIZE2>(size2);
}
}
COPYASSIGNMOVE(parray2D, default)
};
} // namespace plib

View File

@ -15,245 +15,253 @@
//#include <cstdint>
namespace plib {
namespace chrono {
template <typename T>
struct sys_ticks
{
using type = typename T::rep;
static inline type start() { return T::now().time_since_epoch().count(); }
static inline type stop() { return T::now().time_since_epoch().count(); }
static inline constexpr type per_second() { return T::period::den / T::period::num; }
};
using hires_ticks = sys_ticks<std::chrono::high_resolution_clock>;
using steady_ticks = sys_ticks<std::chrono::steady_clock>;
using system_ticks = sys_ticks<std::chrono::system_clock>;
#if defined(__x86_64__) && !defined(_clang__) && !defined(_MSC_VER) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ > 6))
template <typename T, typename R>
struct base_ticks
{
using ret_type = R;
static ret_type per_second()
namespace chrono {
template <typename T>
struct sys_ticks
{
static ret_type persec = 0;
if (persec == 0)
using type = typename T::rep;
static inline constexpr type start() noexcept { return T::now().time_since_epoch().count(); }
static inline constexpr type stop() noexcept { return T::now().time_since_epoch().count(); }
static inline constexpr type per_second() noexcept { return T::period::den / T::period::num; }
};
using hires_ticks = sys_ticks<std::chrono::high_resolution_clock>;
using steady_ticks = sys_ticks<std::chrono::steady_clock>;
using system_ticks = sys_ticks<std::chrono::system_clock>;
#if defined(__x86_64__) && !defined(_clang__) && !defined(_MSC_VER) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ > 6))
template <typename T, typename R>
struct base_ticks
{
using ret_type = R;
static ret_type per_second()
{
ret_type x = 0;
system_ticks::type t = system_ticks::start();
system_ticks::type e;
x = - T :: start();
do {
e = system_ticks::stop();
} while (e - t < system_ticks::per_second() / 100 );
x += T :: stop();
persec = (ret_type)(double)((double) x * (double) system_ticks::per_second() / double (e - t));
static ret_type persec = 0;
if (persec == 0)
{
ret_type x = 0;
system_ticks::type t = system_ticks::start();
system_ticks::type e;
x = - T :: start();
do {
e = system_ticks::stop();
} while (e - t < system_ticks::per_second() / 100 );
x += T :: stop();
persec = (ret_type)(double)((double) x * (double) system_ticks::per_second() / double (e - t));
}
return persec;
}
return persec;
}
};
};
#if PHAS_RDTSCP
struct fast_ticks : public base_ticks<fast_ticks, int64_t>
{
typedef int64_t type;
static inline type start()
#if PHAS_RDTSCP
struct fast_ticks : public base_ticks<fast_ticks, int64_t>
{
int64_t v;
__asm__ __volatile__ (
"rdtscp;"
"shl $32, %%rdx;"
"or %%rdx, %%rax;"
: "=a"(v) /* outputs */
: /* inputs */
: "%rcx", "%rdx" /* clobbers */
);
return v;
}
static inline type stop()
typedef int64_t type;
static inline type start() noexcept
{
int64_t v;
__asm__ __volatile__ (
"rdtscp;"
"shl $32, %%rdx;"
"or %%rdx, %%rax;"
: "=a"(v) /* outputs */
: /* inputs */
: "%rcx", "%rdx" /* clobbers */
);
return v;
}
static inline type stop() noexcept
{
return start();
}
};
#else
struct fast_ticks : public base_ticks<fast_ticks, int64_t>
{
return start();
}
};
typedef int64_t type;
static inline type start() noexcept
{
int64_t v;
__asm__ __volatile__ (
"rdtsc;"
"shl $32, %%rdx;"
"or %%rdx, %%rax;"
: "=a"(v) /* outputs */
: /* inputs */
: "%rdx" /* clobbers */
);
return v;
}
static inline type stop() noexcept
{
return start();
}
};
#else
struct fast_ticks : public base_ticks<fast_ticks, int64_t>
{
typedef int64_t type;
static inline type start()
#endif
/* Based on "How to Benchmark Code Execution Times on Intel?? IA-32 and IA-64
* Instruction Set Architectures", Intel, 2010
*
*/
#if PUSE_ACCURATE_STATS && PHAS_RDTSCP
/*
* kills performance completely, but is accurate
* cpuid serializes, but clobbers ebx and ecx
*
*/
struct exact_ticks : public base_ticks<exact_ticks, int64_t>
{
int64_t v;
__asm__ __volatile__ (
"rdtsc;"
"shl $32, %%rdx;"
"or %%rdx, %%rax;"
: "=a"(v) /* outputs */
: /* inputs */
: "%rdx" /* clobbers */
);
return v;
}
static inline type stop()
typedef int64_t type;
static inline type start() noexcept
{
int64_t v;
__asm__ __volatile__ (
"cpuid;"
//"xor %%eax, %%eax\n\t"
"rdtsc;"
"shl $32, %%rdx;"
"or %%rdx, %%rax;"
: "=a"(v) /* outputs */
: "a"(0x0) /* inputs */
: "%ebx", "%ecx", "%rdx" /* clobbers*/
);
return v;
}
static inline type stop() noexcept
{
int64_t v;
__asm__ __volatile__ (
"rdtscp;"
"shl $32, %%rdx;"
"or %%rax, %%rdx;"
"mov %%rdx, %%r10;"
"xor %%eax, %%eax\n\t"
"cpuid;"
"mov %%r10, %%rax;"
: "=a" (v)
:
: "%ebx", "%ecx", "%rdx", "%r10"
);
return v;
}
};
#else
using exact_ticks = fast_ticks;
#endif
#else
using fast_ticks = hires_ticks;
using exact_ticks = fast_ticks;
#endif
template<bool enabled_>
struct counter
{
return start();
}
};
#endif
/* Based on "How to Benchmark Code Execution Times on Intel?? IA-32 and IA-64
* Instruction Set Architectures", Intel, 2010
*
*/
#if PUSE_ACCURATE_STATS && PHAS_RDTSCP
/*
* kills performance completely, but is accurate
* cpuid serializes, but clobbers ebx and ecx
*
*/
struct exact_ticks : public base_ticks<exact_ticks, int64_t>
{
typedef int64_t type;
static inline type start()
{
int64_t v;
__asm__ __volatile__ (
"cpuid;"
//"xor %%eax, %%eax\n\t"
"rdtsc;"
"shl $32, %%rdx;"
"or %%rdx, %%rax;"
: "=a"(v) /* outputs */
: "a"(0x0) /* inputs */
: "%ebx", "%ecx", "%rdx" /* clobbers*/
);
return v;
}
static inline type stop()
{
int64_t v;
__asm__ __volatile__ (
"rdtscp;"
"shl $32, %%rdx;"
"or %%rax, %%rdx;"
"mov %%rdx, %%r10;"
"xor %%eax, %%eax\n\t"
"cpuid;"
"mov %%r10, %%rax;"
: "=a" (v)
:
: "%ebx", "%ecx", "%rdx", "%r10"
);
return v;
}
};
#else
using exact_ticks = fast_ticks;
#endif
#else
using fast_ticks = hires_ticks;
using exact_ticks = fast_ticks;
#endif
template<bool enabled_>
struct counter
{
counter() : m_count(0) { }
using type = uint_least64_t;
type operator()() const { return m_count; }
void inc() { ++m_count; }
void reset() { m_count = 0; }
constexpr static bool enabled = enabled_;
private:
type m_count;
};
template<>
struct counter<false>
{
using type = uint_least64_t;
constexpr type operator()() const { return 0; }
void inc() const { }
void reset() const { }
constexpr static bool enabled = false;
};
template< typename T, bool enabled_ = true>
struct timer
{
using type = typename T::type;
using ctype = uint_least64_t;
constexpr static bool enabled = enabled_;
struct guard_t
{
guard_t() = delete;
guard_t(timer &m) noexcept : m_m(m) { m_m.m_time -= T::start(); }
~guard_t() { m_m.m_time += T::stop(); ++m_m.m_count; }
COPYASSIGNMOVE(guard_t, default)
counter() : m_count(0) { }
using type = uint_least64_t;
type operator()() const noexcept { return m_count; }
void inc() noexcept { ++m_count; }
void reset() noexcept { m_count = 0; }
constexpr static bool enabled = enabled_;
private:
timer &m_m;
type m_count;
};
friend struct guard_t;
timer() : m_time(0), m_count(0) { }
type operator()() const { return m_time; }
void reset() { m_time = 0; m_count = 0; }
type average() const { return (m_count == 0) ? 0 : m_time / m_count; }
type total() const { return m_time; }
ctype count() const { return m_count; }
double as_seconds() const { return static_cast<double>(total())
/ static_cast<double>(T::per_second()); }
guard_t guard() { return guard_t(*this); }
private:
type m_time;
ctype m_count;
};
template<typename T>
struct timer<T, false>
{
using type = typename T::type;
using ctype = uint_least64_t;
struct guard_t
template<>
struct counter<false>
{
guard_t() = default;
COPYASSIGNMOVE(guard_t, default)
/* using default constructor will trigger warning on
* unused local variable.
*/
// NOLINTNEXTLINE(modernize-use-equals-default)
~guard_t() { }
using type = uint_least64_t;
constexpr type operator()() const { return 0; }
void inc() const { }
void reset() const { }
constexpr static bool enabled = false;
};
constexpr type operator()() const { return 0; }
void reset() const { }
constexpr type average() const { return 0; }
constexpr type total() const { return 0; }
constexpr ctype count() const { return 0; }
constexpr double as_seconds() const { return 0.0; }
constexpr static bool enabled = false;
guard_t guard() { return guard_t(); }
};
template< typename T, bool enabled_ = true>
struct timer
{
using type = typename T::type;
using ctype = uint_least64_t;
constexpr static bool enabled = enabled_;
struct guard_t
{
guard_t() = delete;
guard_t(timer &m) noexcept : m_m(m) { m_m.m_time -= T::start(); }
~guard_t() { m_m.m_time += T::stop(); ++m_m.m_count; }
COPYASSIGNMOVE(guard_t, default)
private:
timer &m_m;
};
friend struct guard_t;
timer() : m_time(0), m_count(0) { }
type operator()() const { return m_time; }
void reset() { m_time = 0; m_count = 0; }
type average() const { return (m_count == 0) ? 0 : m_time / m_count; }
type total() const { return m_time; }
ctype count() const { return m_count; }
double as_seconds() const { return static_cast<double>(total())
/ static_cast<double>(T::per_second()); }
guard_t guard() { return guard_t(*this); }
private:
type m_time;
ctype m_count;
};
template<typename T>
struct timer<T, false>
{
using type = typename T::type;
using ctype = uint_least64_t;
struct guard_t
{
guard_t() = default;
COPYASSIGNMOVE(guard_t, default)
/* using default constructor will trigger warning on
* unused local variable.
*/
// NOLINTNEXTLINE(modernize-use-equals-default)
~guard_t() { }
};
constexpr type operator()() const { return 0; }
void reset() const { }
constexpr type average() const { return 0; }
constexpr type total() const { return 0; }
constexpr ctype count() const { return 0; }
constexpr double as_seconds() const { return 0.0; }
constexpr static bool enabled = false;
guard_t guard() { return guard_t(); }
};
} // namespace chrono
//============================================================
// Performance tracking
//============================================================
template<bool enabled_>
using pperftime_t = plib::chrono::timer<plib::chrono::exact_ticks, enabled_>;
template<bool enabled_>
using pperfcount_t = plib::chrono::counter<enabled_>;
} // namespace plib
#endif /* PCHRONO_H_ */

View File

@ -1,7 +1,7 @@
// license:GPL-2.0+
// copyright-holders:Couriersud
/*
* nl_string.c
* pfm_log.cpp
*
*/

View File

@ -10,283 +10,506 @@
#ifndef PLISTS_H_
#define PLISTS_H_
#include "palloc.h"
#include "pchrono.h"
#include "pstring.h"
#include <algorithm>
#include <array>
#include <atomic>
#include <mutex>
#include <type_traits>
#include <utility>
#include <vector>
namespace plib {
/* ----------------------------------------------------------------------------------------
* uninitialised_array_t:
* fixed size array allowing to override constructor and initialize
* members by placement new.
*
* Use with care. This template is provided to improve locality of storage
* in high frequency applications. It should not be used for anything else.
* ---------------------------------------------------------------------------------------- */
template <class C, std::size_t N>
class uninitialised_array_t
{
public:
using iterator = C *;
using const_iterator = const C *;
//uninitialised_array_t() noexcept = default;
uninitialised_array_t() noexcept
: m_initialized(0)
{
}
COPYASSIGNMOVE(uninitialised_array_t, delete)
~uninitialised_array_t() noexcept
{
if (m_initialized>=N)
for (std::size_t i=0; i<N; i++)
(*this)[i].~C();
}
size_t size() const { return N; }
C& operator[](const std::size_t &index) noexcept
{
return *reinterpret_cast<C *>(&m_buf[index]);
}
const C& operator[](const std::size_t &index) const noexcept
{
return *reinterpret_cast<const C *>(&m_buf[index]);
}
template<typename... Args>
void emplace(const std::size_t index, Args&&... args)
{
m_initialized++;
// allocate on buffer
new (&m_buf[index]) C(std::forward<Args>(args)...);
}
iterator begin() const noexcept { return reinterpret_cast<iterator>(&m_buf[0]); }
iterator end() const noexcept { return reinterpret_cast<iterator>(&m_buf[N]); }
iterator begin() noexcept { return reinterpret_cast<iterator>(&m_buf[0]); }
iterator end() noexcept { return reinterpret_cast<iterator>(&m_buf[N]); }
const_iterator cbegin() const noexcept { return reinterpret_cast<const_iterator>(&m_buf[0]); }
const_iterator cend() const noexcept { return reinterpret_cast<const_iterator>(&m_buf[N]); }
protected:
private:
/* ensure proper alignment */
PALIGNAS_VECTOROPT()
std::array<typename std::aligned_storage<sizeof(C), alignof(C)>::type, N> m_buf;
unsigned m_initialized;
};
// ----------------------------------------------------------------------------------------
// plinkedlist_t: a simple linked list
// the list allows insertions / deletions if used properly
// ----------------------------------------------------------------------------------------
#if 0
template <class LC>
class linkedlist_t
{
public:
struct element_t
/**! fixed size array allowing to override constructor and initialize members by placement new.
*
* Use with care. This template is provided to improve locality of storage
* in high frequency applications. It should not be used for anything else.
*
*/
template <class C, std::size_t N>
class uninitialised_array_t
{
public:
friend class linkedlist_t<LC>;
using iterator = C *;
using const_iterator = const C *;
constexpr element_t() : m_next(nullptr) {}
constexpr element_t(const element_t &rhs) = delete;
constexpr element_t(element_t &&rhs) = delete;
//uninitialised_array_t() noexcept = default;
uninitialised_array_t() noexcept
: m_initialized(0)
{
}
constexpr LC *next() const noexcept { return m_next; }
COPYASSIGNMOVE(uninitialised_array_t, delete)
~uninitialised_array_t() noexcept
{
if (m_initialized>=N)
for (std::size_t i=0; i<N; i++)
(*this)[i].~C();
}
size_t size() const { return N; }
C& operator[](const std::size_t &index) noexcept
{
return *reinterpret_cast<C *>(&m_buf[index]);
}
const C& operator[](const std::size_t &index) const noexcept
{
return *reinterpret_cast<const C *>(&m_buf[index]);
}
template<typename... Args>
void emplace(const std::size_t index, Args&&... args)
{
m_initialized++;
// allocate on buffer
new (&m_buf[index]) C(std::forward<Args>(args)...);
}
iterator begin() const noexcept { return reinterpret_cast<iterator>(&m_buf[0]); }
iterator end() const noexcept { return reinterpret_cast<iterator>(&m_buf[N]); }
iterator begin() noexcept { return reinterpret_cast<iterator>(&m_buf[0]); }
iterator end() noexcept { return reinterpret_cast<iterator>(&m_buf[N]); }
const_iterator cbegin() const noexcept { return reinterpret_cast<const_iterator>(&m_buf[0]); }
const_iterator cend() const noexcept { return reinterpret_cast<const_iterator>(&m_buf[N]); }
protected:
~element_t() = default;
private:
LC * m_next;
/* ensure proper alignment */
PALIGNAS_VECTOROPT()
std::array<typename std::aligned_storage<sizeof(C), alignof(C)>::type, N> m_buf;
unsigned m_initialized;
};
struct iter_t final : public std::iterator<std::forward_iterator_tag, LC>
{
private:
LC* p;
public:
explicit constexpr iter_t(LC* x) noexcept : p(x) { }
explicit constexpr iter_t(const iter_t &rhs) noexcept : p(rhs.p) { }
iter_t(iter_t &&rhs) noexcept { std::swap(*this, rhs); }
iter_t& operator=(const iter_t &rhs) { iter_t t(rhs); std::swap(*this, t); return *this; }
iter_t& operator=(iter_t &&rhs) { std::swap(*this, rhs); return *this; }
iter_t& operator++() noexcept {p = p->next();return *this;}
iter_t operator++(int) noexcept {iter_t tmp(*this); operator++(); return tmp;}
constexpr bool operator==(const iter_t& rhs) const noexcept {return p == rhs.p;}
constexpr bool operator!=(const iter_t& rhs) const noexcept {return p != rhs.p;}
/* constexpr */ LC& operator*() noexcept {return *p;}
/* constexpr */ LC* operator->() noexcept {return p;}
constexpr LC& operator*() const noexcept {return *p;}
constexpr LC* operator->() const noexcept {return p;}
};
constexpr linkedlist_t() : m_head(nullptr) {}
constexpr iter_t begin() const noexcept { return iter_t(m_head); }
constexpr iter_t end() const noexcept { return iter_t(nullptr); }
void push_front(LC *elem) noexcept
{
elem->m_next = m_head;
m_head = elem;
}
void push_back(LC *elem) noexcept
{
LC **p = &m_head;
while (*p != nullptr)
{
p = &((*p)->m_next);
}
*p = elem;
elem->m_next = nullptr;
}
void remove(const LC *elem) noexcept
{
auto p = &m_head;
while(*p != elem)
{
//nl_assert(*p != nullptr);
p = &((*p)->m_next);
}
(*p) = elem->m_next;
}
LC *front() const noexcept { return m_head; }
void clear() noexcept { m_head = nullptr; }
constexpr bool empty() const noexcept { return (m_head == nullptr); }
private:
LC *m_head;
};
#else
template <class LC>
class linkedlist_t
{
public:
struct element_t
/**! a simple linked list.
*
* the list allows insertions deletions whilst being processed.
*/
template <class LC>
class linkedlist_t
{
public:
friend class linkedlist_t<LC>;
struct element_t
{
public:
friend class linkedlist_t<LC>;
constexpr element_t() : m_next(nullptr), m_prev(nullptr) {}
~element_t() noexcept = default;
constexpr element_t() : m_next(nullptr), m_prev(nullptr) {}
~element_t() noexcept = default;
COPYASSIGNMOVE(element_t, delete)
COPYASSIGNMOVE(element_t, delete)
constexpr LC *next() const noexcept { return m_next; }
constexpr LC *prev() const noexcept { return m_prev; }
private:
LC * m_next;
LC * m_prev;
};
struct iter_t final : public std::iterator<std::forward_iterator_tag, LC>
{
private:
LC* p;
public:
explicit constexpr iter_t(LC* x) noexcept : p(x) { }
constexpr iter_t(iter_t &rhs) noexcept : p(rhs.p) { }
iter_t(iter_t &&rhs) noexcept { std::swap(*this, rhs); }
iter_t& operator=(const iter_t &rhs) noexcept { if (this != &rhs) p = rhs.p; return *this; }
iter_t& operator=(iter_t &&rhs) noexcept { std::swap(*this, rhs); return *this; }
~iter_t() = default;
iter_t& operator++() noexcept { p = p->next();return *this; }
// NOLINTNEXTLINE(cert-dcl21-cpp)
iter_t operator++(int) & noexcept { const iter_t tmp(*this); operator++(); return tmp; }
constexpr bool operator==(const iter_t& rhs) const noexcept { return p == rhs.p; }
constexpr bool operator!=(const iter_t& rhs) const noexcept { return p != rhs.p; }
C14CONSTEXPR LC& operator*() noexcept { return *p; }
C14CONSTEXPR LC* operator->() noexcept { return p; }
C14CONSTEXPR LC& operator*() const noexcept { return *p; }
C14CONSTEXPR LC* operator->() const noexcept { return p; }
};
constexpr linkedlist_t() : m_head(nullptr) {}
constexpr iter_t begin() const noexcept { return iter_t(m_head); }
constexpr iter_t end() const noexcept { return iter_t(nullptr); }
void push_front(LC *elem) noexcept
{
elem->m_next = m_head;
elem->m_prev = nullptr;
if (m_head)
m_head->m_prev = elem;
m_head = elem;
}
void push_back(LC *elem) noexcept
{
LC ** p(&m_head);
LC * prev(nullptr);
while (*p != nullptr)
{
prev = *p;
p = &((*p)->m_next);
}
*p = elem;
elem->m_prev = prev;
elem->m_next = nullptr;
}
void remove(const LC *elem) noexcept
{
if (elem->m_prev)
elem->m_prev->m_next = elem->m_next;
else
m_head = elem->m_next;
if (elem->m_next)
elem->m_next->m_prev = elem->m_prev;
else
{
/* update tail */
}
}
LC *front() const noexcept { return m_head; }
constexpr bool empty() const noexcept { return (m_head == nullptr); }
void clear() noexcept
{
LC *p(m_head);
while (p != nullptr)
{
LC *n(p->m_next);
p->m_next = nullptr;
p->m_prev = nullptr;
p = n;
}
m_head = nullptr;
}
constexpr LC *next() const noexcept { return m_next; }
constexpr LC *prev() const noexcept { return m_prev; }
private:
LC * m_next;
LC * m_prev;
LC *m_head;
};
struct iter_t final : public std::iterator<std::forward_iterator_tag, LC>
// ----------------------------------------------------------------------------------------
// FIXME: Move elsewhere
// ----------------------------------------------------------------------------------------
template<bool enabled_ = true>
class pspin_mutex
{
private:
LC* p;
public:
explicit constexpr iter_t(LC* x) noexcept : p(x) { }
constexpr iter_t(iter_t &rhs) noexcept : p(rhs.p) { }
iter_t(iter_t &&rhs) noexcept { std::swap(*this, rhs); }
iter_t& operator=(const iter_t &rhs) noexcept { p = rhs.p; return *this; }
iter_t& operator=(iter_t &&rhs) noexcept { std::swap(*this, rhs); return *this; }
iter_t& operator++() noexcept {p = p->next();return *this;}
// NOLINTNEXTLINE(cert-dcl21-cpp)
iter_t operator++(int) & noexcept {const iter_t tmp(*this); operator++(); return tmp;}
~iter_t() = default;
constexpr bool operator==(const iter_t& rhs) const noexcept {return p == rhs.p;}
constexpr bool operator!=(const iter_t& rhs) const noexcept {return p != rhs.p;}
constexpr LC& operator*() noexcept {return *p;}
constexpr LC* operator->() noexcept {return p;}
constexpr LC& operator*() const noexcept {return *p;}
constexpr LC* operator->() const noexcept {return p;}
pspin_mutex() noexcept = default;
void lock() noexcept{ while (m_lock.test_and_set(std::memory_order_acquire)) { } }
void unlock() noexcept { m_lock.clear(std::memory_order_release); }
private:
PALIGNAS_CACHELINE()
std::atomic_flag m_lock = ATOMIC_FLAG_INIT;
};
constexpr linkedlist_t() : m_head(nullptr) {}
constexpr iter_t begin() const noexcept { return iter_t(m_head); }
constexpr iter_t end() const noexcept { return iter_t(nullptr); }
void push_front(LC *elem) noexcept
template<>
class pspin_mutex<false>
{
if (m_head)
m_head->m_prev = elem;
elem->m_next = m_head;
elem->m_prev = nullptr;
m_head = elem;
}
public:
void lock() const noexcept { }
void unlock() const noexcept { }
};
void push_back(LC *elem) noexcept
// ----------------------------------------------------------------------------------------
// timed queue
// ----------------------------------------------------------------------------------------
template <class Element, class Time>
struct pqentry_t final
{
LC ** p(&m_head);
LC * prev(nullptr);
while (*p != nullptr)
constexpr pqentry_t() noexcept : m_exec_time(), m_object(nullptr) { }
constexpr pqentry_t(const Time t, const Element o) noexcept : m_exec_time(t), m_object(o) { }
#if 0
~pqentry_t() = default;
constexpr pqentry_t(const pqentry_t &e) noexcept = default;
constexpr pqentry_t(pqentry_t &&e) noexcept = default;
pqentry_t& operator=(pqentry_t && other) noexcept = default;
pqentry_t& operator=(const pqentry_t &other) noexcept = default;
void swap(pqentry_t &other) noexcept
{
prev = *p;
p = &((*p)->m_next);
std::swap(m_exec_time, other.m_exec_time);
std::swap(m_object, other.m_object);
}
*p = elem;
elem->m_prev = prev;
elem->m_next = nullptr;
}
void remove(const LC *elem) noexcept
{
if (elem->m_prev)
elem->m_prev->m_next = elem->m_next;
else
m_head = elem->m_next;
if (elem->m_next)
elem->m_next->m_prev = elem->m_prev;
else
{
/* update tail */
}
}
LC *front() const noexcept { return m_head; }
constexpr bool empty() const noexcept { return (m_head == nullptr); }
void clear() noexcept
{
LC *p(m_head);
while (p != nullptr)
{
LC *n(p->m_next);
p->m_next = nullptr;
p->m_prev = nullptr;
p = n;
}
m_head = nullptr;
}
private:
LC *m_head;
};
#endif
inline bool operator ==(const pqentry_t &rhs) const noexcept
{
return m_object == rhs.m_object;
}
inline bool operator ==(const Element &rhs) const noexcept
{
return m_object == rhs;
}
inline bool operator <=(const pqentry_t &rhs) const noexcept
{
return (m_exec_time <= rhs.m_exec_time);
}
inline bool operator <(const pqentry_t &rhs) const noexcept
{
return (m_exec_time < rhs.m_exec_time);
}
inline static constexpr pqentry_t never() noexcept { return pqentry_t(Time::never(), nullptr); }
Time m_exec_time;
Element m_object;
};
/* Use TS = true for a threadsafe queue */
template <class T, bool TS>
class timed_queue_linear : nocopyassignmove
{
public:
explicit timed_queue_linear(const std::size_t list_size)
: m_list(list_size)
{
clear();
}
std::size_t capacity() const noexcept { return m_list.capacity() - 1; }
bool empty() const noexcept { return (m_end == &m_list[1]); }
template<bool KEEPSTAT>
void push(T && e) noexcept
{
#if 0
/* Lock */
lock_guard_type lck(m_lock);
T * i(m_end-1);
for (; *i < e; --i)
{
*(i+1) = *(i);
if (KEEPSTAT)
m_prof_sortmove.inc();
}
*(i+1) = std::move(e);
++m_end;
#else
/* Lock */
lock_guard_type lck(m_lock);
T * i(m_end++);
*i = std::move(e);
for (; *(i-1) < *i; --i)
{
std::swap(*(i-1), *(i));
if (KEEPSTAT)
m_prof_sortmove.inc();
}
#endif
if (KEEPSTAT)
m_prof_call.inc();
}
T pop() noexcept { return *(--m_end); }
const T &top() const noexcept { return *(m_end-1); }
template <bool KEEPSTAT, class R>
void remove(const R &elem) noexcept
{
/* Lock */
lock_guard_type lck(m_lock);
if (KEEPSTAT)
m_prof_remove.inc();
for (T * i = m_end - 1; i > &m_list[0]; --i)
{
// == operator ignores time!
if (*i == elem)
{
std::copy(i+1, m_end--, i);
return;
}
}
}
template <bool KEEPSTAT, class R>
void retime(R && elem) noexcept
{
/* Lock */
lock_guard_type lck(m_lock);
if (KEEPSTAT)
m_prof_retime.inc();
for (R * i = m_end - 1; i > &m_list[0]; --i)
{
if (*i == elem) // partial equal!
{
*i = std::forward<R>(elem);
while (*(i-1) < *i)
{
std::swap(*(i-1), *i);
--i;
}
while (i < m_end && *i < *(i+1))
{
std::swap(*(i+1), *i);
++i;
}
return;
}
}
}
void clear() noexcept
{
lock_guard_type lck(m_lock);
m_end = &m_list[0];
/* put an empty element with maximum time into the queue.
* the insert algo above will run into this element and doesn't
* need a comparison with queue start.
*/
m_list[0] = T::never();
m_end++;
}
// save state support & mame disasm
const T *listptr() const noexcept { return &m_list[1]; }
std::size_t size() const noexcept { return static_cast<std::size_t>(m_end - &m_list[1]); }
const T & operator[](const std::size_t index) const noexcept { return m_list[ 1 + index]; }
private:
using mutex_type = pspin_mutex<TS>;
using lock_guard_type = std::lock_guard<mutex_type>;
mutex_type m_lock;
PALIGNAS_CACHELINE()
T * m_end;
aligned_vector<T> m_list;
public:
// profiling
pperfcount_t<true> m_prof_sortmove;
pperfcount_t<true> m_prof_call;
pperfcount_t<true> m_prof_remove;
pperfcount_t<true> m_prof_retime;
};
template <class T, bool TS>
class timed_queue_heap : nocopyassignmove
{
public:
struct compare
{
constexpr bool operator()(const T &a, const T &b) const { return b <= a; }
};
explicit timed_queue_heap(const std::size_t list_size)
: m_list(list_size)
{
clear();
}
std::size_t capacity() const noexcept { return m_list.capacity(); }
bool empty() const noexcept { return &m_list[0] == m_end; }
template <bool KEEPSTAT>
void push(T &&e) noexcept
{
/* Lock */
lock_guard_type lck(m_lock);
*m_end++ = e;
std::push_heap(&m_list[0], m_end, compare());
if (KEEPSTAT)
m_prof_call.inc();
}
T pop() noexcept
{
T ret(m_list[0]);
std::pop_heap(&m_list[0], m_end, compare());
m_end--;
return ret;
}
const T &top() const noexcept { return m_list[0]; }
template <bool KEEPSTAT, class R>
void remove(const R &elem) noexcept
{
/* Lock */
lock_guard_type lck(m_lock);
if (KEEPSTAT)
m_prof_remove.inc();
for (T * i = m_end - 1; i >= &m_list[0]; i--)
{
if (*i == elem)
{
m_end--;
for (;i < m_end; i++)
*i = std::move(*(i+1));
std::make_heap(&m_list[0], m_end, compare());
return;
}
}
}
template <bool KEEPSTAT>
void retime(const T &elem) noexcept
{
/* Lock */
lock_guard_type lck(m_lock);
if (KEEPSTAT)
m_prof_retime.inc();
for (T * i = m_end - 1; i >= &m_list[0]; i--)
{
if (*i == elem) // partial equal!
{
*i = elem;
std::make_heap(&m_list[0], m_end, compare());
return;
}
}
}
void clear()
{
lock_guard_type lck(m_lock);
m_list.clear();
m_end = &m_list[0];
}
// save state support & mame disasm
constexpr const T *listptr() const { return &m_list[0]; }
constexpr std::size_t size() const noexcept { return m_list.size(); }
constexpr const T & operator[](const std::size_t index) const { return m_list[ 0 + index]; }
private:
using mutex_type = pspin_mutex<TS>;
using lock_guard_type = std::lock_guard<mutex_type>;
mutex_type m_lock;
std::vector<T> m_list;
T *m_end;
public:
// profiling
pperfcount_t<true> m_prof_sortmove;
pperfcount_t<true> m_prof_call;
pperfcount_t<true> m_prof_remove;
pperfcount_t<true> m_prof_retime;
};
} // namespace plib
#endif /* PLISTS_H_ */

View File

@ -123,6 +123,12 @@ namespace plib {
}
}
static inline mempool &instance()
{
static mempool s_mempool;
return s_mempool;
}
void *allocate(size_t align, size_t size)
{
block *b = nullptr;
@ -220,6 +226,8 @@ namespace plib {
}
}
bool operator ==(const mempool &rhs) { return this == &rhs; }
};
} // namespace plib

View File

@ -261,8 +261,8 @@ ptokenizer::token_t ptokenizer::get_token_internal()
ppreprocessor::ppreprocessor(defines_map_type *defines)
: std::istream(new readbuffer(this))
, m_ifflag(0)
, m_level(0)
, m_if_flag(0)
, m_if_level(0)
, m_lineno(0)
, m_pos(0)
, m_state(PROCESS)
@ -473,34 +473,34 @@ pstring ppreprocessor::process_line(pstring line)
std::vector<pstring> lti(psplit(lt, " ", true));
if (lti[0] == "#if")
{
m_level++;
m_if_level++;
std::size_t start = 0;
lt = replace_macros(lt);
std::vector<pstring> t(psplit(replace_all(lt.substr(3), " ", ""), m_expr_sep));
auto val = static_cast<int>(expr(t, start, 255));
if (val == 0)
m_ifflag |= (1 << m_level);
m_if_flag |= (1 << m_if_level);
}
else if (lti[0] == "#ifdef")
{
m_level++;
m_if_level++;
if (get_define(lti[1]) == nullptr)
m_ifflag |= (1 << m_level);
m_if_flag |= (1 << m_if_level);
}
else if (lti[0] == "#ifndef")
{
m_level++;
m_if_level++;
if (get_define(lti[1]) != nullptr)
m_ifflag |= (1 << m_level);
m_if_flag |= (1 << m_if_level);
}
else if (lti[0] == "#else")
{
m_ifflag ^= (1 << m_level);
m_if_flag ^= (1 << m_if_level);
}
else if (lti[0] == "#endif")
{
m_ifflag &= ~(1 << m_level);
m_level--;
m_if_flag &= ~(1 << m_if_level);
m_if_level--;
}
else if (lti[0] == "#include")
{
@ -508,7 +508,7 @@ pstring ppreprocessor::process_line(pstring line)
}
else if (lti[0] == "#pragma")
{
if (m_ifflag == 0 && lti.size() > 3 && lti[1] == "NETLIST")
if (m_if_flag == 0 && lti.size() > 3 && lti[1] == "NETLIST")
{
if (lti[2] == "warning")
error("NETLIST: " + catremainder(lti, 3, " "));
@ -516,7 +516,7 @@ pstring ppreprocessor::process_line(pstring line)
}
else if (lti[0] == "#define")
{
if (m_ifflag == 0)
if (m_if_flag == 0)
{
if (lti.size() < 2)
error("PREPRO: define needs at least one argument: " + line);
@ -534,14 +534,14 @@ pstring ppreprocessor::process_line(pstring line)
}
else
{
if (m_ifflag == 0)
if (m_if_flag == 0)
error(pfmt("unknown directive on line {1}: {2}")(m_lineno)(replace_macros(line)));
}
}
else
{
lt = replace_macros(lt);
if (m_ifflag == 0)
if (m_if_flag == 0)
ret += lt;
}
return ret;

View File

@ -190,7 +190,7 @@ public:
{
m_lineno++;
line = process_line(line);
m_buf += decltype(m_buf)(line.c_str()) + static_cast<char>(10);
m_outbuf += decltype(m_outbuf)(line.c_str()) + static_cast<char>(10);
}
return *this;
}
@ -202,10 +202,10 @@ public:
: std::istream(new readbuffer(this))
, m_defines(std::move(s.m_defines))
, m_expr_sep(std::move(s.m_expr_sep))
, m_ifflag(s.m_ifflag)
, m_level(s.m_level)
, m_if_flag(s.m_if_flag)
, m_if_level(s.m_if_level)
, m_lineno(s.m_lineno)
, m_buf(std::move(s.m_buf))
, m_outbuf(std::move(s.m_outbuf))
, m_pos(s.m_pos)
, m_state(s.m_state)
, m_comment(s.m_comment)
@ -225,15 +225,14 @@ protected:
int_type underflow() override
{
//printf("here\n");
if (this->gptr() == this->egptr())
{
/* clang reports sign error - weird */
std::size_t bytes = pstring_mem_t_size(m_strm->m_buf) - static_cast<std::size_t>(m_strm->m_pos);
std::size_t bytes = pstring_mem_t_size(m_strm->m_outbuf) - static_cast<std::size_t>(m_strm->m_pos);
if (bytes > m_buf.size())
bytes = m_buf.size();
std::copy(m_strm->m_buf.c_str() + m_strm->m_pos, m_strm->m_buf.c_str() + m_strm->m_pos + bytes, m_buf.data());
std::copy(m_strm->m_outbuf.c_str() + m_strm->m_pos, m_strm->m_outbuf.c_str() + m_strm->m_pos + bytes, m_buf.data());
//printf("%ld\n", (long int)bytes);
this->setg(m_buf.data(), m_buf.data(), m_buf.data() + bytes);
@ -268,10 +267,10 @@ private:
defines_map_type m_defines;
std::vector<pstring> m_expr_sep;
std::uint_least64_t m_ifflag; // 31 if levels
int m_level;
std::uint_least64_t m_if_flag; // 31 if levels
int m_if_level;
int m_lineno;
pstring_t<pu8_traits> m_buf;
pstring_t<pu8_traits> m_outbuf;
std::istream::pos_type m_pos;
state_e m_state;
pstring m_line;

View File

@ -15,24 +15,24 @@
/*
*
* NL_PMF_TYPE_GNUC_PMF
* PMF_TYPE_GNUC_PMF
* Use standard pointer to member function syntax C++11
*
* NL_PMF_TYPE_GNUC_PMF_CONV
* PMF_TYPE_GNUC_PMF_CONV
* Use gnu extension and convert the pmf to a function pointer.
* This is not standard compliant and needs
* -Wno-pmf-conversions to compile.
*
* NL_PMF_TYPE_INTERNAL
* PMF_TYPE_INTERNAL
* Use the same approach as MAME for deriving the function pointer.
* This is compiler-dependent as well
*
* Benchmarks for ./nltool -c run -f src/mame/machine/nl_pong.cpp -t 10 -n pong_fast
*
* NL_PMF_TYPE_INTERNAL: 215% 215%
* NL_PMF_TYPE_GNUC_PMF: 163% 196%
* NL_PMF_TYPE_GNUC_PMF_CONV: 215% 215%
* NL_PMF_TYPE_VIRTUAL: 213% 209%
* PMF_TYPE_INTERNAL: 215% 215%
* PMF_TYPE_GNUC_PMF: 163% 196%
* PMF_TYPE_GNUC_PMF_CONV: 215% 215%
* PMF_TYPE_VIRTUAL: 213% 209%
*
* The whole exercise was done to avoid virtual calls. In prior versions of
* netlist, the INTERNAL and GNUC_PMF_CONV approach provided significant improvement.

View File

@ -1,7 +1,7 @@
// license:GPL-2.0+
// copyright-holders:Couriersud
/*
* nl_string.c
* pstring.cpp
*
*/

View File

@ -17,9 +17,9 @@
// noexcept on move operator -> issue with macosx clang
#define COPYASSIGNMOVE(name, def) \
name(const name &) = def; \
name(name &&) /*noexcept*/ = def; \
name(name &&) noexcept = def; \
name &operator=(const name &) = def; \
name &operator=(name &&) /*noexcept*/ = def;
name &operator=(name &&) noexcept = def;
#define COPYASSIGN(name, def) \
name(const name &) = def; \
@ -36,14 +36,14 @@ namespace plib
template<> struct is_integral<INT128> { static constexpr bool value = true; };
template<> struct numeric_limits<UINT128>
{
static constexpr UINT128 max()
static constexpr UINT128 max() noexcept
{
return ~((UINT128)0);
}
};
template<> struct numeric_limits<INT128>
{
static constexpr INT128 max()
static constexpr INT128 max() noexcept
{
return (~((UINT128)0)) >> 1;
}
@ -80,7 +80,7 @@ namespace plib
// Avoid unused variable warnings
//============================================================
template<typename... Ts>
inline void unused_var(Ts&&...) {}
inline void unused_var(Ts&&...) noexcept {}
//============================================================
// is_pow2
@ -100,7 +100,7 @@ namespace plib
template<typename T>
constexpr
typename std::enable_if<std::is_integral<T>::value && std::is_signed<T>::value, T>::type
abs(T v)
abs(T v) noexcept
{
return v < 0 ? -v : v;
}
@ -108,14 +108,14 @@ namespace plib
template<typename T>
constexpr
typename std::enable_if<std::is_integral<T>::value && std::is_unsigned<T>::value, T>::type
abs(T v)
abs(T v) noexcept
{
return v;
}
template<typename M, typename N>
constexpr typename std::common_type<M, N>::type
gcd(M m, N n)
gcd(M m, N n) noexcept
{
static_assert(std::is_integral<M>::value, "gcd: M must be an integer");
static_assert(std::is_integral<N>::value, "gcd: N must be an integer");
@ -127,7 +127,7 @@ namespace plib
template<typename M, typename N>
constexpr typename std::common_type<M, N>::type
lcm(M m, N n)
lcm(M m, N n) noexcept
{
static_assert(std::is_integral<M>::value, "lcm: M must be an integer");
static_assert(std::is_integral<N>::value, "lcm: N must be an integer");

View File

@ -31,7 +31,6 @@
#pragma GCC optimize "ivopts"
#endif
#include "netlist/nl_lists.h"
#include "netlist/nl_factory.h"
#include "nld_matrix_solver.h"
#include "nld_ms_direct.h"