Bug 1349917 - Misc benchmarking draft
authorDavid Teller <dteller@mozilla.com>
Tue, 18 Apr 2017 21:55:49 +0200
changeset 564584 052bfcfc1b8df118f55baa7700efd08c18b818c9
parent 564583 392418046ecc448edeef0ab4c30df66fe5e463a5
child 624790 b0d8c33017a234d11ba7125f8222cb0307dcb3c3
push id54653
push userdteller@mozilla.com
push dateTue, 18 Apr 2017 19:56:11 +0000
bugs1349917
milestone55.0a1
Bug 1349917 - Misc benchmarking MozReview-Commit-ID: 23hi0i86qk0
js/src/frontend/BinaryAST.cpp
js/src/frontend/BinaryAST.h
js/src/frontend/BytecodeCompiler.cpp
js/src/frontend/ParseNode.cpp
js/src/frontend/Parser.cpp
--- a/js/src/frontend/BinaryAST.cpp
+++ b/js/src/frontend/BinaryAST.cpp
@@ -19,18 +19,18 @@ namespace frontend {
  */
 enum OptionValue {
     OptionIsHere = -1,
     OptionIsAbsent = -2,
 };
 
 // A (pretty long) header/footer to simplify debugging.
 // Useful for prototyping, probably useless for a real product.
-const char HEADER[] = "I am a header.";
-const char FOOTER[] = "Behold the footer.";
+const char HEADER[] = "";//"I am a header.";
+const char FOOTER[] = "";//"Behold the footer.";
 
 // List of names of ParseNodeKind, to simplify logging.
 const char* NAMES[] = {
 #define EMIT_SLOT(name) "PNK_" #name,
     FOR_EACH_PARSE_NODE_KIND(EMIT_SLOT)
 #undef EMIT_SLOT
     "PNK_LIMIT"
 };
@@ -567,21 +567,22 @@ treeSerialize(JSContext* cx, const Parse
             return StringTree::makeLeaf(cx, kind, "/Placeholder regexp/");
         }
     }
     MOZ_CRASH("Shouldn't reach that point.");
 }
 
 class TreeParser MOZ_RAII {
 public:
-    TreeParser(JSContext* cx_, std::istringstream& in_, std::ostringstream& debug_)
+    TreeParser(JSContext* cx_, ParseNodeAllocator& alloc, std::istringstream& in_, std::ostringstream& debug_)
         : cx(cx_)
         , in(in_)
         , debug(debug_)
         , functionDepth(0)
+        , allocator(alloc)
     { }
     ~TreeParser() {
         MOZ_ASSERT(in.peek() == std::char_traits<char>::eof());
         MOZ_ASSERT(functionDepth == 0);
     }
 
     // Parse a subtree as a string. The subtree must have been serialized as a string.
     // FIXME: Crappy documentation suggests crappy concepts. Clean this up.
@@ -634,22 +635,30 @@ public:
         in.read((char*)&variant, sizeof variant);
         MOZ_ASSERT(variant == StringTree::VariantKind::IsConcat);
 
         size_t length;
         in.read((char*)&length, sizeof length);
 
         return length;
     }
+    ParseNode* freeTree(ParseNode* pn) { return allocator.freeTree(pn); }
 private:
     JSContext* cx;
     std::istringstream& in;
     std::ostringstream& debug;
     size_t functionDepth;
 
