GUI: stop copying and sorting the channel post array in the Qt thread

setPosts() runs in the Qt thread, through postToObject(). It copied the whole
post array a fourth time and then sorted it there, so for a channel with a few
thousand posts the interface was frozen for the duration of both.

Move the sort into the loader thread in update_posts(), where the array is
already sitting after the service call, and have setPosts() take its argument
by rvalue reference so the array is moved in rather than copied. The sort is
kept in setPosts() as a safety net: on an already ordered array it only runs
comparisons and moves nothing.

Combined with the libretroshare side making posts movable, the array is now
handed from the data store to the model without a single deep copy of a
thumbnail.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
jolavillette 2026-07-27 11:11:56 +02:00
parent 54d4c791aa
commit 28e7f791ff
2 changed files with 25 additions and 7 deletions

View File

@ -527,7 +527,7 @@ void RsGxsChannelPostsModel::updateSinglePost(const RsGxsChannelPost& post,std::
triggerViewUpdate(true,false);
}
void RsGxsChannelPostsModel::setPosts(const RsGxsChannelGroup& group, std::vector<RsGxsChannelPost>& posts)
void RsGxsChannelPostsModel::setPosts(const RsGxsChannelGroup& group, std::vector<RsGxsChannelPost>&& posts)
{
RsGxsProfiler::Timer prof_timer;
@ -538,14 +538,21 @@ void RsGxsChannelPostsModel::setPosts(const RsGxsChannelGroup& group, std::vecto
// createPostsArray(posts);
mPosts = posts;
// The caller hands over its array and discards it right after, so take it
// rather than deep copying every post (and every thumbnail) one more time.
mPosts = std::move(posts);
const long prof_copy_ms = prof_timer.lap();
// update_posts() already sorted the array in its loader thread. Kept as a
// cheap safety net for any other caller: on already ordered input this only
// costs comparisons, no element is moved.
std::sort(mPosts.begin(),mPosts.end());
const long prof_sort_ms = prof_timer.lap();
mFilteredPosts.reserve(mPosts.size());
for(uint32_t i=0;i<mPosts.size();++i)
mFilteredPosts.push_back(i);
@ -614,13 +621,23 @@ void RsGxsChannelPostsModel::update_posts(const RsGxsGroupId& group_id)
return;
}
const long prof_content_ms = prof_timer.ms();
const long prof_content_ms = prof_timer.lap();
RS_GXS_PROF( prof_grpinfo_ms + prof_content_ms,
// Sort here rather than in setPosts(): setPosts() runs in the Qt thread,
// where sorting a few thousand posts is a visible freeze. The model only
// ever displays a sorted array, so the order may as well be established
// in this loader thread.
std::sort(posts->begin(),posts->end());
const long prof_sort_ms = prof_timer.ms();
const long prof_total_ms = prof_grpinfo_ms + prof_content_ms + prof_sort_ms;
RS_GXS_PROF( prof_total_ms,
"update_posts grp=" << group_id << " posts=" << posts->size()
<< " getChannelsInfo=" << prof_grpinfo_ms << "ms"
<< " getChannelAllContent=" << prof_content_ms << "ms"
<< " service_total=" << (prof_grpinfo_ms + prof_content_ms) << "ms" );
<< " sort=" << prof_sort_ms << "ms"
<< " service_total=" << prof_total_ms << "ms" );
#ifdef DEBUG_CHANNEL_MODEL
std::cerr << "Got channel all content for channel " << group_id << std::endl;
std::cerr << " posts : " << posts->size() << std::endl;
@ -638,7 +655,7 @@ void RsGxsChannelPostsModel::update_posts(const RsGxsGroupId& group_id)
* Qt::QueuedConnection is important!
*/
setPosts(group,*posts) ;
setPosts(group,std::move(*posts)) ;
delete posts;

View File

@ -239,7 +239,8 @@ private:
//void computeMessagesHierarchy(const RsGxsChannelGroup& forum_group, const std::vector<RsMsgMetaData> &msgs_array, std::vector<ChannelPostsModelPostEntry> &posts, std::map<RsGxsMessageId, std::vector<std::pair<time_t, RsGxsMessageId> > > &mPostVersions);
void old_createPostsArray(std::vector<RsGxsChannelPost> &posts);
void createPostsArray(std::vector<RsGxsChannelPost>& posts);
void setPosts(const RsGxsChannelGroup& group, std::vector<RsGxsChannelPost> &posts);
/// Takes ownership of posts: the array is moved into the model.
void setPosts(const RsGxsChannelGroup& group, std::vector<RsGxsChannelPost> &&posts);
public:
void updateSinglePost(const RsGxsChannelPost& post, std::set<RsGxsFile>& added_files, std::set<RsGxsFile>& removed_files);
private: