惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

人人都是产品经理
人人都是产品经理
D
Docker
GbyAI
GbyAI
B
Blog RSS Feed
博客园 - 司徒正美
博客园 - Franky
美团技术团队
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
aimingoo的专栏
aimingoo的专栏
C
Check Point Blog
IT之家
IT之家
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
www.infosecurity-magazine.com
www.infosecurity-magazine.com
AI
AI
O
OpenAI News
Attack and Defense Labs
Attack and Defense Labs
cs.CV updates on arXiv.org
cs.CV updates on arXiv.org
T
Tailwind CSS Blog
酷 壳 – CoolShell
酷 壳 – CoolShell
S
Secure Thoughts
博客园 - 聂微东
L
LINUX DO - 最新话题
U
Unit 42
SecWiki News
SecWiki News
A
Arctic Wolf
Schneier on Security
Schneier on Security
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
V
Visual Studio Blog
量子位
The Cloudflare Blog
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
大猫的无限游戏
大猫的无限游戏
Google DeepMind News
Google DeepMind News
G
Google Developers Blog
T
Threat Research - Cisco Blogs
TaoSecurity Blog
TaoSecurity Blog
Recent Commits to openclaw:main
Recent Commits to openclaw:main
B
Blog
博客园 - 【当耐特】
C
CERT Recently Published Vulnerability Notes
Scott Helme
Scott Helme
Last Week in AI
Last Week in AI
D
Darknet – Hacking Tools, Hacker News & Cyber Security
Microsoft Security Blog
Microsoft Security Blog
Apple Machine Learning Research
Apple Machine Learning Research
F
Full Disclosure
Hacker News: Ask HN
Hacker News: Ask HN
A
About on SuperTechFans
博客园 - 三生石上(FineUI控件)
Latest news
Latest news

Henri Sivonen’s pages

Parin vuoden tutkimattomuus crates.io: Rust Package Registry Asiakirjatonta toimintaa It’s not wrong that "🤦🏼‍♂️".length == 7 Koulutartuntojen tilastointimenettely Perusteasiakirjoja hallussapitämättä ikärajoitettu Asiantuntijat ja nukkuva vallan vahtikoira Koronapassilausunto Suppealla tietopohjalla ohimeneväksi väitetty Text Encoding Menu in 2021 The Text Encoding Submenu Is Gone An HTML5 Conformance Checker Not Part of the Technology Stack Browser Technology Stack Bogo-XML Declaration Returns to Gecko A Look at Encoding Detection and Encoding Menu Telemetry from Firefox 86 Why Supporting Unlabeled UTF-8 in HTML on the Web Would Be Problematic Rust Target Names Aren’t Passed to LLVM Toimintamalli Activating Browser Modes with Doctype Johtopäätöksiä mallin rakenteesta Tehtävänmäärittelyä kirjoittamatta ja kuolemia laskematta laumasuojamallinnettu Character Encoding Menu in 2014 Erillissuosituksen tarpeettomuudesta yleissuosituksen poikkeukseksi? STM:n maskiaikajana Rust 2021 Oma-aloitteisesti mallinnettu Kokopinovaatimuksin kilpailutettu chardetng: A More Compact Character Encoding Detector for the Legacy Web Varauksia paisutellen tiedotettu Perusteasiakirjoitta tiedotettu Always Use UTF-8 & Always Label Your HTML Saying So IME Smoke Testing The Validator.nu HTML Parser About the Hiragino Fonts with CSS It’s Time to Stop Adding New Features for Non-Unicode Execution Encodings in C++ Rust 2020 The Last of the Parsing Quirks About about:blank Rust 2019 a Web-Compatible Character Encoding Library in Rust How I Wrote a Modern C++ Library in Rust Using cargo-fuzz to Transfer Code Review of Simple Safe Code to Complex Code that Uses unsafe A Rust Crate that Also Quacks Like a Modern C++ Library #Rust2018 No Namespaces in JSON, Please A Lecture about HTML5 Julkisesti luotettu varmenne ikidomainille TLS:ää (SSL:ää) varten -webkit-HTML5 Lists in Attribute Values The Sad Story of PNG Gamma “Correction” If You Want Software Freedom on Phones, You Should Work on Firefox OS, Custom Hardware and Web App Self-Hostablility HTML5 Parser Improvements ARIA in HTML5 Integration: Document Conformance (Draft, Take Two) Schema.org and Pre-Existing Communities Lowering memory requirements by replacing Schematron HTML5 Parsing in Gecko: A Build Introducing SAX Tree NVDL Support in Validator.nu HOWTO Avoid Being Called a Bozo When Producing XML An Unofficial Q&A about the Discontinuation of the XHTML2 WG Thoughts on HTML5 Becoming a W3C Recommendation Four Finnish Banks Training Users to Give Banking Credentials to Another Site Unimpressed by Leopard Sergeant Semantics The Content Sink Inheritance Diagram – 2006-06-30 What is EME? About Points and Pixels as Units The Performance Cost of the HTML Tree Builder Social Media Impression Management The spacer Element Is Gone Openmind 2006 Performance Mistake XHTML and Mobile Devices WebM-Enabled Browser Usage Share Exceeds H.264-Enabled Browser Usage Share on Desktop (in StatCounter Numbers) HTML5 Parser-Based View Source Syntax Highlighting Vendor Prefixes Are Hurting the Web Accept-Charset Is No More Dualroids Writing Structural Stylable Document in Mozilla Editor ISO-8859-15 on haitallinen Hourglass The Scientific Method According to Hixie Maemo Source Code Karpelan lukkovertaus ontuu Digitaalisesta arkistoinnista ARIA in HTML5 Integration: Document Conformance (Draft) XHTML—What’s the Point? (Draft, incomplete) Mac OS X Browser Comparison HOWTO Spot a Wannabe Web Standards Advocate An Idea About Intermediate Language Trees and Web UI Generation Thoughts on Using SSL/TLS Certificates as the Solution to Phishing Bureaucracy Meets the Web Europe Day HOWTO Establish a 100% Literacy Rate What to Do with All These Photos? Charmod Norm Checking Validator Web Service Interface Ideas DTDs Don’t Work on the Web EFFI’s Day in Court
DOM Traversal Performance – 2006-05-26
Henri Sivonen · 2011-12-22 · via Henri Sivonen’s pages

