Reduce memory usage and allocations in terminal output code

Store data in unique_ptr instead of vectors as we have fixed sizes
Do not allocate new hashes, recompute them on-demand
This commit is contained in:
Maxime Coste 2021-07-31 15:06:21 +10:00
parent 580869fd49
commit 73da47258d
2 changed files with 38 additions and 34 deletions

View File

@ -23,22 +23,6 @@ namespace Kakoune
using std::min; using std::min;
using std::max; using std::max;
void TerminalUI::Window::create(const DisplayCoord& p, const DisplayCoord& s)
{
kak_assert(p.line >= 0 and p.column >= 0);
kak_assert(s.line >= 0 and s.column >= 0);
pos = p;
size = s;
lines.resize((int)size.line);
}
void TerminalUI::Window::destroy()
{
pos = DisplayCoord{};
size = DisplayCoord{};
lines.clear();
}
struct TerminalUI::Window::Line struct TerminalUI::Window::Line
{ {
struct Atom struct Atom
@ -148,17 +132,33 @@ struct TerminalUI::Window::Line
Vector<Atom> atoms; Vector<Atom> atoms;
}; };
void TerminalUI::Window::create(const DisplayCoord& p, const DisplayCoord& s)
{
kak_assert(p.line >= 0 and p.column >= 0);
kak_assert(s.line >= 0 and s.column >= 0);
pos = p;
size = s;
lines.reset(new Line[(int)size.line]);
}
void TerminalUI::Window::destroy()
{
pos = DisplayCoord{};
size = DisplayCoord{};
lines.reset();
}
void TerminalUI::Window::blit(Window& target) void TerminalUI::Window::blit(Window& target)
{ {
kak_assert(pos.line < target.lines.size()); kak_assert(pos.line < target.size.line);
auto target_line = target.lines.begin() + (size_t)pos.line; LineCount line_index = pos.line;
for (auto& line : lines) for (auto& line : ArrayView{lines.get(), (size_t)size.line})
{ {
line.resize(size.column); line.resize(size.column);
target_line->resize(target.size.column); auto& target_line = target.lines[(size_t)line_index];
target_line->atoms.insert(target_line->erase_range(pos.column, size.column), target_line.resize(target.size.column);
line.atoms.begin(), line.atoms.end()); target_line.atoms.insert(target_line.erase_range(pos.column, size.column), line.atoms.begin(), line.atoms.end());
if (++target_line == target.lines.end()) if (++line_index == target.size.line)
break; break;
} }
} }
@ -167,7 +167,7 @@ void TerminalUI::Window::draw(DisplayCoord pos,
ConstArrayView<DisplayAtom> atoms, ConstArrayView<DisplayAtom> atoms,
const Face& default_face) const Face& default_face)
{ {
if (pos.line >= lines.size()) // We might receive an out of date draw command after a resize if (pos.line >= size.line) // We might receive an out of date draw command after a resize
return; return;
lines[(size_t)pos.line].resize(pos.column); lines[(size_t)pos.line].resize(pos.column);
@ -245,7 +245,7 @@ void TerminalUI::Screen::set_face(const Face& face, Writer& writer)
void TerminalUI::Screen::output(bool force, bool synchronized, Writer& writer) void TerminalUI::Screen::output(bool force, bool synchronized, Writer& writer)
{ {
if (lines.empty()) if (not lines)
return; return;
// iTerm2 "begin synchronized update" sequence // iTerm2 "begin synchronized update" sequence
@ -254,16 +254,20 @@ void TerminalUI::Screen::output(bool force, bool synchronized, Writer& writer)
if (force) if (force)
{ {
hashes.clear(); std::fill_n(hashes.get(), (size_t)size.line, 0);
writer.write("\033[m"); writer.write("\033[m");
m_active_face = Face{}; m_active_face = Face{};
} }
auto hash_line = [](const Line& line) {
return (hash_value(line.atoms) << 1) | 1; // ensure non-zero
};
struct Change { int keep; int add; int del; }; struct Change { int keep; int add; int del; };
Vector<Change> changes{Change{}}; Vector<Change> changes{Change{}};
auto new_hashes = lines | transform([](auto& line) { return hash_value(line.atoms); }) | gather<Vector>(); auto new_hashes = ArrayView{lines.get(), (size_t)size.line} | transform(hash_line);
for_each_diff(hashes.begin(), hashes.size(), for_each_diff(hashes.get(), (int)size.line,
new_hashes.begin(), new_hashes.size(), new_hashes.begin(), (int)size.line,
[&changes](DiffOp op, int len) mutable { [&changes](DiffOp op, int len) mutable {
switch (op) switch (op)
{ {
@ -278,7 +282,7 @@ void TerminalUI::Screen::output(bool force, bool synchronized, Writer& writer)
break; break;
} }
}); });
hashes = std::move(new_hashes); std::copy(new_hashes.begin(), new_hashes.end(), hashes.get());
int line = 0; int line = 0;
for (auto& change : changes) for (auto& change : changes)
@ -590,7 +594,7 @@ void TerminalUI::check_resize(bool force)
m_window.create({0, 0}, terminal_size); m_window.create({0, 0}, terminal_size);
m_screen.create({0, 0}, terminal_size); m_screen.create({0, 0}, terminal_size);
m_screen.hashes.clear(); m_screen.hashes.reset(new size_t[(int)terminal_size.line]{});
kak_assert(m_window); kak_assert(m_window);
m_dimensions = terminal_size - 1_line; m_dimensions = terminal_size - 1_line;

View File

@ -81,10 +81,10 @@ private:
void blit(Window& target); void blit(Window& target);
void draw(DisplayCoord pos, ConstArrayView<DisplayAtom> atoms, const Face& default_face); void draw(DisplayCoord pos, ConstArrayView<DisplayAtom> atoms, const Face& default_face);
explicit operator bool() const { return not lines.empty(); } explicit operator bool() const { return (bool)lines; }
struct Line; struct Line;
Vector<Line> lines; std::unique_ptr<Line[]> lines;
}; };
struct Screen : Window struct Screen : Window
@ -92,7 +92,7 @@ private:
void output(bool force, bool synchronized, Writer& writer); void output(bool force, bool synchronized, Writer& writer);
void set_face(const Face& face, Writer& writer); void set_face(const Face& face, Writer& writer);
Vector<size_t> hashes; std::unique_ptr<size_t[]> hashes;
Face m_active_face; Face m_active_face;
}; };