Extreme inefficiency of String operations in the JavaScript runtime
See original GitHub issueI’ve created a simple helper TagOutput
to generate well-formed and well-escaped XML/HTML content. In the test shown below, I’m using this class to generate a relatively small (4k) HTML fragment in memory and append it to the browser DOM:
import ceylon.collection {
MutableList,
ArrayList
}
interface State of elementContent | tagOpen | attributeOpen {}
object elementContent satisfies State {}
object tagOpen satisfies State {}
object attributeOpen satisfies State {}
shared class TagOutput() {
variable State state = elementContent;
MutableList<String> openTags = ArrayList<String>();
StringBuilder buffer = StringBuilder();
shared actual String string => buffer.string;
shared TagOutput tag(String tagName) {
closeStart();
buffer.append("<");
buffer.append(tagName);
openTags.add(tagName);
state = tagOpen;
return this;
}
shared TagOutput openAttribute(String name) {
assert (state == tagOpen);
buffer.append(" ");
buffer.append(name);
buffer.append("=\"");
state = attributeOpen;
return this;
}
shared TagOutput closeAttribute() {
assert (state == attributeOpen);
buffer.append("\"");
state = tagOpen;
return this;
}
shared TagOutput attribute(String name, String? val) {
assert (state == tagOpen);
if (exists val) {
openAttribute(name);
attributeValue(val);
closeAttribute();
}
return this;
}
shared TagOutput end(String? expectedTagName = null) {
String? tagName = openTags.deleteLast();
assert (exists tagName);
if (exists expectedTagName) {
assert (tagName.equals(expectedTagName));
}
closeStart();
buffer.append("</");
buffer.append(tagName);
buffer.append(">");
return this;
}
shared TagOutput endEmpty() {
assert (state == tagOpen);
String? tagName = openTags.deleteLast();
assert (exists tagName);
buffer.append("/>");
state = elementContent;
return this;
}
shared TagOutput text(String text) {
closeStart();
quoteText(text);
return this;
}
void closeStart() {
switch (state)
case (elementContent) {
// Ignore
}
case (tagOpen) {
buffer.append(">");
state = elementContent;
}
case (attributeOpen) {
"Missing call to `closeAttribute`."
assert (false);
}
}
void quoteText(String val) {
val.each((ch) {
switch (ch)
case ('<') {
buffer.append("<");
}
case ('>') {
buffer.append(">");
}
case ('&') {
buffer.append("&");
}
else {
buffer.appendCharacter(ch);
}
});
}
shared TagOutput attributeValue(String val) {
assert (state == attributeOpen);
val.each((ch) {
switch (ch)
case ('<') {
buffer.append("<");
}
case ('>') {
buffer.append(">");
}
case ('&') {
buffer.append("&");
}
case ('"') {
buffer.append(""");
}
else {
buffer.appendCharacter(ch);
}
});
return this;
}
}
"Run the module `tagout`."
shared void run() {
value output = TagOutput();
for (n in 0..20) {
output.tag("div").attribute("id", "c" + n.string).attribute("class", "foo");
for (x in 0..4) {
output.tag("span").attribute("class", "bar");
output.text("some text ");
output.end("span");
}
output.end("div");
}
dynamic {
dynamic div = window.document.createElement("div");
div.innerHTML = output.string;
window.document.body.appendChild(div);
}
}
In the UI, I experienced a long delay for that operation. Using the browser’s profiler, the problem is obvious: For generating this 4k document, 6M calls to String.charCodeAt
and 6M calls to String.countCodepoints
took almost all the time:
I’m almost sure, that the problem is not in my code… any thoughts?
Issue Analytics
- State:
- Created 8 years ago
- Comments:28 (21 by maintainers)
Top Results From Across the Web
What is the efficiency of javascript .toUpperCase method?
1 Answer 1 ... The algorithm described in String.prototype.toLowerCase ( ) of the specification is O(n). Each codepoint of the original String (or ......
Read more >Adventures in the land of substrings and RegExps.
Whenever you see a string value in your JavaScript code - it can actually be backed by any of those representations, runtime is...
Read more >Performance with JavaScript String Objects - Mozilla Hacks
This article aims to take a look at the performance of JavaScript engines towards primitive value Strings and Object Strings.
Read more >Let's talk about Javascript string encoding | Kevin Burke
First, some very basics about string encoding. ... It would be inefficient to waste 4 bytes on every "a" in the document -...
Read more >Top Related Medium Post
No results found
Top Related StackOverflow Question
No results found
Troubleshoot Live Code
Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start FreeTop Related Reddit Thread
No results found
Top Related Hackernoon Post
No results found
Top Related Tweet
No results found
Top Related Dev.to Post
No results found
Top Related Hashnode Post
No results found
Top GitHub Comments
… ok the problem are my glasses, not the machine… I just confused the rows…
So, was the problem really just with
append
? If so, I ran a test to append 4000 1-char strings to a StringBuilder and it takes under 3s on my shitty laptop.Anyway, the code in the original issue description runs really fast now, so I’m closing this.