Appending to a string, often means that the new string needs more memory than the old one. If there is not enough free memory at the end of the string (assuming a smart enough mem manager), then it needs to be relocated.
So doing append over and over again, means that the string will over and over be copied to new memory.
Collecting all parts in a TStringList only copies the pointers. But in the end one big string must be created.
I am not sure if TStringList optimizes this in the final join.
You need to calculate the total size needed.
SetLength(target, totalsize);
and then for each string
move(to_be_appended[1], target[insert_pos], length(to_be_appended));