almost a magnitude of perf win by moving input polling into thread

This commit is contained in:
Stephan Dilly 2020-03-19 12:55:38 +01:00
parent 8618e404d4
commit eefd31ad05
3 changed files with 66 additions and 19 deletions

View file

@ -13,7 +13,7 @@ Over the last 2 years my go to GUI tool for this was [fork](https://git-fork.com
* [x] (un)stage files * [x] (un)stage files
* [x] inspect diffs * [x] inspect diffs
* [x] commit * [x] commit
* [ ] input polling in thread * [x] input polling in thread
* [ ] file watcher instead of polling git * [ ] file watcher instead of polling git
* [ ] log view * [ ] log view

View file

@ -6,7 +6,7 @@ mod git_utils;
mod poll; mod poll;
mod tui_utils; mod tui_utils;
use app::App; use crate::{app::App, poll::QueueEvent};
use crossterm::{ use crossterm::{
event::{DisableMouseCapture, EnableMouseCapture}, event::{DisableMouseCapture, EnableMouseCapture},
terminal::{ terminal::{
@ -15,8 +15,7 @@ use crossterm::{
}, },
ExecutableCommand, Result, ExecutableCommand, Result,
}; };
use poll::PollResult; use std::io;
use std::{io, time::Duration};
use tui::{backend::CrosstermBackend, Terminal}; use tui::{backend::CrosstermBackend, Terminal};
fn main() -> Result<()> { fn main() -> Result<()> {
@ -33,18 +32,17 @@ fn main() -> Result<()> {
let mut app = App::new(); let mut app = App::new();
let receiver = poll::start_polling_thread();
loop { loop {
app.update(); app.update();
terminal.draw(|mut f| app.draw(&mut f))?; terminal.draw(|mut f| app.draw(&mut f))?;
loop { let events = receiver.recv().unwrap();
if let PollResult::Event(e) = for e in events {
poll::poll(Duration::from_millis(10)) if let QueueEvent::Event(ev) = e {
{ app.event(ev);
app.event(e);
} else {
break;
} }
} }

View file

@ -1,20 +1,69 @@
use crossterm::event::{self, Event}; use crossterm::event::{self, Event};
use std::time::Duration; use std::{
sync::mpsc::{self, Receiver},
thread::{self, sleep},
time::{Duration, Instant},
};
/// as ///
pub enum PollResult { #[derive(Clone)]
Timeout, pub enum QueueEvent {
Tick,
Event(Event), Event(Event),
} }
static MAX_POLL_DURATION: Duration = Duration::from_secs(2);
static MIN_POLL_DURATION: Duration = Duration::from_millis(5);
static MAX_BATCHING_DURATION: Duration = Duration::from_millis(25);
static TICK_DURATION: Duration = Duration::from_secs(2);
/// we run 2 threads feeding us with update events.
/// ///
pub fn poll(dur: Duration) -> PollResult { /// Thread 1:
/// We will
pub fn start_polling_thread() -> Receiver<Vec<QueueEvent>> {
let (tx, rx) = mpsc::channel();
let tx1 = tx.clone();
thread::spawn(move || {
let mut last_send = Instant::now();
let mut batch = Vec::new();
loop {
let timeout = if batch.len() > 0 {
MIN_POLL_DURATION
} else {
MAX_POLL_DURATION
};
if let Some(e) = poll(timeout) {
batch.push(QueueEvent::Event(e));
}
if batch.len() > 0
&& last_send.elapsed() > MAX_BATCHING_DURATION
{
tx1.send(batch).unwrap();
batch = Vec::new();
last_send = Instant::now();
}
}
});
thread::spawn(move || loop {
tx.send(vec![QueueEvent::Tick]).unwrap();
sleep(TICK_DURATION);
});
rx
}
///
fn poll(dur: Duration) -> Option<Event> {
if event::poll(dur).unwrap() { if event::poll(dur).unwrap() {
// It's guaranteed that read() wont block if `poll` returns `Ok(true)` // It's guaranteed that read() wont block if `poll` returns `Ok(true)`
let event = event::read().unwrap(); let event = event::read().unwrap();
Some(event)
PollResult::Event(event)
} else { } else {
PollResult::Timeout None
} }
} }