I’m working on a smarter text zoom for Firefox. It is a UI feature, and usually such UI features are implemented in JavaScript. JavaScript is easier to write and less bug-prone than C++.

But there is a problem. My JavaScript implementation is slow. It’s not too slow for normal Web pages. In fact, the algorithm works fast enough on a random portal-style news page. However, since the UI will be locked while the script runs the, I need to consider worse cases.

Today I’ve mostly been trying to figure out what to do about it. I’ve been running benchmarks and reading code on LXR.

I am using the Web Applications 1.0 spec as my real-world monster test case. (I realize that it is not a stable test case. However, today it was stable enough.)

My algorithm takes 24 seconds to run on the WA 1.0 spec on my dual core 2 GHz PowerMac G5! The code looks like this:

function countNonSpace(str) {
  var count = 0;
  // looks like JS does not support \p{Zs} etc. :-(
  // using characters below space plus Zs, Zp and Zl
  var pattern = /[\u0000-\u0020\u00A0\u1680\u180E\u2000-\u2009\u200A\u2028\u2029\u202F\u205F\u3000]/g;
  while (pattern(str)) {
    count++;
  }
  return str.length - count;
}

function countTextSizeChars(node, buckets, view) {
  if (!node) {
    return;
  }
  
  var sizeStack = new Array(0);
  var styleStack = new Array(0);
  
  var current = node;
  var next = null;
  for (;;) {
    switch (current.nodeType) {
      case Node.TEXT_NODE:
      case Node.CDATA_SECTION_NODE:
        var len = countNonSpace(current.data);
        if (len > 0) {
          var size = sizeStack[sizeStack.length - 2];
          if (size == -1) {
            var style = styleStack[styleStack.length - 2];
            size = sizeInPx(style.getPropertyValue("font-size"));
            if (size > MAX_BUCKET) {
              size = MAX_BUCKET;
            }
            sizeStack[sizeStack.length - 2] = size;
          }
          buckets[size] += len;
        }
        break;
      case Node.ELEMENT_NODE:
        // svg not supported
        if (current.namespaceURI == "http://www.w3.org/2000/svg") {
          break;
        }
        
        // have to call getComputedStyle for every element to
        // see which ones are being rendered :-(
        var style = view.getComputedStyle(current, null);
        styleStack[styleStack.length - 1] = style;
        if((style.getPropertyValue("display") == "none") ||
           (style.getPropertyValue("visibility") == "hidden")) {
          // this element is not being seen
          break;
        }
        
        var contentDocument = current.contentDocument;
        if (contentDocument) {
          // frame, object or similar
          var height = style.getPropertyValue("height");
          var width = style.getPropertyValue("width");
          if ((height != "auto") && (width != "auto") && 
              ((sizeInPx(height) < 130) ||
               (sizeInPx(width) < 130))) {
            // The frame or object is likely an ad or a tiny iframe for
            // some hack like remote DOM events. Staying out.
            break;
          } else {
            // descend recursively
            countTextSizeChars(
              contentDocument.documentElement, 
              buckets,
              contentDocument.defaultView
            );
          }
        } else {
          // not a frame, object or similar
          // descend iteratively
          if (next = current.firstChild) {
            current = next;
            sizeStack.push(-1);
            styleStack.push(null);
            continue;
          }
        }
        break;
      case Node.DOCUMENT_FRAGMENT_NODE:
      case Node.DOCUMENT_NODE:
        if (next = current.firstChild) {
          current = next;
          sizeStack.push(-1);
          styleStack.push(null);
          continue;
        }
        break;
    }
    for (;;) {
      if (next = current.nextSibling) {
        current = next;
        break;
      }
      current = current.parentNode;
      sizeStack.pop();
      styleStack.pop();
      if (current == node) {
        return;
      }
    }
  }
}