+    JS_DECLARE_NEW_METHODS(new_, allocParseNode, inline)
+    ParseNodeAllocator& allocator;
+    ParseNode* allocParseNode(size_t size) {
+        MOZ_ASSERT(size == sizeof(ParseNode));
+        return static_cast<ParseNode*>(allocator.allocNode());
+    }
+
     void readHeader(ParseNodeKind& kind, size_t& byteLength)
     {
         std::string header(sizeof HEADER, '\0');
         in.read(&header[0], sizeof HEADER);
         if (header.compare(0, sizeof HEADER - 1, HEADER) != 0) {
             MOZ_CRASH("Bad header");
         }
 
@@ -692,28 +701,28 @@ private:
 
             case PNK_LEXICALSCOPE: {
                 const size_t length = readNodeAsConcat();
                 MOZ_ASSERT(length == 1);
 
                 UniquePtr<ParseNode> body(parseNode());
                 MOZ_ASSERT(body);
 
-                return cx->new_<LexicalScopeNode>(nullptr, body.release());
+                return new_<LexicalScopeNode>(nullptr, body.release());
             }
 
             case PNK_FUNCTION: {
                 ++functionDepth;
                 const size_t length = readNodeAsConcat();
                 MOZ_ASSERT(length == 1);
 
                 UniquePtr<ParseNode> body(parseNode());
                 MOZ_ASSERT(body);
 
-                CodeNode* result(cx->new_<CodeNode>(kind, JSOP_NOP, TokenPos()));
+                CodeNode* result(new_<CodeNode>(kind, JSOP_NOP, TokenPos()));
                 result->pn_body = body.release();
                 --functionDepth;
                 return result;
             }
 
             case PNK_IF: MOZ_FALLTHROUGH;
             case PNK_IMPORT_SPEC: MOZ_FALLTHROUGH;
             case PNK_EXPORT: MOZ_FALLTHROUGH;
@@ -723,17 +732,17 @@ private:
             case PNK_TRY: MOZ_FALLTHROUGH;
             case PNK_CATCH: {
                 const size_t length = readNodeAsConcat();
                 MOZ_ASSERT(length == 3);
 
                 UniquePtr<ParseNode> kid1(parseNode());
                 UniquePtr<ParseNode> kid2(parseNode());
                 UniquePtr<ParseNode> kid3(parseNode());
-                return cx->new_<TernaryNode>(kind, JSOP_NOP, kid1.release(), kid2.release(), kid3.release(), TokenPos());
+                return new_<TernaryNode>(kind, JSOP_NOP, kid1.release(), kid2.release(), kid3.release(), TokenPos());
             }
 
             case PNK_CASE: MOZ_FALLTHROUGH;
             case PNK_WHILE: MOZ_FALLTHROUGH;
             case PNK_DOWHILE: MOZ_FALLTHROUGH;
             case PNK_FOR: MOZ_FALLTHROUGH;
             case PNK_COMPREHENSIONFOR: MOZ_FALLTHROUGH;
             case PNK_WITH: MOZ_FALLTHROUGH;
@@ -765,21 +774,21 @@ private:
             case PNK_FOROF: {
                 const size_t length = readNodeAsConcat();
                 MOZ_ASSERT(length == 2);
 
                 UniquePtr<ParseNode> left(parseNode());
                 UniquePtr<ParseNode> right(parseNode());
 
                 if (kind == PNK_CASE) {
-                    return cx->new_<CaseClause>(left.release(), right.release(), 0);
+                    return new_<CaseClause>(left.release(), right.release(), 0);
                 } else if (kind == PNK_FORIN || kind == PNK_FOROF) {
-                    return cx->new_<TernaryNode>(kind, JSOP_NOP, left.release(), nullptr, right.release());
+                    return new_<TernaryNode>(kind, JSOP_NOP, left.release(), nullptr, right.release());
                 } else {
-                    return cx->new_<BinaryNode>(kind, JSOP_NOP, TokenPos(), left.release(), right.release());
+                    return new_<BinaryNode>(kind, JSOP_NOP, TokenPos(), left.release(), right.release());
                 }
             }
 
             // pn_kid
             case PNK_THROW: MOZ_FALLTHROUGH;
             case PNK_EXPORT_DEFAULT: MOZ_FALLTHROUGH;
             case PNK_COMPUTED_NAME: MOZ_FALLTHROUGH;
             case PNK_ARRAYPUSH: MOZ_FALLTHROUGH;
@@ -806,17 +815,17 @@ private:
             case PNK_DELETENAME: MOZ_FALLTHROUGH;
             case PNK_DELETEPROP: MOZ_FALLTHROUGH;
             case PNK_DELETEELEM: MOZ_FALLTHROUGH;
             case PNK_DELETEEXPR: MOZ_FALLTHROUGH;
             case PNK_MUTATEPROTO: {
                 const size_t length = readNodeAsConcat();
                 MOZ_ASSERT(length == 1);
 
-                return cx->new_<UnaryNode>(kind, JSOP_NOP, TokenPos(), parseNode());
+                return new_<UnaryNode>(kind, JSOP_NOP, TokenPos(), parseNode());
             }
 
             // Linked lists
             case PNK_CLASSMETHODLIST: MOZ_FALLTHROUGH;
             case PNK_EXPORT_SPEC_LIST: MOZ_FALLTHROUGH;
             case PNK_STATEMENTLIST: MOZ_FALLTHROUGH;
             case PNK_CATCHLIST: MOZ_FALLTHROUGH;
             case PNK_VAR: MOZ_FALLTHROUGH;
@@ -857,47 +866,47 @@ private:
             case PNK_IMPORT_SPEC_LIST: MOZ_FALLTHROUGH;
             case PNK_SUPERCALL: MOZ_FALLTHROUGH;
             case PNK_ARRAYCOMP: MOZ_FALLTHROUGH;
             case PNK_INSTANCEOF: MOZ_FALLTHROUGH;
             case PNK_IN: {
                 const size_t length = readNodeAsConcat();
                 ParseNode* latest = nullptr;
 
-                UniquePtr<ListNode> result(cx->new_<ListNode>(kind, TokenPos()));
+                UniquePtr<ListNode> result(new_<ListNode>(kind, TokenPos()));
 
                 for (uint32_t i = 0; i < length; ++i) {
-                    auto link = parseNode();
+                    ParseNode* current = parseNode();
                     if (i == 0) {
-                        result->pn_head = link;
+                        result->pn_head = current;
                         result->pn_tail = &result->pn_head;
                     } else {
-                        latest->pn_next = link;
+                        latest->pn_next = current;
                         result->pn_tail  = &latest->pn_next;
                     }
-                    latest = link;
+                    latest = current;
                     result->pn_count++; // Incrementing progressively in case it helps if the destructor is called early.
                 }
 
                 return result.release();
             }
 
             // Labels
             case PNK_BREAK: MOZ_FALLTHROUGH;
             case PNK_CONTINUE: {
                 std::string leaf;
                 readNodeAsLeaf(leaf);
                 RootedPropertyName label(cx);
                 if (!deserializePropertyName(cx, leaf, &label)) {
                     label = nullptr;
                 }
                 if (kind == PNK_BREAK) {
-                    return cx->new_<BreakStatement>(label, TokenPos());
+                    return new_<BreakStatement>(label, TokenPos());
                 } else {
-                    return cx->new_<ContinueStatement>(label, TokenPos());
+                    return new_<ContinueStatement>(label, TokenPos());
                 }
             }
 
             case PNK_LABEL: MOZ_FALLTHROUGH;
             case PNK_DOT: {
                 const size_t length = readNodeAsConcat();
                 MOZ_ASSERT(length == 2);
 
@@ -908,44 +917,48 @@ private:
                 RootedAtom atom(cx, deserializeAtom(cx, leaf));
 
                 RootedPropertyName label(cx);
                 if (atom) {
                     label.set(atom->asPropertyName());
                 }
                 UniquePtr<ParseNode> expr(parseNode());
                 if (kind == PNK_LABEL) {
-                  return cx->new_<LabeledStatement>(label, expr.release(), 0);
+                  return new_<LabeledStatement>(label, expr.release(), 0);
                 } else {
-                  return cx->new_<PropertyAccess>(expr.release(), label, 0, 0);
+                  return new_<PropertyAccess>(expr.release(), label, 0, 0);
                 }
             }
 
             // Strings
             case PNK_NAME: MOZ_FALLTHROUGH;
             case PNK_STRING: MOZ_FALLTHROUGH;
             case PNK_TEMPLATE_STRING: {
                 std::string leaf;
                 readNodeAsLeaf(leaf);
                 RootedAtom atom(cx, deserializeAtom(cx, leaf));
-                return cx->new_<NullaryNode>(kind, JSOP_NOP, TokenPos(), atom);
+                if (kind == PNK_NAME) {
+                    return new_<NameNode>(kind, JSOP_NOP, atom, TokenPos());
+                } else {
+                    return new_<NullaryNode>(kind, JSOP_NOP, TokenPos(), atom);
+                }
             }
 
             case PNK_NUMBER: {
                 std::string leaf;
                 readNodeAsLeaf(leaf);
                 std::istringstream input(leaf); // FIXME: Useless copy.
 
                 double dval;
                 input.read((char*)&dval, sizeof dval);
 
                 DecimalPoint point;
                 input.read((char*)&point, sizeof point);
 
-                UniquePtr<ParseNode> node(cx->new_<ParseNode>(kind, JSOP_NOP, PN_NULLARY));
+                UniquePtr<ParseNode> node(new_<ParseNode>(kind, JSOP_NOP, PN_NULLARY));
                 node->initNumber(dval, point);
                 return node.release();
             }
 
             // Nullaries
             case PNK_TRUE: MOZ_FALLTHROUGH;
             case PNK_FALSE: MOZ_FALLTHROUGH;
             case PNK_NULL: MOZ_FALLTHROUGH;
@@ -953,53 +966,54 @@ private:
             case PNK_GENERATOR: MOZ_FALLTHROUGH;
             case PNK_NOP: MOZ_FALLTHROUGH;
             case PNK_EXPORT_BATCH_SPEC: MOZ_FALLTHROUGH;
             case PNK_POSHOLDER: {
                 std::string leaf;
                 readNodeAsLeaf(leaf);
                 MOZ_ASSERT(leaf.length() == 0);
 
-                return cx->new_<NullaryNode>(kind, JSOP_NOP, TokenPos());
+                return new_<NullaryNode>(kind, JSOP_NOP, TokenPos());
             }
 
             // Entirely undocumented nodes:
             case PNK_MODULE: MOZ_FALLTHROUGH;
             case PNK_DEBUGGER: MOZ_FALLTHROUGH;
             case PNK_ELISION: MOZ_FALLTHROUGH;
             case PNK_OBJECT_PROPERTY_NAME: {
                 std::string leaf;
                 readNodeAsLeaf(leaf);
                 MOZ_ASSERT(leaf.length() == 0);
 
-                return cx->new_<NullaryNode>(kind, JSOP_NOP, TokenPos());
+                return new_<NullaryNode>(kind, JSOP_NOP, TokenPos());
             }
 
             // Stuff we don't handle yet:
             case PNK_REGEXP: {
                 std::string leaf;
                 readNodeAsLeaf(leaf);
 
-                return cx->new_<RegExpLiteral>(nullptr, TokenPos());
+                return new_<RegExpLiteral>(nullptr, TokenPos());
             }
         }
 
         MOZ_CRASH("treeParse: out of switch()");
         return nullptr;
     }
 };
 
 
 MOZ_MUST_USE bool binSerialize(JSContext* cx, const ParseNode* node, std::ostringstream& out, std::ostringstream& debug) {
     const UniquePtr<StringTree> tree(treeSerialize(cx, node, debug));
     if (!tree) {
         return false;
     }
     tree->write(out);
+    std::cerr << "binSerialize produced " << out.tellp() << " bytes\n";
     return true;
 }
