diff options
-rw-r--r-- | src/lib/netlist/analog/nld_mosfet.cpp | 11 | ||||
-rw-r--r-- | src/lib/netlist/analog/nlid_fourterm.cpp | 17 | ||||
-rw-r--r-- | src/lib/netlist/analog/nlid_twoterm.h | 4 | ||||
-rw-r--r-- | src/lib/netlist/build/makefile | 2 | ||||
-rw-r--r-- | src/lib/netlist/devices/nld_4316.cpp | 3 | ||||
-rw-r--r-- | src/lib/netlist/devices/nlid_truthtable.cpp | 8 | ||||
-rw-r--r-- | src/lib/netlist/nl_base.cpp | 79 | ||||
-rw-r--r-- | src/lib/netlist/nl_base.h | 114 | ||||
-rw-r--r-- | src/lib/netlist/nl_config.h | 4 | ||||
-rw-r--r-- | src/lib/netlist/plib/pchrono.h | 6 | ||||
-rwxr-xr-x | src/lib/netlist/plib/plists.h | 114 | ||||
-rw-r--r-- | src/lib/netlist/plib/ppmf.h | 25 | ||||
-rw-r--r-- | src/lib/netlist/solver/nld_matrix_solver.cpp | 39 | ||||
-rw-r--r-- | src/lib/netlist/solver/nld_matrix_solver.h | 47 | ||||
-rw-r--r-- | src/lib/netlist/solver/nld_matrix_solver_ext.h | 9 | ||||
-rw-r--r-- | src/lib/netlist/solver/nld_solver.cpp | 129 | ||||
-rw-r--r-- | src/lib/netlist/solver/nld_solver.h | 28 | ||||
-rw-r--r-- | src/mame/audio/nl_kidniki.cpp | 2 |
18 files changed, 420 insertions, 221 deletions
diff --git a/src/lib/netlist/analog/nld_mosfet.cpp b/src/lib/netlist/analog/nld_mosfet.cpp index 0bc46adb002..acc7ad8359f 100644 --- a/src/lib/netlist/analog/nld_mosfet.cpp +++ b/src/lib/netlist/analog/nld_mosfet.cpp @@ -333,13 +333,12 @@ namespace analog NETLIB_HANDLERI(termhandler) { - // FIXME: This should never be called - if (!m_SG.P().net().is_rail_net()) - m_SG.P().solve_now(); // Basis - else if (!m_SG.N().net().is_rail_net()) - m_SG.N().solve_now(); // Emitter + // only called if connected to a rail net ==> notify the solver to recalculate + auto *solv(m_SG.solver()); + if (solv != nullptr) + solv->solve_now(); else - m_DG.N().solve_now(); // Collector + m_DG.solver()->solve_now(); } NETLIB_UPDATE_PARAMI(); NETLIB_UPDATE_TERMINALSI(); diff --git a/src/lib/netlist/analog/nlid_fourterm.cpp b/src/lib/netlist/analog/nlid_fourterm.cpp index ed3ff1ca579..14276fcb66b 100644 --- a/src/lib/netlist/analog/nlid_fourterm.cpp +++ b/src/lib/netlist/analog/nlid_fourterm.cpp @@ -32,15 +32,16 @@ NETLIB_RESET(VCCS) NETLIB_HANDLER(VCCS, termhandler) { + solver::matrix_solver_t *solv = nullptr; // only called if connected to a rail net ==> notify the solver to recalculate - if (!m_IP.net().is_rail_net()) - m_IP.solve_now(); - else if (!m_IN.net().is_rail_net()) - m_IN.solve_now(); - else if (!m_OP.net().is_rail_net()) - m_OP.solve_now(); - else if (!m_ON.net().is_rail_net()) - m_ON.solve_now(); + if ((solv = m_IP.solver()) != nullptr) + solv->solve_now(); + else if ((solv = m_IN.solver()) != nullptr) + solv->solve_now(); + else if ((solv = m_OP.solver()) != nullptr) + solv->solve_now(); + else if ((solv = m_ON.solver()) != nullptr) + solv->solve_now(); } // ---------------------------------------------------------------------------------------- diff --git a/src/lib/netlist/analog/nlid_twoterm.h b/src/lib/netlist/analog/nlid_twoterm.h index 66b6d97ffe9..4e6dae91cb1 100644 --- a/src/lib/netlist/analog/nlid_twoterm.h +++ b/src/lib/netlist/analog/nlid_twoterm.h @@ -96,11 +96,11 @@ namespace analog void solve_now() const; template <typename F> - void change_state(F f, netlist_time delay = netlist_time::quantum()) const + void change_state(F f) const { auto *solv(solver()); if (solv) - solv->change_state(f, delay); + solv->change_state(f); } void set_G_V_I(nl_fptype G, nl_fptype V, nl_fptype I) const noexcept diff --git a/src/lib/netlist/build/makefile b/src/lib/netlist/build/makefile index ce7060c4dea..e46e1ac4c26 100644 --- a/src/lib/netlist/build/makefile +++ b/src/lib/netlist/build/makefile @@ -320,7 +320,7 @@ clang-libc: -Wmissing-variable-declarations -Wno-float-equal -Wconversion \ -Wno-c++98-compat -Wno-c++98-compat-pedantic -Wformat-nonliteral \ -Wno-exit-time-destructors -Winconsistent-missing-destructor-override \ - -Wno-return-std-move-in-c++11 -Wunreachable-code" \ + -Wno-return-std-move-in-c++11 -Wno-unreachable-code" \ LDEXTRAFLAGS=-stdlib=libc++ clang-5: diff --git a/src/lib/netlist/devices/nld_4316.cpp b/src/lib/netlist/devices/nld_4316.cpp index b5b7fa80760..7c0159dbe91 100644 --- a/src/lib/netlist/devices/nld_4316.cpp +++ b/src/lib/netlist/devices/nld_4316.cpp @@ -37,8 +37,7 @@ namespace netlist { namespace devices { m_R.set_R(m_base_r()); else m_R.set_R(plib::reciprocal(exec().gmin())); - } - , NLTIME_FROM_NS(1)); + }); } private: diff --git a/src/lib/netlist/devices/nlid_truthtable.cpp b/src/lib/netlist/devices/nlid_truthtable.cpp index 8614f949f77..947b2651acb 100644 --- a/src/lib/netlist/devices/nlid_truthtable.cpp +++ b/src/lib/netlist/devices/nlid_truthtable.cpp @@ -193,8 +193,8 @@ namespace devices } - plib::uninitialised_array_t<logic_input_t, m_NI> m_I; - plib::uninitialised_array_t<logic_output_t, m_NO> m_Q; + plib::static_vector<logic_input_t, m_NI> m_I; + plib::static_vector<logic_output_t, m_NO> m_Q; #if USE_TT_ALTERNATIVE state_var<type_t> m_state; @@ -389,7 +389,7 @@ namespace devices for (std::size_t i=0; i < m_NI; i++) { inout[i] = plib::trim(inout[i]); - m_I.emplace(i, *this, inout[i], nldelegate(&NETLIB_NAME(truthtable_t)<m_NI, m_NO> :: inputs, this)); + m_I.emplace_back(*this, inout[i], nldelegate(&NETLIB_NAME(truthtable_t)<m_NI, m_NO> :: inputs, this)); } #else for (std::size_t i=0; i < m_NI; i++) @@ -412,7 +412,7 @@ namespace devices for (std::size_t i=0; i < m_NO; i++) { outputs[i] = plib::trim(outputs[i]); - m_Q.emplace(i, *this, outputs[i]); + m_Q.emplace_back(*this, outputs[i]); // Connect output "Q" to input "_Q" if this exists // This enables timed state without having explicit state .... pstring tmp = "_" + outputs[i]; diff --git a/src/lib/netlist/nl_base.cpp b/src/lib/netlist/nl_base.cpp index 078236da474..61265e9cb65 100644 --- a/src/lib/netlist/nl_base.cpp +++ b/src/lib/netlist/nl_base.cpp @@ -36,45 +36,6 @@ namespace netlist // queue_t // ---------------------------------------------------------------------------------------- - detail::queue_t::queue_t(netlist_t &nl, const pstring &name) - : timed_queue<plib::pqentry_t<netlist_time_ext, net_t *>, false>(config::MAX_QUEUE_SIZE::value) - , netlist_object_t(nl, name) - , m_qsize(0) - , m_times(config::MAX_QUEUE_SIZE::value) - , m_net_ids(config::MAX_QUEUE_SIZE::value) - { - } - - void detail::queue_t::register_state(plib::state_manager_t &manager, const pstring &module) - { - //state().log().debug("register_state\n"); - manager.save_item(this, m_qsize, module + "." + "qsize"); - manager.save_item(this, &m_times[0], module + "." + "times", m_times.size()); - manager.save_item(this, &m_net_ids[0], module + "." + "names", m_net_ids.size()); - } - - void detail::queue_t::on_pre_save(plib::state_manager_t &manager) - { - plib::unused_var(manager); - m_qsize = this->size(); - for (std::size_t i = 0; i < m_qsize; i++ ) - { - m_times[i] = this->listptr()[i].exec_time().as_raw(); - m_net_ids[i] = state().find_net_id(this->listptr()[i].object()); - } - } - - void detail::queue_t::on_post_load(plib::state_manager_t &manager) - { - plib::unused_var(manager); - this->clear(); - for (std::size_t i = 0; i < m_qsize; i++ ) - { - detail::net_t *n = state().nets()[m_net_ids[i]].get(); - this->push<false>(queue_t::entry_t(netlist_time_ext::from_raw(m_times[i]),n)); - } - } - // ---------------------------------------------------------------------------------------- // device_object_t // ---------------------------------------------------------------------------------------- @@ -101,6 +62,30 @@ namespace netlist //return terminal_type::TERMINAL; // please compiler } + netlist_state_t &detail::device_object_t::state() noexcept + { + return m_device->state(); + } + + const netlist_state_t &detail::device_object_t::state() const noexcept + { + return m_device->state(); + } + + // ---------------------------------------------------------------------------------------- + // netlist_object_t + // ---------------------------------------------------------------------------------------- + + netlist_state_t & detail::netlist_object_t::state() noexcept + { + return m_netlist.nlstate(); + } + + const netlist_state_t & detail::netlist_object_t::state() const noexcept + { + return m_netlist.nlstate(); + } + // ---------------------------------------------------------------------------------------- // netlist_t // ---------------------------------------------------------------------------------------- @@ -110,7 +95,9 @@ namespace netlist , m_solver(nullptr) , m_time(netlist_time_ext::zero()) , m_mainclock(nullptr) - , m_queue(*this, aname + "." + "m_queue") + , m_queue(config::MAX_QUEUE_SIZE::value, + detail::queue_t::id_delegate(&netlist_state_t :: find_net_id, &state), + detail::queue_t::obj_delegate(&netlist_state_t :: net_by_id, &state)) , m_use_stats(false) { state.save(*this, static_cast<plib::state_manager_t::callback_t &>(m_queue), aname, "m_queue"); @@ -175,6 +162,10 @@ namespace netlist return i; return std::numeric_limits<std::size_t>::max(); } + detail::net_t *netlist_state_t::net_by_id(std::size_t id) const + { + return m_nets[id].get(); + } void netlist_state_t::rebuild_lists() { @@ -702,14 +693,6 @@ namespace netlist state().setup().register_term(*this, *otherterm); } - void terminal_t::solve_now() const - { - const auto *solv(solver()); - // Nets may belong to railnets which do not have a solver attached - if (solv != nullptr) - solver()->solve_now(); - } - void terminal_t::set_ptrs(nl_fptype *gt, nl_fptype *go, nl_fptype *Idr) noexcept(false) { // NOLINTNEXTLINE(readability-implicit-bool-conversion) diff --git a/src/lib/netlist/nl_base.h b/src/lib/netlist/nl_base.h index 7e113214820..66caae7519c 100644 --- a/src/lib/netlist/nl_base.h +++ b/src/lib/netlist/nl_base.h @@ -356,7 +356,12 @@ namespace netlist //! Copy Constructor removed. constexpr state_var(const state_var &rhs) = delete; //! Assignment operator to assign value of a state var. - constexpr state_var &operator=(const state_var &rhs) noexcept { m_value = rhs.m_value; return *this; } // OSX doesn't like noexcept + constexpr state_var &operator=(const state_var &rhs) noexcept + { + if (this != &rhs) + m_value = rhs.m_value; + return *this; + } // OSX doesn't like noexcept //! Assignment operator to assign value of type T. constexpr state_var &operator=(const T &rhs) noexcept { m_value = rhs; return *this; } //! Assignment move operator to assign value of type T. @@ -870,11 +875,6 @@ namespace netlist } } - /// @brief Solve the system this terminal is connected to. - /// - /// \note deprecated - will be removed - void solve_now() const; // FIXME: remove this - void set_ptrs(nl_fptype *gt, nl_fptype *go, nl_fptype *Idr) noexcept(false); private: @@ -1498,35 +1498,73 @@ namespace netlist // We don't need a thread-safe queue currently. Parallel processing of // solvers will update inputs after parallel processing. - class queue_t : - //public timed_queue<pqentry_t<net_t *, netlist_time>, false, NL_KEEP_STATISTICS>, - public timed_queue<plib::pqentry_t<netlist_time_ext, net_t *>, false>, - public netlist_object_t, + template <typename O, bool TS> + class queue_base : + public timed_queue<plib::pqentry_t<netlist_time_ext, O *>, false>, public plib::state_manager_t::callback_t { public: - using entry_t = plib::pqentry_t<netlist_time_ext, net_t *>; + using entry_t = plib::pqentry_t<netlist_time_ext, O *>; using base_queue = timed_queue<entry_t, false>; - explicit queue_t(netlist_t &nl, const pstring &name); - ~queue_t() noexcept override = default; + using id_delegate = plib::pmfp<std::size_t, const O *>; + using obj_delegate = plib::pmfp<O *, std::size_t>; + + explicit queue_base(std::size_t size, id_delegate get_id, obj_delegate get_obj) + : timed_queue<plib::pqentry_t<netlist_time_ext, O *>, false>(size) + , m_qsize(0) + , m_times(size) + , m_net_ids(size) + , m_get_id(get_id) + , m_obj_by_id(get_obj) + { + } + + ~queue_base() noexcept override = default; - queue_t(const queue_t &) = delete; - queue_t(queue_t &&) = delete; - queue_t &operator=(const queue_t &) = delete; - queue_t &operator=(queue_t &&) = delete; + queue_base(const queue_base &) = delete; + queue_base(queue_base &&) = delete; + queue_base &operator=(const queue_base &) = delete; + queue_base &operator=(queue_base &&) = delete; protected: - void register_state(plib::state_manager_t &manager, const pstring &module) override; - void on_pre_save(plib::state_manager_t &manager) override; - void on_post_load(plib::state_manager_t &manager) override; + void register_state(plib::state_manager_t &manager, const pstring &module) override + { + manager.save_item(this, m_qsize, module + "." + "qsize"); + manager.save_item(this, &m_times[0], module + "." + "times", m_times.size()); + manager.save_item(this, &m_net_ids[0], module + "." + "names", m_net_ids.size()); + } + void on_pre_save(plib::state_manager_t &manager) override + { + plib::unused_var(manager); + m_qsize = this->size(); + for (std::size_t i = 0; i < m_qsize; i++ ) + { + m_times[i] = this->listptr()[i].exec_time().as_raw(); + m_net_ids[i] = m_get_id(this->listptr()[i].object()); + } + } + void on_post_load(plib::state_manager_t &manager) override + { + plib::unused_var(manager); + this->clear(); + for (std::size_t i = 0; i < m_qsize; i++ ) + { + O *n = m_obj_by_id(m_net_ids[i]); + this->template push<false>(entry_t(netlist_time_ext::from_raw(m_times[i]),n)); + } + } private: std::size_t m_qsize; std::vector<netlist_time_ext::internal_type> m_times; std::vector<std::size_t> m_net_ids; + id_delegate m_get_id; + obj_delegate m_obj_by_id; }; + using queue_t = queue_base<net_t, false>; + } // namespace detail // ----------------------------------------------------------------------------- @@ -1620,6 +1658,7 @@ namespace netlist // FIXME: only used by queue_t save state std::size_t find_net_id(const detail::net_t *net) const; + detail::net_t *net_by_id(std::size_t id) const; template <typename T> void register_net(device_arena::owned_ptr<T> &&net) { m_nets.push_back(std::move(net)); } @@ -1883,6 +1922,7 @@ namespace netlist const log_type &log() const noexcept { return m_state.log(); } void print_stats() const; + bool use_stats() const { return m_use_stats; } bool stats_enabled() const noexcept { return m_use_stats; } void enable_stats(bool val) noexcept { m_use_stats = val; } @@ -1914,7 +1954,7 @@ namespace netlist // ----------------------------------------------------------------------------- template<class C, std::size_t N> - class object_array_base_t : public plib::uninitialised_array_t<C, N> + class object_array_base_t : public plib::static_vector<C, N> { public: template<class D, typename... Args> @@ -1922,28 +1962,28 @@ namespace netlist object_array_base_t(D &dev, std::array<const char *, N> &&names, Args&&... args) { for (std::size_t i = 0; i<N; i++) - this->emplace(i, dev, pstring(names[i]), std::forward<Args>(args)...); + this->emplace_back(dev, pstring(names[i]), std::forward<Args>(args)...); } template<class D> object_array_base_t(D &dev, const pstring &fmt) { for (std::size_t i = 0; i<N; i++) - this->emplace(i, dev, formatted(fmt, i)); + this->emplace_back(dev, formatted(fmt, i)); } template<class D, typename... Args> object_array_base_t(D &dev, std::size_t offset, const pstring &fmt, Args&&... args) { for (std::size_t i = 0; i<N; i++) - this->emplace(i, dev, formatted(fmt, i+offset), std::forward<Args>(args)...); + this->emplace_back(dev, formatted(fmt, i+offset), std::forward<Args>(args)...); } template<class D> object_array_base_t(D &dev, std::size_t offset, const pstring &fmt, nldelegate delegate) { for (std::size_t i = 0; i<N; i++) - this->emplace(i, dev, formatted(fmt, i+offset), delegate); + this->emplace_back(dev, formatted(fmt, i+offset), delegate); } template<class D> @@ -1988,7 +2028,7 @@ namespace netlist object_array_t(D &dev, std::size_t offset, std::size_t qmask, const pstring &fmt, std::array<nldelegate, ND> &&delegates) { - passert_always_msg(delegates.size() >= N, "initializer_list size mismatch"); + static_assert(N <= ND, "initializer_list size mismatch"); std::size_t i = 0; for (auto &e : delegates) { @@ -1997,7 +2037,7 @@ namespace netlist pstring name(this->formatted(fmt, i+offset)); if ((qmask >> i) & 1) name += "Q"; - this->emplace(i, dev, name, e); + this->emplace_back(dev, name, e); } i++; } @@ -2437,16 +2477,6 @@ namespace netlist // inline implementations - cold // ----------------------------------------------------------------------------- - inline netlist_state_t & detail::netlist_object_t::state() noexcept - { - return m_netlist.nlstate(); - } - - inline const netlist_state_t & detail::netlist_object_t::state() const noexcept - { - return m_netlist.nlstate(); - } - template<typename T, typename... Args> inline device_arena::unique_ptr<T> detail::netlist_object_t::make_pool_object(Args&&... args) { @@ -2521,16 +2551,6 @@ namespace netlist dev = state().make_pool_object<C>(owner, name, std::forward<Args>(args)...); } - inline netlist_state_t &detail::device_object_t::state() noexcept - { - return m_device->state(); - } - - inline const netlist_state_t &detail::device_object_t::state() const noexcept - { - return m_device->state(); - } - inline solver::matrix_solver_t *analog_t::solver() const noexcept { return (this->has_net() ? net().solver() : nullptr); diff --git a/src/lib/netlist/nl_config.h b/src/lib/netlist/nl_config.h index ed8953ba842..061c77b1c6c 100644 --- a/src/lib/netlist/nl_config.h +++ b/src/lib/netlist/nl_config.h @@ -225,6 +225,10 @@ namespace netlist /// using MAX_QUEUE_SIZE = std::integral_constant<std::size_t, 512>; // NOLINT + /// \brief Maximum queue size for solvers + /// + using MAX_SOLVER_QUEUE_SIZE = std::integral_constant<std::size_t, 64>; // NOLINT + using use_float_matrix = std::integral_constant<bool, NL_USE_FLOAT_MATRIX>; using use_long_double_matrix = std::integral_constant<bool, NL_USE_LONG_DOUBLE_MATRIX>; using use_float128_matrix = std::integral_constant<bool, NL_USE_FLOAT128>; diff --git a/src/lib/netlist/plib/pchrono.h b/src/lib/netlist/plib/pchrono.h index 4a5e77d9ca2..03edeeebe6f 100644 --- a/src/lib/netlist/plib/pchrono.h +++ b/src/lib/netlist/plib/pchrono.h @@ -217,6 +217,12 @@ namespace plib { / narrow_cast<S>(T::per_second()); } guard_t guard() noexcept { return guard_t(*this); } + + // pause must be followed by cont(inue) + void pause() noexcept { m_time += T::stop(); } + void cont() noexcept { m_time -= T::start(); } + + private: type m_time; ctype m_count; diff --git a/src/lib/netlist/plib/plists.h b/src/lib/netlist/plib/plists.h index 6929a10601b..328f0b1ad23 100755 --- a/src/lib/netlist/plib/plists.h +++ b/src/lib/netlist/plib/plists.h @@ -17,14 +17,13 @@ namespace plib { - /// \brief fixed size array allowing to override constructor and initialize members by placement new. + /// \brief Array holding uninitialized elements /// /// 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 + class uninitialised_array { public: @@ -41,18 +40,10 @@ namespace plib { using const_reverse_iterator = std::reverse_iterator<const_iterator>; //uninitialised_array_t() noexcept = default; - uninitialised_array_t() noexcept - : m_initialized(0) - { - } + uninitialised_array() noexcept = default; - PCOPYASSIGNMOVE(uninitialised_array_t, delete) - ~uninitialised_array_t() noexcept - { - if (m_initialized>=N) - for (size_type i=0; i<N; ++i) - (*this)[i].~C(); - } + PCOPYASSIGNMOVE(uninitialised_array, delete) + ~uninitialised_array() noexcept = default; constexpr size_t size() const noexcept { return N; } @@ -70,34 +61,113 @@ namespace plib { return reinterpret_cast<const_reference>(m_buf[index]); } + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) + iterator begin() const noexcept { return reinterpret_cast<iterator>(&m_buf[0]); } + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) + iterator end() const noexcept { return reinterpret_cast<iterator>(&m_buf[0] + N); } + + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) + iterator begin() noexcept { return reinterpret_cast<iterator>(&m_buf[0]); } + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) + iterator end() noexcept { return reinterpret_cast<iterator>(&m_buf[0] + N); } + + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) + const_iterator cbegin() const noexcept { return reinterpret_cast<const_iterator>(&m_buf[0]); } + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) + const_iterator cend() const noexcept { return reinterpret_cast<const_iterator>(&m_buf[0] + N); } + + protected: + + private: + std::array<typename std::aligned_storage<sizeof(C), alignof(C)>::type, N> m_buf; + }; + + /// \brief fixed allocation vector + /// + /// Currently only emplace_back and clear are supported. + /// + /// 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 static_vector + { + public: + + using value_type = C; + using pointer = value_type *; + using const_pointer = const value_type *; + using reference = value_type &; + using const_reference = const value_type &; + using iterator = value_type *; + using const_iterator = const value_type *; + using size_type = std::size_t; + using difference_type = std::ptrdiff_t; + using reverse_iterator = std::reverse_iterator<iterator>; + using const_reverse_iterator = std::reverse_iterator<const_iterator>; + + static_vector() noexcept + : m_pos(0) + { + } + + PCOPYASSIGNMOVE(static_vector, delete) + ~static_vector() noexcept + { + clear(); + } + + constexpr size_t size() const noexcept { return m_pos; } + + constexpr bool empty() const noexcept { return size() == 0; } + + void clear() + { + for (size_type i=0; i<m_pos; ++i) + (*this)[i].~C(); + m_pos = 0; + } + template<typename... Args> - void emplace(size_type index, Args&&... args) + void emplace_back(Args&&... args) + { + // placement new on buffer + new (&m_buf[m_pos]) C(std::forward<Args>(args)...); + m_pos++; + } + + reference operator[](size_type index) noexcept + { + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) + return reinterpret_cast<reference>(m_buf[index]); + } + + constexpr const_reference operator[](size_type index) const noexcept { - m_initialized++; - // allocate on buffer - new (&m_buf[index]) C(std::forward<Args>(args)...); + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) + return reinterpret_cast<const_reference>(m_buf[index]); } // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) iterator begin() const noexcept { return reinterpret_cast<iterator>(&m_buf[0]); } // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) - iterator end() const noexcept { return reinterpret_cast<iterator>(&m_buf[0] + N); } + iterator end() const noexcept { return reinterpret_cast<iterator>(&m_buf[0] + m_pos); } // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) iterator begin() noexcept { return reinterpret_cast<iterator>(&m_buf[0]); } // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) - iterator end() noexcept { return reinterpret_cast<iterator>(&m_buf[0] + N); } + iterator end() noexcept { return reinterpret_cast<iterator>(&m_buf[0] + m_pos); } // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) const_iterator cbegin() const noexcept { return reinterpret_cast<const_iterator>(&m_buf[0]); } // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) - const_iterator cend() const noexcept { return reinterpret_cast<const_iterator>(&m_buf[0] + N); } + const_iterator cend() const noexcept { return reinterpret_cast<const_iterator>(&m_buf[0] + m_pos); } protected: private: std::array<typename std::aligned_storage<sizeof(C), alignof(C)>::type, N> m_buf; - unsigned m_initialized; + size_type m_pos; }; /// \brief a simple linked list. diff --git a/src/lib/netlist/plib/ppmf.h b/src/lib/netlist/plib/ppmf.h index 75e2e3b184d..6dd80758ad5 100644 --- a/src/lib/netlist/plib/ppmf.h +++ b/src/lib/netlist/plib/ppmf.h @@ -233,6 +233,9 @@ namespace plib { template <class C> using specific_member_function = R (C::*)(Targs...); + template <class C> + using const_specific_member_function = R (C::*)(Targs...) const; + using generic_member_function = specific_member_function<generic_class>; template <class C> @@ -320,6 +323,9 @@ namespace plib { template <class C> using specific_member_function = typename helper::template specific_member_function<C>; + template <class C> + using const_specific_member_function = typename helper::template const_specific_member_function<C>; + using generic_class = typename helper::generic_class; using generic_member_function = typename helper::generic_member_function; @@ -343,7 +349,17 @@ namespace plib { // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) auto *s = reinterpret_cast<std::uint8_t *>(&m_resolved); std::fill(s, s + sizeof(m_resolved), 0); - bind(object, &mftp); + bind<specific_member_function<O>>(object, &mftp); + } + + template<typename O> + pmfp_base(const_specific_member_function<O> mftp, O *object) + : m_obj(nullptr) + { + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) + auto *s = reinterpret_cast<std::uint8_t *>(&m_resolved); + std::fill(s, s + sizeof(m_resolved), 0); + bind<const_specific_member_function<O>>(object, &mftp); } bool is_set() const noexcept { return m_resolved != nullptr; } @@ -354,7 +370,7 @@ namespace plib { template<typename O> void set(specific_member_function<O> mftp, O *object) { - bind(object, &mftp); + bind<specific_member_function<O>>(object, &mftp); } inline R operator()(Targs... args) const noexcept(true) @@ -363,11 +379,10 @@ namespace plib { } private: - template<typename O, typename MF> + template<typename SPC, typename O, typename MF> void bind(O * object, MF *fraw) { - //auto pFunc = *reinterpret_cast<specific_member_function<O> *>(fraw); // mftp; - specific_member_function<O> pFunc; + SPC pFunc; static_assert(sizeof(pFunc) >= sizeof(MF), "size error"); // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) *reinterpret_cast<MF *>(&pFunc) = *fraw; diff --git a/src/lib/netlist/solver/nld_matrix_solver.cpp b/src/lib/netlist/solver/nld_matrix_solver.cpp index 0b08f198f57..871eff3dca3 100644 --- a/src/lib/netlist/solver/nld_matrix_solver.cpp +++ b/src/lib/netlist/solver/nld_matrix_solver.cpp @@ -42,6 +42,7 @@ namespace solver const net_list_t &nets, const solver_parameters_t *params) : device_t(static_cast<device_t &>(main_solver), name) + , m_next_exec(*this, "m_next_exec", netlist_time_ext::zero()) , m_params(*params) , m_iterative_fail(*this, "m_iterative_fail", 0) , m_iterative_total(*this, "m_iterative_total", 0) @@ -51,16 +52,8 @@ namespace solver , m_stat_newton_raphson_fail(*this, "m_stat_newton_raphson_fail", 0) , m_stat_vsolver_calls(*this, "m_stat_vsolver_calls", 0) , m_last_step(*this, "m_last_step", netlist_time_ext::zero()) - , m_fb_sync(*this, "FB_sync", nldelegate(&matrix_solver_t::fb_sync, this)) - , m_Q_sync(*this, "Q_sync") , m_ops(0) { - if (!this->state().setup().connect(m_fb_sync, m_Q_sync)) - { - // FIXME: avoid clang warning for now - m_main_solver.state().log().fatal(MF_ERROR_CONNECTING_1_TO_2(m_fb_sync.name(), m_Q_sync.name())); - throw nl_exception(MF_ERROR_CONNECTING_1_TO_2(m_fb_sync.name(), m_Q_sync.name())); - } setup_base(this->state().setup(), nets); // now setup the matrix @@ -72,6 +65,11 @@ namespace solver return &state().setup().get_connected_terminal(*term)->net(); } + void matrix_solver_t::reschedule(netlist_time ts) + { + m_main_solver.reschedule(this, ts); + } + void matrix_solver_t::setup_base(setup_t &setup, const net_list_t &nets) { log().debug("New solver setup\n"); @@ -495,18 +493,11 @@ namespace solver if (m_stat_newton_raphson % 100 == 0) log().warning(MW_NEWTON_LOOPS_EXCEEDED_INVOCATION_3(100, this->name(), exec().time().as_double() * 1e6)); - if (resched && !m_Q_sync.net().is_queued()) + if (resched) { // reschedule .... log().warning(MW_NEWTON_LOOPS_EXCEEDED_ON_NET_2(this->name(), exec().time().as_double() * 1e6)); - // FIXME: test and enable - this is working better, though not optimal yet -#if 0 - // Don't store, the result can not be used return netlist_time::from_fp(m_params.m_nr_recalc_delay()); -#else - //restore(); // restore old voltages - m_Q_sync.net().toggle_and_push_to_queue(netlist_time::from_fp(m_params.m_nr_recalc_delay())); -#endif } if (m_params.m_dynamic_ts) return next_time_step; @@ -514,16 +505,17 @@ namespace solver return netlist_time::from_fp(m_params.m_max_timestep); } - netlist_time matrix_solver_t::solve(netlist_time_ext now) + netlist_time matrix_solver_t::solve(netlist_time_ext now, const char *source) { netlist_time_ext delta = now - m_last_step(); PFDEBUG(printf("solve %.10f\n", delta.as_double());) + plib::unused_var(source); // We are already up to date. Avoid oscillations. // FIXME: Make this a parameter! if (delta < netlist_time_ext::quantum()) { - PFDEBUG(printf("solve return\n");) + //printf("solve return %s at %f\n", source, now.as_double()); return netlist_time::zero(); } @@ -549,9 +541,16 @@ namespace solver } if (m_params.m_dynamic_ts) - return compute_next_timestep(delta.as_fp<nl_fptype>(), m_params.m_min_timestep, m_params.m_max_timestep); + { + if (timestep_device_count() > 0) + return compute_next_timestep(delta.as_fp<nl_fptype>(), m_params.m_min_timestep, m_params.m_max_timestep); + } + + if (timestep_device_count() > 0) + return netlist_time::from_fp(m_params.m_max_timestep); + + return netlist_time::zero(); - return netlist_time::from_fp(m_params.m_max_timestep); } int matrix_solver_t::get_net_idx(const analog_net_t *net) const noexcept diff --git a/src/lib/netlist/solver/nld_matrix_solver.h b/src/lib/netlist/solver/nld_matrix_solver.h index fb63648673e..feb7d6bbd42 100644 --- a/src/lib/netlist/solver/nld_matrix_solver.h +++ b/src/lib/netlist/solver/nld_matrix_solver.h @@ -197,7 +197,7 @@ namespace solver // after every call to solve, update inputs must be called. // this can be done as well as a batch to ease parallel processing. - netlist_time solve(netlist_time_ext now); + netlist_time solve(netlist_time_ext now, const char *source); void update_inputs(); /// \brief Checks if solver may alter a net @@ -211,6 +211,12 @@ namespace solver std::size_t dynamic_device_count() const noexcept { return m_dynamic_funcs.size(); } std::size_t timestep_device_count() const noexcept { return m_step_funcs.size(); } + /// \brief reschedule solver execution + /// + /// Calls reschedule on main solver + /// + void reschedule(netlist_time ts); + /// \brief Immediately solve system at current time /// /// This should only be called from update and update_param events. @@ -221,47 +227,35 @@ namespace solver // this should only occur outside of execution and thus // using time should be safe. - const netlist_time new_timestep = solve(exec().time()); + const netlist_time new_timestep = solve(exec().time(), "solve_now"); plib::unused_var(new_timestep); update_inputs(); if (m_params.m_dynamic_ts && (timestep_device_count() != 0)) { - m_Q_sync.net().toggle_and_push_to_queue(netlist_time::from_fp(m_params.m_min_timestep)); + this->reschedule(netlist_time::from_fp(m_params.m_min_timestep)); } } template <typename F> - void change_state(F f, netlist_time delay = netlist_time::quantum()) + void change_state(F f) { // We only need to update the net first if this is a time stepping net if (timestep_device_count() > 0) { - const netlist_time new_timestep = solve(exec().time()); + const netlist_time new_timestep = solve(exec().time(), "change_state"); plib::unused_var(new_timestep); update_inputs(); } f(); - if ((delay == netlist_time::quantum()) && (timestep_device_count() > 0)) + if (timestep_device_count() > 0) { PFDEBUG(printf("here2\n");) - m_Q_sync.net().toggle_and_push_to_queue(netlist_time::from_fp(m_params.m_min_ts_ts())); + this->reschedule(netlist_time::from_fp(m_params.m_min_ts_ts())); } else - m_Q_sync.net().toggle_and_push_to_queue(delay); - } - - NETLIB_HANDLERI(fb_sync) - { - PFDEBUG(printf("update\n");) - const netlist_time new_timestep = solve(exec().time()); - update_inputs(); - - if (m_params.m_dynamic_ts && (timestep_device_count() != 0) && new_timestep > netlist_time::zero()) - { - m_Q_sync.net().toggle_and_push_to_queue(new_timestep); - } + this->reschedule(netlist_time::quantum()); } NETLIB_RESETI(); @@ -277,6 +271,8 @@ namespace solver // return number of floating point operations for solve constexpr std::size_t ops() const { return m_ops; } + state_var<netlist_time_ext> m_next_exec; + std::size_t id = 0; // FIXME - testing protected: matrix_solver_t(devices::nld_solver &main_solver, const pstring &name, const net_list_t &nets, @@ -289,6 +285,14 @@ namespace solver virtual void backup() = 0; virtual void restore() = 0; + std::size_t max_railstart() const noexcept + { + std::size_t max_rail = 0; + for (std::size_t k = 0; k < m_terms.size(); k++) + max_rail = std::max(max_rail, m_terms[k].railstart()); + return max_rail; + } + plib::pmatrix2d_vrl<fptype, arena_type> m_gonn; plib::pmatrix2d_vrl<fptype, arena_type> m_gtn; plib::pmatrix2d_vrl<fptype, arena_type> m_Idrn; @@ -339,9 +343,6 @@ namespace solver plib::aligned_vector<nldelegate_dyn> m_dynamic_funcs; plib::aligned_vector<device_arena::unique_ptr<proxied_analog_output_t>> m_inps; - logic_input_t m_fb_sync; - logic_output_t m_Q_sync; - std::size_t m_ops; plib::aligned_vector<terms_for_net_t> m_rails_temp; // setup only diff --git a/src/lib/netlist/solver/nld_matrix_solver_ext.h b/src/lib/netlist/solver/nld_matrix_solver_ext.h index b89f7dde9ce..577abbb7fd6 100644 --- a/src/lib/netlist/solver/nld_matrix_solver_ext.h +++ b/src/lib/netlist/solver/nld_matrix_solver_ext.h @@ -56,15 +56,6 @@ namespace solver //PALIGNAS_VECTOROPT() parrays define alignment already plib::pmatrix2d<float_type *> m_mat_ptr; - std::size_t max_railstart() const noexcept - { - std::size_t max_rail = 0; - for (std::size_t k = 0; k < m_terms.size(); k++) - max_rail = std::max(max_rail, m_terms[k].railstart()); - return max_rail; - } - - template <typename T, typename M> void log_fill(const T &fill, M &mat) { diff --git a/src/lib/netlist/solver/nld_solver.cpp b/src/lib/netlist/solver/nld_solver.cpp index d756ecbd8f2..45f8a389b51 100644 --- a/src/lib/netlist/solver/nld_solver.cpp +++ b/src/lib/netlist/solver/nld_solver.cpp @@ -17,6 +17,7 @@ #include "nld_ms_w.h" #include "nld_solver.h" #include "plib/pomp.h" +#include "plib/ptimed_queue.h" #include <algorithm> #include <type_traits> @@ -32,8 +33,12 @@ namespace devices NETLIB_RESET(solver) { + if (exec().use_stats()) + m_fb_step.set_delegate(NETLIB_DELEGATE(fb_step<true>)); for (auto &s : m_mat_solvers) s->reset(); + for (auto &s : m_mat_solvers) + m_queue.push<false>({netlist_time_ext::zero(), s.get()}); } void NETLIB_NAME(solver)::stop() @@ -42,6 +47,91 @@ namespace devices s->log_stats(); } +#if 1 + + template<bool KEEP_STATS> + NETLIB_HANDLER(solver, fb_step) + { + const netlist_time_ext now(exec().time()); + const std::size_t nthreads = m_params.m_parallel() < 2 ? 1 : std::min(static_cast<std::size_t>(m_params.m_parallel()), plib::omp::get_max_threads()); + const netlist_time_ext sched(now + (nthreads <= 1 ? netlist_time_ext::zero() : netlist_time_ext::from_usec(100))); + plib::uninitialised_array<solver::matrix_solver_t *, config::MAX_SOLVER_QUEUE_SIZE::value> tmp; + plib::uninitialised_array<netlist_time, config::MAX_SOLVER_QUEUE_SIZE::value> nt; + std::size_t p=0; + + while (m_queue.size() > 0) + { + auto t = m_queue.top().exec_time(); + auto o = m_queue.top().object(); + if (t != now) + if (t > sched) + break; + tmp[p++] = o; + m_queue.pop(); + } + + // FIXME: Disabled for now since parallel processing will decrease performance + // for tested applications. More testing required here + if (1 || nthreads < 2) + { + if (!KEEP_STATS) + { + for (std::size_t i = 0; i < p; i++) + nt[i] = tmp[i]->solve(now, "no-parallel"); + } + else + { + stats()->m_stat_total_time.pause(); + for (std::size_t i = 0; i < p; i++) + { + tmp[i]->stats()->m_stat_call_count.inc(); + auto g(tmp[i]->stats()->m_stat_total_time.guard()); + nt[i] = tmp[i]->solve(now, "no-parallel"); + } + stats()->m_stat_total_time.cont(); + } + + for (std::size_t i = 0; i < p; i++) + { + if (nt[i] != netlist_time::zero()) + m_queue.push<false>({now + nt[i], tmp[i]}); + tmp[i]->update_inputs(); + } + } + else + { + plib::omp::set_num_threads(nthreads); + plib::omp::for_static(static_cast<std::size_t>(0), p, [&tmp, &nt,now](std::size_t i) + { + nt[i] = tmp[i]->solve(now, "parallel"); + }); + for (std::size_t i = 0; i < p; i++) + { + if (nt[i] != netlist_time::zero()) + m_queue.push<false>({now + nt[i], tmp[i]}); + tmp[i]->update_inputs(); + } + } + if (m_queue.size() > 0) + m_Q_step.net().toggle_and_push_to_queue(m_queue.top().exec_time() - now); + } + + void NETLIB_NAME(solver) :: reschedule(solver::matrix_solver_t *solv, netlist_time ts) + { + const netlist_time_ext now(exec().time()); + const netlist_time_ext sched(now + ts); + m_queue.remove<false>(solv); + m_queue.push<false>({sched, solv}); + + if (m_Q_step.net().is_queued()) + { + if (m_Q_step.net().next_scheduled_time() > sched) + m_Q_step.net().toggle_and_push_to_queue(ts); + } + else + m_Q_step.net().toggle_and_push_to_queue(ts); + } +#else NETLIB_HANDLER(solver, fb_step) { if (m_params.m_dynamic_ts) @@ -54,26 +144,26 @@ namespace devices std::size_t nthreads = std::min(static_cast<std::size_t>(m_params.m_parallel()), plib::omp::get_max_threads()); - std::vector<solver::matrix_solver_t *> &solvers = (force_solve ? m_mat_solvers_all : m_mat_solvers_timestepping); + std::vector<solver_entry *> &solvers = (force_solve ? m_mat_solvers_all : m_mat_solvers_timestepping); if (nthreads > 1 && solvers.size() > 1) { plib::omp::set_num_threads(nthreads); plib::omp::for_static(static_cast<std::size_t>(0), solvers.size(), [&solvers, now](std::size_t i) { - const netlist_time ts = solvers[i]->solve(now); + const netlist_time ts = solvers[i]->ptr->solve(now); plib::unused_var(ts); }); } else for (auto & solver : solvers) { - const netlist_time ts = solver->solve(now); + const netlist_time ts = solver->ptr->solve(now); plib::unused_var(ts); } for (auto & solver : solvers) - solver->update_inputs(); + solver->ptr->update_inputs(); // step circuit if (!m_Q_step.net().is_queued()) @@ -81,8 +171,7 @@ namespace devices m_Q_step.net().toggle_and_push_to_queue(netlist_time::from_fp(m_params.m_max_timestep)); } } - - //using solver_ptr = host_arena::unique_ptr<solver::matrix_solver_t>; +#endif // FIXME: should be created in device space template <class C> @@ -90,8 +179,7 @@ namespace devices NETLIB_NAME(solver)::net_list_t &nets, solver::solver_parameters_t ¶ms, std::size_t size) { - return plib::make_unique<C, host_arena>(main_solver, name, nets, ¶ms, size); - //return nl.make_pool_object<C>(nl, name, nets, ¶ms, size); + return plib::make_unique<C, device_arena>(main_solver, name, nets, ¶ms, size); } template <typename FT, int SIZE> @@ -140,10 +228,10 @@ namespace devices switch (net_count) { case 1: - return plib::make_unique<solver::matrix_solver_direct1_t<FT>, host_arena>(*this, sname, nets, &m_params); + return plib::make_unique<solver::matrix_solver_direct1_t<FT>, device_arena>(*this, sname, nets, &m_params); //return state().make_pool_object<solver::matrix_solver_direct1_t<FT>>(state(), sname, nets, &m_params); case 2: - return plib::make_unique<solver::matrix_solver_direct2_t<FT>, host_arena>(*this, sname, nets, &m_params); + return plib::make_unique<solver::matrix_solver_direct2_t<FT>, device_arena>(*this, sname, nets, &m_params); //return state().make_pool_object<solver::matrix_solver_direct2_t<FT>>(state(), sname, nets, &m_params); case 3: return create_solver<FT, 3>(3, sname, nets); @@ -338,12 +426,9 @@ namespace devices } } - m_mat_solvers_all.push_back(ms.get()); - if (ms->timestep_device_count() != 0) - m_mat_solvers_timestepping.push_back(ms.get()); - - m_mat_solvers.emplace_back(std::move(ms)); + m_mat_solvers.push_back(std::move(ms)); } + } solver::static_compile_container NETLIB_NAME(solver)::create_solver_code(solver::static_compile_target target) @@ -358,6 +443,20 @@ namespace devices return mp; } + std::size_t NETLIB_NAME(solver)::get_solver_id(const solver::matrix_solver_t *net) const + { + for (std::size_t i=0; i < m_mat_solvers.size(); i++) + if (m_mat_solvers[i].get() == net) + return i; + return std::numeric_limits<std::size_t>::max(); + } + + solver::matrix_solver_t * NETLIB_NAME(solver)::solver_by_id(std::size_t id) const + { + return m_mat_solvers[id].get(); + } + + NETLIB_DEVICE_IMPL(solver, "SOLVER", "FREQ") } // namespace devices diff --git a/src/lib/netlist/solver/nld_solver.h b/src/lib/netlist/solver/nld_solver.h index 315c1e81d3b..269be1cd5ff 100644 --- a/src/lib/netlist/solver/nld_solver.h +++ b/src/lib/netlist/solver/nld_solver.h @@ -26,12 +26,19 @@ namespace devices { NETLIB_OBJECT(solver) { + public: + using queue_type = detail::queue_base<solver::matrix_solver_t, false>; + NETLIB_CONSTRUCTOR(solver) - , m_fb_step(*this, "FB_step", NETLIB_DELEGATE(fb_step)) + , m_fb_step(*this, "FB_step", NETLIB_DELEGATE(fb_step<false>)) , m_Q_step(*this, "Q_step") , m_params(*this) + , m_queue(config::MAX_SOLVER_QUEUE_SIZE::value, + queue_type::id_delegate(&NETLIB_NAME(solver) :: get_solver_id, this), + queue_type::obj_delegate(&NETLIB_NAME(solver) :: solver_by_id, this)) { - // internal staff + // internal stuff + state().save(*this, static_cast<plib::state_manager_t::callback_t &>(m_queue), this->name(), "m_queue"); connect(m_fb_step, m_Q_step); } @@ -46,11 +53,14 @@ namespace devices NETLIB_RESETI(); // NETLIB_UPDATE_PARAMI(); - //using solver_ptr = device_arena::unique_ptr<solver::matrix_solver_t>; - using solver_ptr = host_arena::unique_ptr<solver::matrix_solver_t>; + using solver_ptr = device_arena::unique_ptr<solver::matrix_solver_t>; + //using solver_ptr = host_arena::unique_ptr<solver::matrix_solver_t>; using net_list_t = solver::matrix_solver_t::net_list_t; + void reschedule(solver::matrix_solver_t *solv, netlist_time ts); + private: + template<bool KEEP_STATS> NETLIB_HANDLERI(fb_step); logic_input_t m_fb_step; @@ -58,18 +68,20 @@ namespace devices // FIXME: these should be created in device space std::vector<solver_ptr> m_mat_solvers; - std::vector<solver::matrix_solver_t *> m_mat_solvers_all; - std::vector<solver::matrix_solver_t *> m_mat_solvers_timestepping; solver::solver_parameters_t m_params; + queue_type m_queue; template <typename FT, int SIZE> solver_ptr create_solver(std::size_t size, const pstring &solvername, net_list_t &nets); template <typename FT> - solver_ptr create_solvers( - const pstring &sname, net_list_t &nets); + solver_ptr create_solvers(const pstring &sname, net_list_t &nets); + + std::size_t get_solver_id(const solver::matrix_solver_t *net) const; + solver::matrix_solver_t *solver_by_id(std::size_t id) const; + }; } // namespace devices diff --git a/src/mame/audio/nl_kidniki.cpp b/src/mame/audio/nl_kidniki.cpp index f30657a2086..21913be71f6 100644 --- a/src/mame/audio/nl_kidniki.cpp +++ b/src/mame/audio/nl_kidniki.cpp @@ -385,7 +385,7 @@ NETLIST_START(kidniki) #endif #if (USE_FRONTIERS) - PARAM(Solver.PARALLEL, 2) // More does not help + PARAM(Solver.PARALLEL, 3) // More does not help #else PARAM(Solver.PARALLEL, 0) #endif |