Let’s see if traversing the text content is the problem:

        // var len = countNonSpace(current.data);
        var len = 5;

Down to a bit over 17 seconds. Obviously, this part needs scrutiny but it is not the whole story. (I spent time poking around in LXR. DOM Inspector seems to be using IsOnlyWhitespace on nsITextContent. I have not figured out yet, how I can treat the contents of a text node as nsITextContent from JS.)

But let’s remove everything but the traversal algorithm itself:

function countTextSizeChars(node, buckets, view) {
  if (!node) {
    return;
  }
  
  var current = node;
  var next = null;
  for (;;) {
    switch (current.nodeType) {
      case Node.TEXT_NODE:
      case Node.CDATA_SECTION_NODE:
        break;
      case Node.ELEMENT_NODE:
      case Node.DOCUMENT_FRAGMENT_NODE:
      case Node.DOCUMENT_NODE:
        if (next = current.firstChild) {
          current = next;
          continue;
        }
        break;
    }
    for (;;) {
      if (next = current.nextSibling) {
        current = next;
        break;
      }
      current = current.parentNode;
      if (current == node) {
        return;
      }
    }
  }
}

Still a bit over 9 seconds. Ouch. It’s pretty bad if the tree traversal itself takes that much time. To get an idea how well the hardware can do, I tried the exact same algorithm on the same document (albeit without comment nodes) in Java. (I happen to have suitable code ready. In fact, the JavaScript code was adapted from my previous Java code.)

Using the Xerces DOM and the GNU JAXP DOM, the traversal took a tad under 7 milliseconds on average. That is, less than one thousandth of what it took in Gecko using JS! With the Crimson DOM, the traversal took a bit over 22 milliseconds on average.

How can Crimson be so much slower? Actually, I expected that. With the Xerces and GNU, the references to the first child and next sibling are stored in each element node. Crimson, on the other hand, stores an array of children.

Gecko also stores an array of children, but it is supposed to do some clever caching to make document order traversal using first child and next sibling fast, which is why I tried this algorithm in the first place even though I knew that the underlying implementation stores an array of children.

Obviously, traversing the content tree in C++ cannot be as slow as in JS, because Gecko can display the WA 1.0 spec reasonably fast. Also, the mere interpretativeness of JS shouldn’t carry a thousand-fold penalty.

My guess for the moment was that crossing the XPConnect boundary between JavaScript and compiled code was really inefficient, so I decided to test in Opera and Safari.

To do that, I made a local copy of the spec and copied the script into it. The script took 1.3 seconds in Opera and 1.7 seconds in Safari. Now I had to test that one in Firefox. It took 2.2 seconds! I loaded it into my test harness that I had been using earlier. Still only 2.5 seconds! Loaded the page from HTTP and ran the script. 9.6 seconds.

Looks like I have been benchmarking the performance of the security pricipals for the whole day. Aaargh!