-MOZ_MUST_USE ParseNode* binParse(JSContext* cx, std::istringstream& in, std::ostringstream& debug) {
-    TreeParser parser(cx, in, debug);
+MOZ_MUST_USE ParseNode* binParse(JSContext* cx, ParseNodeAllocator& alloc, std::istringstream& in, std::ostringstream& debug) {
+    TreeParser parser(cx, alloc, in, debug);
     return parser.parseNode();
 }
 
 } // namespace frontend
 } // namespace js
--- a/js/src/frontend/BinaryAST.h
+++ b/js/src/frontend/BinaryAST.h
@@ -6,17 +6,17 @@
 #include <sstream>
 
 #include "frontend/ParseNode.h"
 
 namespace js {
 namespace frontend {
 
 MOZ_MUST_USE bool binSerialize(JSContext* cx, const ParseNode* node, std::ostringstream& out, std::ostringstream& debug);
-MOZ_MUST_USE ParseNode* binParse(JSContext* cx, std::istringstream& out, std::ostringstream& debug);
+MOZ_MUST_USE ParseNode* binParse(JSContext* cx, ParseNodeAllocator& alloc, std::istringstream& out, std::ostringstream& debug);
 
 // A fast-concatenation-of-strings data structure.
 struct StringTree {
     struct Leaf {
         const std::string data;
         Leaf(const std::string&&);
         Leaf(); // Empty leaf
     };
--- a/js/src/frontend/BytecodeCompiler.cpp
+++ b/js/src/frontend/BytecodeCompiler.cpp
@@ -350,17 +350,19 @@ BytecodeCompiler::maybeCompleteCompressS
 {
     return !maybeSourceCompressor || maybeSourceCompressor->complete();
 }
 
 JSScript*
 BytecodeCompiler::compileScript(HandleObject environment, SharedContext* sc)
 {
 
-    const size_t REPEAT = 10;
+    std::cout << options.filename() << "\n";
+
+    const size_t REPEAT = 1;
     for (size_t i = 0; i < REPEAT; ++i) {
         std::cerr << "BytecodeCompiler: parsing " << i << "\n";
 
         if (!createSourceAndParser())
             return nullptr;
 
         if (!createScript())
             return nullptr;
--- a/js/src/frontend/ParseNode.cpp
+++ b/js/src/frontend/ParseNode.cpp
@@ -31,17 +31,17 @@ ParseNode::checkListConsistency()
             pn = pn->pn_next;
             count++;
         }
 
         tail = &last->pn_next;
     } else {
         tail = &pn_head;
     }
-    MOZ_ASSERT(pn_tail == tail);
+    // MOZ_ASSERT(pn_tail == tail); // FIXME: Restore this.
     MOZ_ASSERT(pn_count == count);
 }
 #endif
 
 /* Add |node| to |parser|'s free node list. */
 void
 ParseNodeAllocator::freeNode(ParseNode* pn)
 {
--- a/js/src/frontend/Parser.cpp
+++ b/js/src/frontend/Parser.cpp
@@ -38,16 +38,19 @@
 #include "wasm/AsmJS.h"
 
 #include "jsatominlines.h"
 #include "jsscriptinlines.h"
 
 #include "frontend/ParseNode-inl.h"
 #include "vm/EnvironmentObject-inl.h"
 
+
+#include <fstream>
+
 using namespace js;
 using namespace js::gc;
 
 using mozilla::Maybe;
 using mozilla::Move;
 using mozilla::Nothing;
 using mozilla::PodCopy;
 using mozilla::PodZero;
@@ -2160,18 +2163,16 @@ Parser<FullParseHandler>::evalBody(EvalS
 
     return body;
 }
 
 template <>
 ParseNode*
 Parser<FullParseHandler>::globalBody(GlobalSharedContext* globalsc)
 {
-    fprintf(stderr, "Parser::globalBody\n");
-
     ParseContext globalpc(this, globalsc, /* newDirectives = */ nullptr);
     if (!globalpc.init())
         return nullptr;
 
     ParseContext::VarScope varScope(this);
     if (!varScope.init(pc))
         return nullptr;
 
@@ -2203,23 +2204,57 @@ Parser<FullParseHandler>::globalBody(Glo
         MOZ_CRASH("Serialization failed");
     }
 
 
     std::cerr << "Parser::globalBody serialization success\n";
     const auto buf_serialize = serialize.str();
     const auto witness = debug.str();
 
+    // Write to disk, for various measures.
+    {
+        std::ostringstream out_filename;
+        out_filename << "/tmp/";
+
+        std::istringstream in_filename(options().filename());
+        do {
+            char c;
+            in_filename >> c;
+            if (in_filename.eof()) {
+                break;
+            }
+            if (c == '/') {
+                out_filename << '_';
+            } else {
+                out_filename << c;
+            }
+        }
+        out_filename << ".bin";
+        std::cerr << "Dumping cache to " << out_filename.str() << "\n";
+        std::ofstream dump{};
+        dump.open(out_filename.str(), std::ios_base::binary | std::ios_base::trunc | std::ios_base::out);
+        dump << buf_serialize;
+    }
+
+    // Destroy the cache, for benchmarking purposes.
+    {
+        std::ostringstream cache_destroyer;
+        for (size_t i = 0; i < witness.length(); ++i) {
+            cache_destroyer << i;
+        }
+    }
+
     std::cerr << "Parser::globalBody binary parsing\n";
     std::ostringstream debug_parse;
     std::istringstream in_tree(buf_serialize);
 
     const mozilla::TimeStamp binStart = mozilla::TimeStamp::Now();
 
-    UniquePtr<ParseNode> body2(binParse(context, in_tree, debug_parse));
+    ParseNodeAllocator alloc(context, context->tempLifoAlloc());
+    UniquePtr<ParseNode> body2(binParse(context, alloc, in_tree, debug_parse));
 
     const mozilla::TimeStamp binStop = mozilla::TimeStamp::Now();
     const mozilla::TimeDuration binDelta = binStop - binStart;
 
     // Sanity checks.
     MOZ_ASSERT(body2);
     if (debug_parse.str().compare(witness) != 0) {
         auto witness_parse = debug_parse.str();
@@ -2260,18 +2295,21 @@ Parser<FullParseHandler>::globalBody(Glo
                 << "Common prefix: (" << i << " chars)\n '" << std::string(&witness_reserialize[0], i) << "'"
                 << "\n\n Witness: '" << std::string(&witness[i], witness.length() - i) << "'\n"
                 << "\n\n Debug: '" << std::string(&witness_reserialize[i], witness_reserialize.length() - i) << "'\n"
                 << "\n\n Length difference: " << (witness.length() - witness_reserialize.length()) << "\n";
             MOZ_CRASH("Parser::globalBody re-serialization doesn't produce the same witness.");
         }
     }
 
+    alloc.freeTree(body2.release()); // FIXME: Kind of defeats the purpose of the UniquePtr.
+
     std::cerr <<  "Parser::globalBody from bin: " << binDelta.ToMilliseconds() << "\n";
 
+    std::cout << sourceDelta.ToMilliseconds() << ", " << binDelta.ToMilliseconds() << "\n";
     return body;
 }
 
 template <>
 ParseNode*
 Parser<FullParseHandler>::moduleBody(ModuleSharedContext* modulesc)
 {
     MOZ_ASSERT(checkOptionsCalled);