netlist: improve performance up to 65% on audio netlists. [Couriersud]

This commit introduces precompiled static solver code. Due to
additional optimizations the compiler can use because the detail
calculation steps for the solution are known e.g. the kidniki netlist
sees a 100% speed increase.

In all environments (windows/*nix/osx) the source for the static
solver code can be created using
	bash src/lib/netlist/nl_create_mame_solvers.sh

This will create src/lib/netlist/generated/static_solvers.cpp which is
compiled into the mame binary.
The script is just a temporary workaround. The intention is that nltool
whill be able to create this file with one call.

There are other improvements in this commit speeding up the processing
of timestep and dynamic calculations.
This commit is contained in:
couriersud 2020-04-13 21:32:00 +02:00
parent d4093c59db
commit 5b6013caea
24 changed files with 10263 additions and 127 deletions

View File

@ -233,4 +233,6 @@ project "netlist"
MAME_DIR .. "src/lib/netlist/macro/nlm_opamp.h",
MAME_DIR .. "src/lib/netlist/macro/nlm_other.cpp",
MAME_DIR .. "src/lib/netlist/macro/nlm_other.h",
MAME_DIR .. "src/lib/netlist/generated/static_solvers.cpp",
}

View File

@ -462,6 +462,10 @@ includedirs {
MAME_DIR .. "src/lib/netlist",
}
defines {
"NL_DISABLE_DYNAMIC_LOAD=1",
}
files {
MAME_DIR .. "src/lib/netlist/prg/nltool.cpp",
}

View File

@ -52,7 +52,6 @@ DEFINE_DEVICE_TYPE(NETLIST_LOGIC_OUTPUT, netlist_mame_logic_output_device, "nl
DEFINE_DEVICE_TYPE(NETLIST_ANALOG_OUTPUT, netlist_mame_analog_output_device, "nl_analog_out", "Netlist Analog Output")
DEFINE_DEVICE_TYPE(NETLIST_STREAM_OUTPUT, netlist_mame_stream_output_device, "nl_stream_out", "Netlist Stream Output")
// ----------------------------------------------------------------------------------------
// Special netlist extension devices ....
// ----------------------------------------------------------------------------------------
@ -88,7 +87,6 @@ protected:
m_parent.logerror("netlist ERROR: %s\n", ls.c_str());
break;
case plib::plog_level::FATAL:
//throw emu_fatalerror(1, "netlist FATAL: %s\n", ls.c_str());
m_parent.logerror("netlist FATAL: %s\n", ls.c_str());
break;
}
@ -128,10 +126,14 @@ protected:
case plib::plog_level::FATAL:
osd_printf_error("netlist FATAL: %s\n", ls);
break;
//throw emu_fatalerror(1, "netlist FATAL: %s\n", ls.c_str());
}
}
plib::unique_ptr<plib::dynlib_base> static_solver_lib() const noexcept override
{
return plib::make_unique<plib::dynlib_static>(nullptr);
}
private:
};

View File

@ -124,6 +124,29 @@ namespace analog
nl_fptype m_gmin;
};
// Constant model for constant capacitor model
// "Circuit simulation", page 274
struct generic_capacitor_const
{
public:
generic_capacitor_const(device_t &dev, const pstring &name)
: m_gmin(nlconst::zero())
{
}
// Returns { G, Ieq }
std::pair<nl_fptype, nl_fptype> timestep(nl_fptype cap, nl_fptype v, nl_fptype step) const noexcept
{
const nl_fptype h(plib::reciprocal(step));
const nl_fptype G(cap * h + m_gmin);
return { G, - G * v };
}
void setparams(nl_fptype gmin) noexcept { m_gmin = gmin; }
private:
nl_fptype m_gmin;
};
// -----------------------------------------------------------------------------
// A generic diode model to be used in other devices (Diode, BJT ...)
// -----------------------------------------------------------------------------
@ -155,9 +178,9 @@ namespace analog
, nlconst::magic(1)
, nlconst::magic(1e-15)
, nlconst::magic(300.0));
m_name = name;
//m_name = name;
}
pstring m_name;
//pstring m_name;
// Basic math
//
// I(V) = f(V)

View File

@ -164,8 +164,8 @@ namespace analog
NETLIB_UPDATE_TERMINALS(D)
{
m_D.update_diode(deltaV());
const nl_fptype G = m_D.G();
const nl_fptype I = m_D.Ieq();
const nl_fptype G(m_D.G());
const nl_fptype I(m_D.Ieq());
set_mat( G, -G, -I,
-G, G, I);
//set(m_D.G(), 0.0, m_D.Ieq());

View File

@ -245,7 +245,44 @@ namespace analog
// -----------------------------------------------------------------------------
// nld_C
// -----------------------------------------------------------------------------
#if 1
NETLIB_OBJECT_DERIVED(C, twoterm)
{
public:
NETLIB_CONSTRUCTOR_DERIVED(C, twoterm)
, m_C(*this, "C", nlconst::magic(1e-6))
, m_cap(*this, "m_cap")
{
}
NETLIB_IS_TIMESTEP(true)
NETLIB_TIMESTEPI()
{
// G, Ieq
const auto res(m_cap.timestep(m_C(), deltaV(), step));
const nl_fptype G = res.first;
const nl_fptype I = res.second;
set_mat( G, -G, -I,
-G, G, I);
}
NETLIB_RESETI()
{
m_cap.setparams(exec().gmin());
}
param_fp_t m_C;
protected:
//NETLIB_UPDATEI();
//FIXME: should be able to change
NETLIB_UPDATE_PARAMI() { }
private:
generic_capacitor_const m_cap;
};
#else
// Code preserved as a basis for a current/voltage controlled capacitor
NETLIB_OBJECT_DERIVED(C, twoterm)
{
public:
@ -292,7 +329,7 @@ namespace analog
//generic_capacitor<capacitor_e::VARIABLE_CAPACITY> m_cap;
generic_capacitor<capacitor_e::CONSTANT_CAPACITY> m_cap;
};
#endif
// -----------------------------------------------------------------------------
// nld_L
// -----------------------------------------------------------------------------

View File

@ -74,7 +74,7 @@ namespace netlist
log().warning(MW_FREQUENCY_OUTSIDE_OF_SPECS_1(m_FREQ()));
m_shift = 0x1ffff;
m_is_timestep = m_RV.m_P.net().solver()->has_timestep_devices();
m_is_timestep = (m_RV.m_P.net().solver()->timestep_device_count() > 0);
}
NETLIB_UPDATE_PARAM(MM5837_dip)

View File

@ -82,7 +82,7 @@ namespace netlist
m_last_state = 1;
m_RVI.reset();
m_RVO.reset();
m_is_timestep = m_RVO.m_P.net().solver()->has_timestep_devices();
m_is_timestep = (m_RVO.m_P.net().solver()->timestep_device_count() > 0);
m_RVI.set_G_V_I(plib::reciprocal(m_model.m_RI()), m_model.m_VI, nlconst::zero());
m_RVO.set_G_V_I(plib::reciprocal(m_model.m_ROL()), m_model.m_VOL, nlconst::zero());
}

View File

@ -145,7 +145,7 @@ namespace netlist
m_last_state = -1;
m_RN.reset();
m_RP.reset();
m_is_timestep = m_RN.m_P.net().solver()->has_timestep_devices();
m_is_timestep = (m_RN.m_P.net().solver()->timestep_device_count() > 0);
m_RN.set_G_V_I(plib::reciprocal(logic_family()->R_low()),
logic_family()->low_offset_V(), nlconst::zero());
m_RP.set_G_V_I(G_OFF,

File diff suppressed because it is too large Load Diff

View File

@ -18,9 +18,20 @@
#include <limits>
// ----------------------------------------------------------------------------------------
// Statically compiled solvers for mame netlist
// ----------------------------------------------------------------------------------------
extern plib::dynlib_static_sym nl_static_syms[];
namespace netlist
{
plib::unique_ptr<plib::dynlib_base> callbacks_t:: static_solver_lib() const
{
return plib::make_unique<plib::dynlib_static>(nl_static_syms);
}
// ----------------------------------------------------------------------------------------
// logic_family_ttl_t
// ----------------------------------------------------------------------------------------
@ -201,8 +212,8 @@ namespace netlist
, m_extended_validation(false)
, m_dummy_version(1)
{
pstring libpath = plib::util::environment("NL_BOOSTLIB", plib::util::buildpath({".", "nlboost.so"}));
m_lib = plib::make_unique<plib::dynlib>(libpath);
m_lib = m_callbacks->static_solver_lib();
m_setup = plib::make_unique<setup_t>(*this);
// create the run interface
@ -795,9 +806,9 @@ namespace netlist
terminal_t::terminal_t(core_device_t &dev, const pstring &aname, terminal_t *otherterm)
: analog_t(dev, aname, STATE_BIDIR)
, m_Idr1(nullptr)
, m_go1(nullptr)
, m_gt1(nullptr)
, m_Idr(nullptr)
, m_go(nullptr)
, m_gt(nullptr)
{
state().setup().register_term(*this, *otherterm);
}

View File

@ -187,6 +187,8 @@ namespace netlist
/// \brief Delegate type for device notification.
///
using nldelegate = plib::pmfp<void>;
using nldelegate_ts = plib::pmfp<void, nl_fptype>;
using nldelegate_dyn = plib::pmfp<void>;
// -----------------------------------------------------------------------------
// forward definitions
@ -825,11 +827,11 @@ namespace netlist
void set_go_gt_I(nl_fptype GO, nl_fptype GT, nl_fptype I) const noexcept
{
// Check for rail nets ...
if (m_go1 != nullptr)
if (m_go != nullptr)
{
*m_Idr1 = I;
*m_go1 = GO;
*m_gt1 = GT;
*m_Idr = I;
*m_go = GO;
*m_gt = GT;
}
}
@ -837,11 +839,11 @@ namespace netlist
void schedule_solve_after(netlist_time after) noexcept;
void set_ptrs(nl_fptype *gt, nl_fptype *go, nl_fptype *Idr) noexcept(false);
private:
nl_fptype *m_Idr1; // drive current
nl_fptype *m_go1; // conductance for Voltage from other term
nl_fptype *m_gt1; // conductance for total conductance
private:
nl_fptype *m_Idr; // drive current
nl_fptype *m_go; // conductance for Voltage from other term
nl_fptype *m_gt; // conductance for total conductance
};
@ -1430,7 +1432,7 @@ namespace netlist
log_type & log() noexcept { return m_log; }
const log_type &log() const noexcept { return m_log; }
plib::dynlib &lib() const noexcept { return *m_lib; }
plib::dynlib_base &lib() const noexcept { return *m_lib; }
netlist_t &exec() noexcept { return *m_netlist; }
const netlist_t &exec() const noexcept { return *m_netlist; }
@ -1576,7 +1578,7 @@ namespace netlist
pstring m_name;
unique_pool_ptr<netlist_t> m_netlist;
plib::unique_ptr<plib::dynlib> m_lib; // external lib needs to be loaded as long as netlist exists
plib::unique_ptr<plib::dynlib_base> m_lib; // external lib needs to be loaded as long as netlist exists
plib::state_manager_t m_state;
plib::unique_ptr<callbacks_t> m_callbacks;
log_type m_log;
@ -1951,9 +1953,9 @@ namespace netlist
throw nl_exception("Inconsistent nullptrs for terminal {}", name());
}
m_gt1 = gt;
m_go1 = go;
m_Idr1 = Idr;
m_gt = gt;
m_go = go;
m_Idr = Idr;
}
inline logic_net_t & logic_t::net() noexcept

View File

@ -0,0 +1,40 @@
OUTDIR=/tmp/static_sysms
GENERATED=src/lib/netlist/generated/static_solvers.cpp
if [ _$OS = "_Windows_NT" ]; then
NLTOOL=./nltool.exe
CXX=g++.exe
else
NLTOOL=./nltool
CXX=g++
fi
rm -rf $OUTDIR
mkdir $OUTDIR
for i in src/mame/machine/nl_*.cpp src/mame/audio/nl_*.cpp; do
nn=`basename $i .cpp | sed -e "s/nl_//g"`
cn=`grep "^//NL_CONTAINS" $i | cut -f2-99 "-d "`
if [ _"$cn" != _"" ]; then
nn=$cn
fi
for j in $nn; do
echo $i : $j
if [ $j != "pongdoubles" ]; then
$NLTOOL -c static -f $i -n $j -t 6 --dir $OUTDIR
fi
done
done
# $CXX -shared -x c++ -fPIC -O3 -march=native -mtune=native -ffast-math $OUTDIR/*.c -o nlboost.so
echo '#include "plib/pdynlib.h"' > $GENERATED
cat $OUTDIR/*.c | sed -e 's/extern "C"/static/' >> $GENERATED
echo 'plib::dynlib_static_sym nl_static_syms[] = {' >> $GENERATED
for i in $OUTDIR/*.c; do
n=`basename $i .c`
echo '{ "'${n}'", reinterpret_cast<void *>(&'${n}')},' >> $GENERATED
done
echo '{"", nullptr}' >> $GENERATED
echo '};' >> $GENERATED

View File

@ -20,6 +20,7 @@
#include "plib/pstring.h"
#include "plib/ptime.h"
#include "plib/putil.h"
#include "plib/pdynlib.h"
#include <unordered_map>
@ -57,6 +58,9 @@ namespace netlist
///
virtual void vlog(const plib::plog_level &l, const pstring &ls) const noexcept = 0;
/// \brief provide library with static solver implementations.
///
virtual plib::unique_ptr<plib::dynlib_base> static_solver_lib() const;
};
using log_type = plib::plog_base<callbacks_t, NL_DEBUG>;

View File

@ -55,7 +55,7 @@ WCHAR *wstring_from_utf8(const char *utf8string)
namespace plib {
dynlib::dynlib(const pstring &libname)
: m_isLoaded(false), m_lib(nullptr)
: m_lib(nullptr)
{
#ifdef _WIN32
//fprintf(stderr, "win: loading <%s>\n", libname.c_str());
@ -65,7 +65,7 @@ dynlib::dynlib(const pstring &libname)
else
m_lib = GetModuleHandle(nullptr);
if (m_lib != nullptr)
m_isLoaded = true;
m_is_loaded = true;
//else
// fprintf(stderr, "win: library <%s> not found!\n", libname.c_str());
delete [] buffer;
@ -78,14 +78,14 @@ dynlib::dynlib(const pstring &libname)
else
m_lib = dlopen(nullptr, RTLD_LAZY);
if (m_lib != nullptr)
m_isLoaded = true;
m_is_loaded = true;
//else
// printf("library <%s> not found: %s\n", libname.c_str(), dlerror());
#endif
}
dynlib::dynlib(const pstring &path, const pstring &libname)
: m_isLoaded(false), m_lib(nullptr)
: m_lib(nullptr)
{
// FIXME: implement path search
plib::unused_var(path);
@ -97,7 +97,7 @@ dynlib::dynlib(const pstring &path, const pstring &libname)
else
m_lib = GetModuleHandle(nullptr);
if (m_lib != nullptr)
m_isLoaded = true;
m_is_loaded = true;
else
{
//printf("win: library <%s> not found!\n", libname.c_str());
@ -112,7 +112,7 @@ dynlib::dynlib(const pstring &path, const pstring &libname)
else
m_lib = dlopen(nullptr, RTLD_LAZY);
if (m_lib != nullptr)
m_isLoaded = true;
m_is_loaded = true;
else
{
//printf("library <%s> not found!\n", libname.c_str());

View File

@ -12,62 +12,108 @@
#include "ptypes.h"
namespace plib {
// ----------------------------------------------------------------------------------------
// pdynlib: dynamic loading of libraries ...
// ----------------------------------------------------------------------------------------
// ----------------------------------------------------------------------------------------
// pdynlib: dynamic loading of libraries ...
// ----------------------------------------------------------------------------------------
class dynlib : public nocopyassignmove
{
public:
explicit dynlib(const pstring &libname);
dynlib(const pstring &path, const pstring &libname);
~dynlib();
COPYASSIGNMOVE(dynlib, delete)
bool isLoaded() const { return m_isLoaded; }
template <typename T>
T getsym(const pstring &name) const noexcept
class dynlib_base : public nocopyassignmove
{
return reinterpret_cast<T>(getsym_p(name));
}
private:
void *getsym_p(const pstring &name) const noexcept;
public:
explicit dynlib_base() : m_is_loaded(false) { }
bool m_isLoaded;
void *m_lib;
};
virtual ~dynlib_base() { }
COPYASSIGNMOVE(dynlib_base, delete)
template <typename R, typename... Args>
class dynproc
{
public:
using calltype = R(*) (Args... args);
bool isLoaded() const { return m_is_loaded; }
dynproc() : m_sym(nullptr) { }
template <typename T>
T getsym(const pstring &name) const noexcept
{
return reinterpret_cast<T>(getsym_p(name));
}
dynproc(dynlib &dl, const pstring &name) noexcept
protected:
bool m_is_loaded;
virtual void *getsym_p(const pstring &name) const noexcept = 0;
};
class dynlib : public dynlib_base
{
m_sym = dl.getsym<calltype>(name);
}
public:
explicit dynlib(const pstring &libname);
dynlib(const pstring &path, const pstring &libname);
void load(dynlib &dl, const pstring &name) noexcept
~dynlib();
protected:
void *getsym_p(const pstring &name) const noexcept override;
private:
void *m_lib;
};
struct dynlib_static_sym
{
m_sym = dl.getsym<calltype>(name);
}
const char *name;
void *addr;
};
R operator ()(Args&&... args) const
class dynlib_static : public dynlib_base
{
return m_sym(std::forward<Args>(args)...);
//return m_sym(args...);
}
public:
explicit dynlib_static(const dynlib_static_sym *syms)
: m_syms(syms)
{
if (syms != nullptr)
m_is_loaded = true;
}
bool resolved() const noexcept { return m_sym != nullptr; }
private:
calltype m_sym;
};
protected:
void *getsym_p(const pstring &name) const noexcept override
{
const dynlib_static_sym *p = m_syms;
while (p->name[0] != 0)
{
if (name == pstring(p->name))
return p->addr;
p++;
}
return nullptr;
}
private:
const dynlib_static_sym *m_syms;
};
template <typename R, typename... Args>
class dynproc
{
public:
using calltype = R(*) (Args... args);
dynproc() : m_sym(nullptr) { }
dynproc(dynlib_base &dl, const pstring &name) noexcept
{
m_sym = dl.getsym<calltype>(name);
}
void load(dynlib_base &dl, const pstring &name) noexcept
{
m_sym = dl.getsym<calltype>(name);
}
R operator ()(Args&&... args) const
{
return m_sym(std::forward<Args>(args)...);
//return m_sym(args...);
}
bool resolved() const noexcept { return m_sym != nullptr; }
private:
calltype m_sym;
};
} // namespace plib
#endif // PSTRING_H_
#endif // PDYNLIB_H_

View File

@ -67,6 +67,20 @@ namespace plib
return (*this)[r][c];
}
T * data() noexcept
{
return m_v.data();
}
const T * data() const noexcept
{
return m_v.data();
}
size_type didx(size_type r, size_type c) const noexcept
{
return m_stride * r + c;
}
private:
size_type m_N;

View File

@ -287,6 +287,19 @@ namespace plib
const std::string &token,
std::size_t maxsplit);
// ----------------------------------------------------------------------------------------
// simple hash
// ----------------------------------------------------------------------------------------
template <typename T>
std::size_t hash(const T *buf, std::size_t size)
{
std::size_t result = 5381;
for (const T* p = buf; p != buf + size; p++)
result = ((result << 5) + result ) ^ (result >> (32 - 5)) ^ static_cast<std::size_t>(*p);
return result;
}
//============================================================
// penum - strongly typed enumeration
//============================================================

View File

@ -23,6 +23,11 @@
#include <ios>
#include <iostream> // scanf
#ifndef NL_DISABLE_DYNAMIC_LOAD
#define NL_DISABLE_DYNAMIC_LOAD 0
#endif
class tool_app_t : public plib::app
{
public:
@ -47,7 +52,8 @@ public:
opt_dir(*this, "d", "dir", "", "output directory for the generated files"),
opt_grp4(*this, "Options for run command", "These options are only used by the run command."),
opt_ttr (*this, "t", "time_to_run", 1, "time to run the emulation (seconds)\n\n abc def\n\n xyz"),
opt_ttr (*this, "t", "time_to_run", 1, "time to run the emulation (seconds)"),
opt_boostlib(*this, "", "boost_lib", "builtin", "generic: will use generic solvers.\nbuiltin: Use optimized solvers compiled in.\nsomelib.so: Use library with precompiled solvers."),
opt_stats(*this, "s", "statistics", "gather runtime statistics"),
opt_logs(*this, "l", "log" , "define terminal to log. This option may be specified repeatedly."),
opt_inp(*this, "i", "input", "", "input file to process (default is none)"),
@ -94,6 +100,7 @@ public:
plib::option_str opt_dir;
plib::option_group opt_grp4;
plib::option_num<nl_fptype> opt_ttr;
plib::option_str opt_boostlib;
plib::option_bool opt_stats;
plib::option_vec opt_logs;
plib::option_str opt_inp;
@ -191,6 +198,23 @@ public:
void vlog(const plib::plog_level &l, const pstring &ls) const noexcept override;
plib::unique_ptr<plib::dynlib_base> static_solver_lib() const override
{
if (m_app.opt_boostlib() == "builtin")
return netlist::callbacks_t::static_solver_lib();
else if (m_app.opt_boostlib() == "generic")
return plib::make_unique<plib::dynlib_static>(nullptr);
if (NL_DISABLE_DYNAMIC_LOAD)
{
throw netlist::nl_exception("Dynamic library loading not supported due to project security concerns.");
}
else
{
//pstring libpath = plib::util::environment("NL_BOOSTLIB", plib::util::buildpath({".", "nlboost.so"}));
return plib::make_unique<plib::dynlib>(m_app.opt_boostlib());
}
}
private:
tool_app_t &m_app;
};

View File

@ -66,6 +66,8 @@ namespace solver
void matrix_solver_t::setup_base(const analog_net_t::list_t &nets)
{
log().debug("New solver setup\n");
std::vector<core_device_t *> step_devices;
std::vector<core_device_t *> dynamic_devices;
m_terms.clear();
@ -90,11 +92,11 @@ namespace solver
{
case detail::terminal_type::TERMINAL:
if (p->device().is_timestep())
if (!plib::container::contains(m_step_devices, &p->device()))
m_step_devices.push_back(&p->device());
if (!plib::container::contains(step_devices, &p->device()))
step_devices.push_back(&p->device());
if (p->device().is_dynamic())
if (!plib::container::contains(m_dynamic_devices, &p->device()))
m_dynamic_devices.push_back(&p->device());
if (!plib::container::contains(dynamic_devices, &p->device()))
dynamic_devices.push_back(&p->device());
{
auto *pterm = dynamic_cast<terminal_t *>(p);
add_term(k, pterm);
@ -131,6 +133,10 @@ namespace solver
}
}
}
for (auto &d : step_devices)
m_step_funcs.push_back(nldelegate_ts(&core_device_t::timestep, d));
for (auto &d : dynamic_devices)
m_dynamic_funcs.push_back(nldelegate_dyn(&core_device_t::update_terminals, d));
}
void matrix_solver_t::sort_terms(matrix_sort_type_e sort)
@ -383,11 +389,11 @@ namespace solver
inp->push(inp->proxied_net()->Q_Analog());
}
void matrix_solver_t::update_dynamic()
void matrix_solver_t::update_dynamic() noexcept
{
// update all non-linear devices
for (auto &dyn : m_dynamic_devices)
dyn->update_terminals();
for (auto &dyn : m_dynamic_funcs)
dyn();
}
void matrix_solver_t::reset()
@ -400,7 +406,7 @@ namespace solver
const netlist_time new_timestep = solve(exec().time());
update_inputs();
if (m_params.m_dynamic_ts && has_timestep_devices() && new_timestep > netlist_time::zero())
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);
}
@ -418,17 +424,17 @@ namespace solver
update_inputs();
if (m_params.m_dynamic_ts && has_timestep_devices())
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));
}
}
void matrix_solver_t::step(const netlist_time &delta)
void matrix_solver_t::step(netlist_time delta) noexcept
{
const auto dd(delta.as_fp<nl_fptype>());
for (auto &d : m_step_devices)
d->timestep(dd);
for (auto &d : m_step_funcs)
d(dd);
}
netlist_time matrix_solver_t::solve(netlist_time_ext now)
@ -445,7 +451,7 @@ namespace solver
step(static_cast<netlist_time>(delta));
++m_stat_vsolver_calls;
if (has_dynamic_devices())
if (dynamic_device_count() != 0)
{
std::size_t this_resched(0);
std::size_t newton_loops = 0;
@ -574,8 +580,8 @@ namespace solver
log().verbose("==============================================");
log().verbose("Solver {1}", this->name());
log().verbose(" ==> {1} nets", this->m_terms.size()); //, (*(*groups[i].first())->m_core_terms.first())->name());
log().verbose(" has {1} elements", this->has_dynamic_devices() ? "dynamic" : "no dynamic");
log().verbose(" has {1} elements", this->has_timestep_devices() ? "timestep" : "no timestep");
log().verbose(" has {1} dynamic elements", this->dynamic_device_count());
log().verbose(" has {1} timestep elements", this->timestep_device_count());
log().verbose(" {1:6.3} average newton raphson loops",
static_cast<nl_fptype>(this->m_stat_newton_raphson) / static_cast<nl_fptype>(this->m_stat_vsolver_calls));
log().verbose(" {1:10} invocations ({2:6.0} Hz) {3:10} gs fails ({4:6.2} %) {5:6.3} average",

View File

@ -182,8 +182,8 @@ namespace solver
netlist_time solve(netlist_time_ext now);
void update_inputs();
bool has_dynamic_devices() const noexcept { return !m_dynamic_devices.empty(); }
bool has_timestep_devices() const noexcept { return !m_step_devices.empty(); }
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(); }
void update_forced();
void update_after(netlist_time after) noexcept
@ -238,8 +238,8 @@ namespace solver
state_var<std::size_t> m_stat_vsolver_calls;
state_var<netlist_time_ext> m_last_step;
std::vector<core_device_t *> m_step_devices;
std::vector<core_device_t *> m_dynamic_devices;
std::vector<nldelegate_ts> m_step_funcs;
std::vector<nldelegate_dyn> m_dynamic_funcs;
logic_input_t m_fb_sync;
logic_output_t m_Q_sync;
@ -251,8 +251,8 @@ namespace solver
void sort_terms(matrix_sort_type_e sort);
void update_dynamic();
void step(const netlist_time &delta);
void update_dynamic() noexcept;
void step(netlist_time delta) noexcept;
int get_net_idx(const analog_net_t *net) const noexcept;
std::pair<int, int> get_left_right_of_diag(std::size_t irow, std::size_t idiag);
@ -311,7 +311,7 @@ namespace solver
plib::parray<FT, SIZE> m_RHS;
PALIGNAS_VECTOROPT()
plib::parray2D<float_type *, SIZE, 0> m_mat_ptr;
plib::pmatrix2d<float_type *> m_mat_ptr;
// FIXME: below should be private
// state - variable time_stepping

View File

@ -124,7 +124,7 @@ namespace solver
mat_type mat;
plib::dynproc<void, FT * , FT * , FT * > m_proc;
plib::dynproc<void, FT *, FT *, FT *, FT *, FT ** > m_proc;
};
@ -140,7 +140,45 @@ namespace solver
pstring fpsuffix(fp_constants<FT>::suffix());
for (std::size_t i = 0; i < mat.nz_num; i++)
strm("{1} m_A{2} = m_A[{3}];\n", fptype, i, i);
strm("{1} m_A{2}(0.0);\n", fptype, i, i);
for (std::size_t k = 0; k < iN; k++)
{
auto &net = this->m_terms[k];
// FIXME: gonn, gtn and Idr - which float types should they have?
//auto gtot_t = std::accumulate(gt, gt + term_count, plib::constants<FT>::zero());
//*tcr_r[railstart] = static_cast<FT>(gtot_t); //mat.A[mat.diag[k]] += gtot_t;
auto pd = this->m_mat_ptr[k][net.railstart()] - &this->mat.A[0];
pstring terms = plib::pfmt("m_A{1} = gt[{2}]")(pd, this->m_gtn.didx(k,0));
for (std::size_t i=1; i < net.count(); i++)
terms += plib::pfmt(" + gt[{1}]")(this->m_gtn.didx(k,i));
strm("\t{1};\n", terms);
//for (std::size_t i = 0; i < railstart; i++)
// *tcr_r[i] += static_cast<FT>(go[i]);
for (std::size_t i = 0; i < net.railstart(); i++)
{
auto p = this->m_mat_ptr[k][i] - &this->mat.A[0];
strm("\tm_A{1} = m_A{1} + go[{2}];\n", p, this->m_gonn.didx(k,i));
}
//auto RHS_t(std::accumulate(Idr, Idr + term_count, plib::constants<FT>::zero()));
terms = plib::pfmt("{1} RHS{2} = Idr[{3}]")(fptype, k, this->m_Idrn.didx(k,0));
for (std::size_t i=1; i < net.count(); i++)
terms += plib::pfmt(" + Idr[{1}]")(this->m_Idrn.didx(k,i));
//for (std::size_t i = railstart; i < term_count; i++)
// RHS_t += (- go[i]) * *cnV[i];
for (std::size_t i = net.railstart(); i < net.count(); i++)
terms += plib::pfmt(" - go[{1}] * *cnV[{1}]")(this->m_gonn.didx(k,i), this->m_connected_net_Vn.didx(k,i));
strm("\t{1};\n", terms);
}
for (std::size_t i = 0; i < iN - 1; i++)
{
@ -178,13 +216,13 @@ namespace solver
pj++; pii++;
}
//RHS[j] += f1 * RHS[i];
strm("\tRHS[{1}] += f{2}_{3} * RHS[{4}];\n", j, i, j, i);
strm("\tRHS{1} += f{2}_{3} * RHS{4};\n", j, i, j, i);
}
}
}
//new_V[iN - 1] = RHS[iN - 1] / mat.A[mat.diag[iN - 1]];
strm("\tV[{1}] = RHS[{2}] / m_A{3};\n", iN - 1, iN - 1, mat.diag[iN - 1]);
strm("\tV[{1}] = RHS{2} / m_A{3};\n", iN - 1, iN - 1, mat.diag[iN - 1]);
for (std::size_t j = iN - 1; j-- > 0;)
{
strm("\t{1} tmp{2} = 0.0{3};\n", fptype, j, fpsuffix);
@ -193,7 +231,7 @@ namespace solver
{
strm("\ttmp{1} += m_A{2} * V[{3}];\n", j, pk, mat.col_idx[pk]);
}
strm("\tV[{1}] = (RHS[{1}] - tmp{1}) / m_A{4};\n", j, j, j, mat.diag[j]);
strm("\tV[{1}] = (RHS{1} - tmp{1}) / m_A{4};\n", j, j, j, mat.diag[j]);
}
}
@ -204,8 +242,8 @@ namespace solver
t.imbue(std::locale::classic());
plib::putf8_fmt_writer w(&t);
generate_code(w);
std::hash<typename std::remove_const<std::remove_reference<decltype(t.str())>::type>::type> h;
return plib::pfmt("nl_gcr_{1:x}_{2}")(h( t.str() ))(mat.nz_num);
//std::hash<typename std::remove_const<std::remove_reference<decltype(t.str())>::type>::type> h;
return plib::pfmt("nl_gcr_{1:x}_{2}")(plib::hash( t.str().c_str(), t.str().size() ))(mat.nz_num);
}
template <typename FT, int SIZE>
@ -217,7 +255,9 @@ namespace solver
pstring name = static_compile_name();
pstring fptype(fp_constants<FT>::name());
strm.writeline(plib::pfmt("extern \"C\" void {1}({2} * __restrict m_A, {2} * __restrict RHS, {2} * __restrict V)\n")(name, fptype));
strm.writeline(plib::pfmt("extern \"C\" void {1}({2} * __restrict V, "
"{2} * __restrict go, {2} * __restrict gt, "
"{2} * __restrict Idr, {2} ** __restrict cnV)\n")(name, fptype));
strm.writeline("{\n");
generate_code(strm);
strm.writeline("}\n");
@ -228,20 +268,21 @@ namespace solver
template <typename FT, int SIZE>
unsigned matrix_solver_GCR_t<FT, SIZE>::vsolve_non_dynamic(bool newton_raphson)
{
// clear matrix
mat.set_scalar(plib::constants<FT>::zero());
// populate matrix
this->fill_matrix_and_rhs();
// now solve it
if (m_proc.resolved())
{
m_proc(&mat.A[0], &this->m_RHS[0], &this->m_new_V[0]);
m_proc(&this->m_new_V[0],
this->m_gonn.data(), this->m_gtn.data(), this->m_Idrn.data(),
this->m_connected_net_Vn.data());
}
else
{
// clear matrix
mat.set_scalar(plib::constants<FT>::zero());
// populate matrix
this->fill_matrix_and_rhs();
// now solve it
// parallel is slow -- very slow
// mat.gaussian_elimination_parallel(RHS);
mat.gaussian_elimination(this->m_RHS);

View File

@ -391,8 +391,8 @@ namespace devices
log().verbose("Solver {1}", ms->name());
log().verbose(" ==> {1} nets", grp.size());
log().verbose(" has {1} elements", ms->has_dynamic_devices() ? "dynamic" : "no dynamic");
log().verbose(" has {1} elements", ms->has_timestep_devices() ? "timestep" : "no timestep");
log().verbose(" has {1} dynamic elements", ms->dynamic_device_count());
log().verbose(" has {1} timestep elements", ms->timestep_device_count());
for (auto &n : grp)
{
log().verbose("Net {1}", n->name());
@ -403,7 +403,7 @@ namespace devices
}
m_mat_solvers_all.push_back(ms.get());
if (ms->has_timestep_devices())
if (ms->timestep_device_count() != 0)
m_mat_solvers_timestepping.push_back(ms.get());
m_mat_solvers.emplace_back(std::move(ms));

View File

@ -6,11 +6,7 @@
#error Somehow nl_base.h made it into the include chain.
#endif
#ifndef NLTOOL_VERSION
#define USE_FRONTIERS 1
#else
#define USE_FRONTIERS 1
#endif
/* if we use frontiers, use fixed STV for smaller matrix sizes */
#if (USE_FRONTIERS)
@ -361,7 +357,7 @@ NETLIST_END()
NETLIST_START(kidniki)
#if (0 || USE_FRONTIERS)
#if (1 || USE_FRONTIERS)
SOLVER(Solver, 48000)
PARAM(Solver.ACCURACY, 1e-7)
PARAM(Solver.NR_LOOPS, 300)