perf: reduce allocations across UI and editor hot paths

- reuse formatting, document text, property resolution, and layout storage
  - use inline containers for interpolations, callbacks, wrapping, and matcher state
  - optimize single-line insertion by mutating document lines without splitting
  - iterate PATH entries directly in Sys::which and reuse candidate buffers
  - avoid temporary clocks and string copies in frequent ecode plugin updates
  - retain documents during autocomplete cache jobs and prevent duplicate scheduling
  - fix PatternMatcher copy assignment and MSVC move-only container compilation
  - expand coverage for matching, text insertion, reusable strings, and Sys::which
This commit is contained in:
Martín Lucas Golini
2026-08-09 21:38:17 -03:00
parent 5f419ca58d
commit 23cf9084ce
35 changed files with 618 additions and 189 deletions
+19 -6
View File
@@ -566,6 +566,13 @@ class EE_API String {
template <typename... Args>
static std::string format( std::string_view format, Args&&... args ) {
std::string result;
formatTo( result, format, std::forward<Args>( args )... );
return result;
}
template <typename... Args>
static void formatTo( std::string& result, std::string_view format, Args&&... args ) {
#ifdef __clang__
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wformat-security"
@@ -578,18 +585,22 @@ class EE_API String {
std::snprintf( nullptr, 0, format.data(),
FormatArg<std::decay_t<Args>>::get( std::forward<Args>( args ) )... );
if ( reqSize < 0 )
return "";
if ( reqSize < 0 ) {
result.clear();
return;
}
std::size_t bufSize = static_cast<std::size_t>( reqSize ) + 1;
std::string result( bufSize, '\0' );
result.resize( bufSize );
int writtenChars =
std::snprintf( &result[0], bufSize, format.data(),
FormatArg<std::decay_t<Args>>::get( std::forward<Args>( args ) )... );
if ( writtenChars < 0 )
return "";
if ( writtenChars < 0 ) {
result.clear();
return;
}
if ( static_cast<std::size_t>( writtenChars ) < bufSize ) {
result.resize( static_cast<std::size_t>( writtenChars ) );
@@ -602,7 +613,6 @@ class EE_API String {
#elif defined( __GNUC__ )
#pragma GCC diagnostic pop
#endif
return result;
}
/** Format a char buffer */
@@ -993,6 +1003,9 @@ class EE_API String {
String& assign( const char* s );
/** Assigns UTF-8 text while retaining the current UTF-32 storage capacity. */
String& assignUtf8( std::string_view utf8String );
String& assign( std::size_t n, StringBaseType c );
template <class InputIterator> String& assign( InputIterator first, InputIterator last ) {
+2 -2
View File
@@ -111,7 +111,7 @@ class EE_API Interpolation1d {
const Time& getDuration() const;
/** @return the vector of points */
const std::vector<Point1d>& getPoints() const;
const SmallVector<Point1d, 8>& getPoints() const;
/** @return the vector of points reversed */
std::vector<Point1d> getReversePoints();
@@ -163,7 +163,7 @@ class EE_API Interpolation1d {
Float mSpeed;
std::vector<Point1d> mPoints;
SmallVector<Point1d, 8> mPoints;
Point1d* mActP;
Point1d* mNexP;
+2 -2
View File
@@ -112,7 +112,7 @@ class EE_API Interpolation2d {
const Uint32& getCurrentPositionIndex() const;
/** @return the vector of waypoints */
const std::vector<Point2d>& getPoints() const;
const SmallVector<Point2d, 8>& getPoints() const;
/** @return the vector of waypoints reversed */
std::vector<Point2d> getReversePoints();
@@ -167,7 +167,7 @@ class EE_API Interpolation2d {
Point2d* mActP;
Point2d* mNexP;
std::vector<Point2d> mPoints;
SmallVector<Point2d, 8> mPoints;
OnPathEndCallback mOnPathEndCallback;
+9 -5
View File
@@ -1,6 +1,7 @@
#ifndef EE_SYSTEM_PATTERNMATCHER_HPP
#define EE_SYSTEM_PATTERNMATCHER_HPP
#include <array>
#include <eepp/config.hpp>
#include <string>
#include <string_view>
@@ -23,16 +24,19 @@ class EE_API PatternMatcher {
class EE_API State {
public:
State( PatternMatcher* pattern, bool ownPattern );
State( const State& other );
State& operator=( const State& other );
~State();
bool range( int index, int& start, int& end );
void swap( State& other ) noexcept;
bool range( int index, int& start, int& end ) const;
bool matches( const char* string, size_t length );
PatternMatcher* mPattern;
Range* mRanges;
size_t mRefCount;
std::array<Range, 10> mRanges;
bool mOwnPattern;
};
@@ -87,7 +91,7 @@ class EE_API PatternMatcher {
iterator end() { return iterator( nullptr ); }
protected:
PatternMatcher::State* mState{ nullptr };
PatternMatcher::State mState;
const char* mString{ nullptr };
size_t mLength{ 0 };
};
@@ -107,7 +111,7 @@ class EE_API PatternMatcher {
PatternMatcher::Match gmatch( const std::string& string ) &;
bool range( int indexGet, int& startMatch, int& endMatch,
PatternMatcher::Range* returnedMatched ) const;
const PatternMatcher::Range* returnedMatched ) const;
bool find( const std::string& s, int& startMatch, int& endMatch, int offset = 0,
int returnedMatchIndex = 0 ) const;
+5 -5
View File
@@ -5,6 +5,7 @@
#include <condition_variable>
#include <deque>
#include <eepp/core/noncopyable.hpp>
#include <eepp/core/small_function.hpp>
#include <eepp/system/lock.hpp>
#include <eepp/system/mutex.hpp>
#include <eepp/system/thread.hpp>
@@ -28,10 +29,9 @@ class EE_API ThreadPool : NonCopyable {
virtual ~ThreadPool();
Uint64 run(
const std::function<void()>& func,
const std::function<void( const Uint64& )>& doneCallback = []( const Uint64& ) {},
const Uint64& tag = 0 );
Uint64 run( SmallFunction<48> func,
const std::function<void( const Uint64& )>& doneCallback = {},
const Uint64& tag = 0 );
Uint32 numThreads() const;
@@ -50,7 +50,7 @@ class EE_API ThreadPool : NonCopyable {
private:
struct Work {
Uint64 id{ 0 };
const std::function<void()> func;
const SmallFunction<48> func;
const std::function<void( const Uint64& )> callback;
Uint64 tag{ 0 };
};
+2
View File
@@ -213,6 +213,8 @@ class EE_API TextDocument {
String getText( const TextRange& range ) const;
void getTextToBuffer( const TextRange& range, String& buffer ) const;
String getText() const;
String getSelectedText() const;
+11
View File
@@ -78,6 +78,17 @@ class EE_API TextDocumentLine {
}
}
void insert( std::size_t position, const String& text ) {
if ( mDocMutex ) {
Lock lock( *mDocMutex );
mText.insert( position, text );
updateState();
} else {
mText.insert( position, text );
updateState();
}
}
String substr( std::size_t pos = 0, std::size_t n = String::StringType::npos ) const {
if ( mDocMutex ) {
Lock lock( *mDocMutex );
+1
View File
@@ -965,6 +965,7 @@ class EE_API UICodeEditor : public UIWidget, public TextDocument::Client {
Mutex mHighlightWordCacheMutex;
TextRange mHighlightTextRange;
TextRange mPreviewColorRange;
String mMouseOverColorBuffer;
std::vector<UICodeEditorPlugin*> mPlugins;
UILoader* mLoader{ nullptr };
Float mGlyphWidth{ 0 };
+8
View File
@@ -1,6 +1,7 @@
#ifndef EE_UI_UISTACKLAYOUT_HPP
#define EE_UI_UISTACKLAYOUT_HPP
#include <eepp/core/small_vector.hpp>
#include <eepp/ui/uilayout.hpp>
namespace EE { namespace UI {
@@ -33,7 +34,14 @@ class EE_API UIStackLayout : public UILayout {
void setRowValign( const RowValign& rowValign );
protected:
struct NodeLine {
SmallVector<UIWidget*, 2> nodes;
Float maxY{ 0 };
Float width{ 0 };
};
RowValign mRowValign{ RowValign::Bottom };
SmallVector<NodeLine, 4> mLines;
Node* mParentRef{ nullptr };
Uint32 mParentSizeChangeCb{ 0 };
Uint32 mParentCloseCb{ 0 };
+9 -3
View File
@@ -29,6 +29,11 @@ class EE_API UIStyle : public UIState {
public:
static UIStyle* New( UIWidget* widget );
UIStyle( const UIStyle& ) = delete;
UIStyle& operator=( const UIStyle& ) = delete;
UIStyle( UIStyle&& ) = delete;
UIStyle& operator=( UIStyle&& ) = delete;
virtual ~UIStyle();
bool stateExists( const Uint32& state ) const;
@@ -133,9 +138,10 @@ class EE_API UIStyle : public UIState {
Uint32 mPropertyResolutionDepth{ 0 };
Uint64 mLoadedVersion{ 0 };
const CSS::StyleSheet* mLoadedStyleSheet{ nullptr };
/** Lazily allocated for styles that use substitutions. The pointed-to properties remain stable
* during nested resolutions and retain their string/vector capacities between state changes. */
SmallVector<std::unique_ptr<CSS::StyleSheetProperty>, 1> mPropertyResolutionSlots;
/** Lazily allocated for styles that use substitutions. The common, non-reentrant resolution
* needs no container allocation; nested slots are retained for later reuse. */
std::unique_ptr<CSS::StyleSheetProperty> mPropertyResolutionSlot;
std::vector<std::unique_ptr<CSS::StyleSheetProperty>> mNestedPropertyResolutionSlots;
bool mChangingState;
bool mForceReapplyProperties;
bool mDisableAnimations;