diff options
author | Aaron Giles <aaron@aarongiles.com> | 2021-03-18 14:23:48 -0700 |
---|---|---|
committer | Aaron Giles <aaron@aarongiles.com> | 2021-03-18 14:23:48 -0700 |
commit | c5f0635323338d3e65850f83171b81dcdf2f86c6 (patch) | |
tree | fdbbd45630f82238eb552961ca133d94b6f7511e | |
parent | 19f4a0c731c68b3bf2432d4b5096428aaf8ecf77 (diff) |
Quick test to see performance difference of using attoseconds exclusively during core scheduling. Seems to be ~4% on firetrap as an example.
-rw-r--r-- | src/emu/diexec.cpp | 9 | ||||
-rw-r--r-- | src/emu/diexec.h | 2 | ||||
-rw-r--r-- | src/emu/schedule.cpp | 90 | ||||
-rw-r--r-- | src/emu/schedule.h | 102 |
4 files changed, 161 insertions, 42 deletions
diff --git a/src/emu/diexec.cpp b/src/emu/diexec.cpp index 49dee5f650e..d4d605552d8 100644 --- a/src/emu/diexec.cpp +++ b/src/emu/diexec.cpp @@ -69,8 +69,6 @@ device_execute_interface::device_execute_interface(const machine_config &mconfig , m_cycles_per_second(0) , m_attoseconds_per_cycle(0) { - memset(&m_localtime, 0, sizeof(m_localtime)); - // configure the fast accessor assert(!device.interfaces().m_execute); device.interfaces().m_execute = this; @@ -214,9 +212,9 @@ attotime device_execute_interface::local_time() const noexcept { assert(m_cycles_running >= *m_icountptr); int cycles = m_cycles_running - *m_icountptr; - return m_localtime + cycles_to_attotime(cycles); + return m_localtime.absolute() + cycles_to_attotime(cycles); } - return m_localtime; + return m_localtime.absolute(); } @@ -372,6 +370,7 @@ void device_execute_interface::interface_validity_check(validity_checker &valid) void device_execute_interface::interface_pre_start() { m_scheduler = &device().machine().scheduler(); + m_localtime.set_base_seconds(m_scheduler->m_basetime.seconds()); // bind delegates m_vblank_interrupt.resolve(); @@ -408,7 +407,7 @@ void device_execute_interface::interface_post_start() device().save_item(NAME(m_nexteatcycles)); device().save_item(NAME(m_trigger)); device().save_item(NAME(m_totalcycles)); - device().save_item(NAME(m_localtime)); +// device().save_item(NAME(m_localtime)); // it's more efficient and causes less clutter to save these this way device().save_item(STRUCT_MEMBER(m_input, m_stored_vector)); diff --git a/src/emu/diexec.h b/src/emu/diexec.h index 065248a9046..8eb94ce1d0b 100644 --- a/src/emu/diexec.h +++ b/src/emu/diexec.h @@ -318,7 +318,7 @@ private: // clock and timing information u64 m_totalcycles; // total device cycles executed - attotime m_localtime; // local time, relative to the timer system's global time + basetime_relative m_localtime; // local time, relative to the timer system's global time s32 m_divisor; // 32-bit attoseconds_per_cycle divisor u8 m_divshift; // right shift amount to fit the divisor into 32 bits u32 m_cycles_per_second; // cycles per second, adjusted for multipliers diff --git a/src/emu/schedule.cpp b/src/emu/schedule.cpp index 3596c5ff888..e6c883ee5d7 100644 --- a/src/emu/schedule.cpp +++ b/src/emu/schedule.cpp @@ -11,6 +11,8 @@ #include "emu.h" #include "debugger.h" +static attotime m_timer_next_basetime; + //************************************************************************** // DEBUGGING //************************************************************************** @@ -54,7 +56,6 @@ emu_timer::emu_timer() : m_temporary(false), m_period(attotime::zero), m_start(attotime::zero), - m_expire(attotime::never), m_device(nullptr), m_id(0) { @@ -88,8 +89,8 @@ inline emu_timer &emu_timer::init(running_machine &machine, timer_expired_delega m_temporary = temporary; m_period = attotime::never; m_start = machine.time(); - m_expire = attotime::never; - m_device = nullptr; + m_expire.set_base_seconds(machine.scheduler().m_basetime.seconds()); + m_expire.set(attotime::never); m_id = 0; // if we're not temporary, register ourselves with the save state system @@ -120,7 +121,8 @@ inline emu_timer &emu_timer::init(device_t &device, device_timer_id id, void *pt m_temporary = temporary; m_period = attotime::never; m_start = machine().time(); - m_expire = attotime::never; + m_expire.set_base_seconds(device.machine().scheduler().m_basetime.seconds()); + m_expire.set(attotime::never); m_device = &device; m_id = id; @@ -191,7 +193,7 @@ void emu_timer::adjust(attotime start_delay, s32 param, const attotime &period) // set the start and expire times m_start = scheduler.time(); - m_expire = m_start + start_delay; + m_expire.set(m_start + start_delay); m_period = period; // remove and re-insert the timer in its new order @@ -223,9 +225,9 @@ attotime emu_timer::elapsed() const noexcept attotime emu_timer::remaining() const noexcept { attotime curtime = machine().time(); - if (curtime >= m_expire) + if (curtime >= m_expire.absolute()) return attotime::zero; - return m_expire - curtime; + return m_expire.absolute() - curtime; } @@ -267,7 +269,7 @@ void emu_timer::register_save() machine().save().save_item(m_device, "timer", name.c_str(), index, NAME(m_enabled)); machine().save().save_item(m_device, "timer", name.c_str(), index, NAME(m_period)); machine().save().save_item(m_device, "timer", name.c_str(), index, NAME(m_start)); - machine().save().save_item(m_device, "timer", name.c_str(), index, NAME(m_expire)); +// machine().save().save_item(m_device, "timer", name.c_str(), index, NAME(m_expire)); } @@ -279,8 +281,8 @@ void emu_timer::register_save() inline void emu_timer::schedule_next_period() { // advance by one period - m_start = m_expire; - m_expire += m_period; + m_start = m_expire.absolute(); + m_expire.set(m_expire.absolute() + m_period); // remove and re-insert us device_scheduler &scheduler = machine().scheduler(); @@ -441,17 +443,19 @@ void device_scheduler::timeslice() m_quantum_allocator.reclaim(m_quantum_list.detach_head()); // loop until we hit the next timer - while (m_basetime < m_timer_list->m_expire) + attoseconds_t basetime = m_basetime.attoseconds(); + while (basetime < m_timer_list->m_expire.relative()) { // by default, assume our target is the end of the next quantum - attotime target(m_basetime + attotime(0, m_quantum_list.first()->m_actual)); + attoseconds_t target = basetime + m_quantum_list.first()->m_actual; + assert(target < 2 * ATTOSECONDS_PER_SECOND); // however, if the next timer is going to fire before then, override - if (m_timer_list->m_expire < target) - target = m_timer_list->m_expire; + if (m_timer_list->m_expire.relative() < target) + target = m_timer_list->m_expire.relative(); LOG("------------------\n"); - LOG("cpu_timeslice: target = %s\n", target.as_string(PRECISION)); + LOG("cpu_timeslice: target = %lld\n", target); // do we have pending suspension changes? if (m_suspend_changes_pending) @@ -462,17 +466,14 @@ void device_scheduler::timeslice() { // only process if this CPU is executing or truly halted (not yielding) // and if our target is later than the CPU's current time (coarse check) - if (EXPECTED((exec->m_suspend == 0 || exec->m_eatcycles) && target.seconds() >= exec->m_localtime.seconds())) + if (EXPECTED(exec->m_suspend == 0 || exec->m_eatcycles)) { // compute how many attoseconds to execute this CPU - attoseconds_t delta = target.attoseconds() - exec->m_localtime.attoseconds(); - if (delta < 0 && target.seconds() > exec->m_localtime.seconds()) - delta += ATTOSECONDS_PER_SECOND; - assert(delta == (target - exec->m_localtime).as_attoseconds()); + attoseconds_t delta = target - exec->m_localtime.relative(); if (exec->m_attoseconds_per_cycle == 0) { - exec->m_localtime = target; + exec->m_localtime.set(target); } // if we have enough for at least 1 cycle, do the math else if (delta >= exec->m_attoseconds_per_cycle) @@ -495,7 +496,7 @@ void device_scheduler::timeslice() exec->run(); else { - exec->debugger_start_cpu_hook(target); + exec->debugger_start_cpu_hook(attotime(m_basetime.seconds(), target) + attotime::zero); exec->run(); exec->debugger_stop_cpu_hook(); } @@ -513,15 +514,15 @@ void device_scheduler::timeslice() // update the local time for this CPU assert(ran < exec->m_cycles_per_second); - attotime deltatime = attotime(0, exec->m_attoseconds_per_cycle * ran); - assert(deltatime >= attotime::zero); - exec->m_localtime += deltatime; + attoseconds_t deltatime = exec->m_attoseconds_per_cycle * ran; + assert(deltatime >= 0); + exec->m_localtime.add(deltatime); LOG(" %d ran, %d total, time = %s\n", ran, s32(exec->m_totalcycles), exec->m_localtime.as_string(PRECISION)); // if the new local CPU time is less than our target, move the target up, but not before the base - if (exec->m_localtime < target) + if (exec->m_localtime.relative() < target) { - target = std::max(exec->m_localtime, m_basetime); + target = std::max(exec->m_localtime.relative(), basetime); LOG(" (new target)\n"); } } @@ -530,9 +531,18 @@ void device_scheduler::timeslice() m_executing_device = nullptr; // update the base time - if (target.seconds() != m_basetime.seconds()) - update_basetime(); - m_basetime = target; + basetime = target; + } + + // update basetime + if (basetime < ATTOSECONDS_PER_SECOND) + m_basetime.set_attoseconds(basetime); + else + { + basetime -= ATTOSECONDS_PER_SECOND; + m_basetime.set_attoseconds(basetime); + m_basetime.set_seconds(m_basetime.seconds() + 1); + update_basetime(); } // execute timers @@ -540,6 +550,18 @@ void device_scheduler::timeslice() } +void device_scheduler::update_basetime() +{ + // update execute devices + for (device_execute_interface &exec : execute_interface_enumerator(machine().root_device())) + exec.m_localtime.set_base_seconds(m_basetime.seconds()); + + // update timers + for (emu_timer *curtimer = m_timer_list; curtimer != nullptr; curtimer = curtimer->next()) + curtimer->m_expire.set_base_seconds(m_basetime.seconds()); +} + + //------------------------------------------------- // abort_timeslice - abort execution for the // current timeslice @@ -811,14 +833,14 @@ void device_scheduler::rebuild_execute_list() inline emu_timer &device_scheduler::timer_list_insert(emu_timer &timer) { // disabled timers sort to the end - const attotime expire = timer.m_enabled ? timer.m_expire : attotime::never; + const attotime expire = timer.m_enabled ? timer.m_expire.absolute() : attotime::never; // loop over the timer list emu_timer *prevtimer = nullptr; for (emu_timer *curtimer = m_timer_list; curtimer != nullptr; prevtimer = curtimer, curtimer = curtimer->next()) { // if the current list entry expires after us, we should be inserted before it - if (curtimer->m_expire > expire) + if (curtimer->m_expire.absolute() > expire) { // link the new guy in before the current list entry timer.m_prev = prevtimer; @@ -875,7 +897,7 @@ inline void device_scheduler::execute_timers() LOG("execute_timers: new=%s head->expire=%s\n", m_basetime.as_string(PRECISION), m_timer_list->m_expire.as_string(PRECISION)); // now process any timers that are overdue - while (m_timer_list->m_expire <= m_basetime) + while (m_timer_list->m_expire.absolute() <= m_basetime) { // if this is a one-shot timer, disable it now emu_timer &timer = *m_timer_list; @@ -886,7 +908,7 @@ inline void device_scheduler::execute_timers() // set the global state of which callback we're in m_callback_timer_modified = false; m_callback_timer = &timer; - m_callback_timer_expire_time = timer.m_expire; + m_callback_timer_expire_time = timer.m_expire.absolute(); // call the callback if (was_enabled) diff --git a/src/emu/schedule.h b/src/emu/schedule.h index 67f6d945898..a6488942da5 100644 --- a/src/emu/schedule.h +++ b/src/emu/schedule.h @@ -29,6 +29,98 @@ // TYPE DEFINITIONS //************************************************************************** +class basetime_relative +{ +public: + static constexpr attoseconds_t MAX_RELATIVE = 2 * ATTOSECONDS_PER_SECOND; + static constexpr attoseconds_t MIN_RELATIVE = -MAX_RELATIVE; + + basetime_relative() : + m_relative(0), + m_absolute(attotime::zero), + m_base_seconds(0) + { + } + + void set(attotime const &src) + { + m_absolute = src; + update_relative(); + } + + void set(attoseconds_t src) + { + m_relative = src; + update_absolute(); + } + + void add(attoseconds_t src) + { + m_relative += src; + update_absolute(); + } + + void set_base_seconds(seconds_t base) + { + if (base != m_base_seconds) + { + m_base_seconds = base; + update_relative(); + } + } + + attoseconds_t relative() const { return m_relative; } + attotime const &absolute() const { return m_absolute; } + + const char *as_string(int precision = 9) const { return m_absolute.as_string(precision); } + +private: + void update_relative() + { + seconds_t delta = m_absolute.seconds() - m_base_seconds; + m_relative = m_absolute.attoseconds(); + if (delta == 0) + return; + if (delta > 0) + { + if (delta == 1) + m_relative += ATTOSECONDS_PER_SECOND; + else + m_relative = MAX_RELATIVE; + } + else + { + if (delta == -1) + m_relative -= ATTOSECONDS_PER_SECOND; + else + m_relative = MIN_RELATIVE; + } + } + + void update_absolute() + { + seconds_t secs = m_base_seconds; + attoseconds_t attos = m_relative; + if (attos >= ATTOSECONDS_PER_SECOND) + { + attos -= ATTOSECONDS_PER_SECOND; + secs++; + } + else if (attos < 0) + { + attos += ATTOSECONDS_PER_SECOND; + secs--; + } + m_absolute.set_seconds(secs); + m_absolute.set_attoseconds(attos); + } + + attoseconds_t m_relative; + attotime m_absolute; + seconds_t m_base_seconds; +}; + + // timer callbacks look like this typedef named_delegate<void (void *, s32)> timer_expired_delegate; @@ -71,7 +163,7 @@ public: attotime elapsed() const noexcept; attotime remaining() const noexcept; attotime start() const { return m_start; } - attotime expire() const { return m_expire; } + attotime expire() const { return m_expire.absolute(); } attotime period() const { return m_period; } private: @@ -92,7 +184,7 @@ private: bool m_temporary; // is the timer temporary? attotime m_period; // the repeat frequency of the timer attotime m_start; // time when the timer was started - attotime m_expire; // time when the timer will expire + basetime_relative m_expire; // time when the timer will expire device_t * m_device; // for device timers, a pointer to the device device_timer_id m_id; // for device timers, the ID of the timer }; @@ -103,6 +195,7 @@ private: class device_scheduler { friend class device_execute_interface; + friend class basetime_relative; friend class emu_timer; public: @@ -155,6 +248,10 @@ private: emu_timer &timer_list_insert(emu_timer &timer); emu_timer &timer_list_remove(emu_timer &timer); void execute_timers(); + void update_basetime(); + + // basetime_relative helpers + attotime const &basetime() const { return m_basetime; } // internal state running_machine & m_machine; // reference to our machine @@ -191,4 +288,5 @@ private: }; + #endif // MAME_EMU_SCHEDULE_H |