Diff
Modified: trunk/Source/_javascript_Core/ChangeLog (198158 => 198159)
--- trunk/Source/_javascript_Core/ChangeLog 2016-03-14 21:44:27 UTC (rev 198158)
+++ trunk/Source/_javascript_Core/ChangeLog 2016-03-14 21:50:20 UTC (rev 198159)
@@ -1,3 +1,36 @@
+2016-03-14 Joseph Pecoraro <[email protected]>
+
+ Reduce generated JSON HeapSnapshot size
+ https://bugs.webkit.org/show_bug.cgi?id=155460
+
+ Reviewed by Geoffrey Garen.
+
+ Adjust the HeapSnapshot JSON to better reduce its size.
+ Changes include:
+
+ - avoid inner array groups and instead just have a large array for
+ nodes/edges. This removes lots of small array allocations.
+ - eliminate duplicate edges
+ - avoid duplicating edge names by including them in their own table;
+ - now both the nodes and edges lists hold only integers
+
+ * heap/HeapSnapshotBuilder.cpp:
+ (JSC::HeapSnapshotBuilder::json):
+ Add some more documentation for the slightly modified format.
+ While generating, clear data structures as early as possible.
+
+ * heap/HeapSnapshotBuilder.h:
+ (JSC::HeapSnapshotEdge::HeapSnapshotEdge):
+ During JSON building, the edge's cell pointers are converted to the
+ identifier they point to. This avoids having to re-lookup the identifier.
+
+ * tests/heapProfiler/driver/driver.js:
+ (CheapHeapSnapshotEdge):
+ (CheapHeapSnapshot):
+ (CheapHeapSnapshot.prototype.edgeNameFromTableIndex):
+ (HeapSnapshot):
+ Update test driver for slightly different snapshot format.
+
2016-03-14 Keith Miller <[email protected]>
We should be able to eliminate cloned arguments objects that use the length property
Modified: trunk/Source/_javascript_Core/heap/HeapSnapshotBuilder.cpp (198158 => 198159)
--- trunk/Source/_javascript_Core/heap/HeapSnapshotBuilder.cpp 2016-03-14 21:44:27 UTC (rev 198158)
+++ trunk/Source/_javascript_Core/heap/HeapSnapshotBuilder.cpp 2016-03-14 21:50:20 UTC (rev 198159)
@@ -133,22 +133,41 @@
// {
// "version": 1.0,
// "nodes": [
-// [<nodeId>, <sizeInBytes>, <nodeClassNameIndex>, <optionalInternal>], ...
+// <nodeId>, <sizeInBytes>, <nodeClassNameIndex>, <internal>,
+// <nodeId>, <sizeInBytes>, <nodeClassNameIndex>, <internal>,
+// ...
// ],
// "nodeClassNames": [
// "string", "Structure", "Object", ...
// ],
// "edges": [
-// [<fromNodeId>, <toNodeId>, <edgeTypeIndex>, <optionalEdgeExtraData>], ...
+// <fromNodeId>, <toNodeId>, <edgeTypeIndex>, <edgeExtraData>,
+// <fromNodeId>, <toNodeId>, <edgeTypeIndex>, <edgeExtraData>,
+// ...
// ],
// "edgeTypes": [
// "Internal", "Property", "Index", "Variable"
+// ],
+// "edgeNames": [
+// "propertyName", "variableName", ...
// ]
// }
//
-// FIXME: Possible compaction improvements:
-// - eliminate inner array groups and just have a single list with fixed group sizes (meta data section).
-// - eliminate duplicate edge extra data strings, have an index into a de-duplicated like edgeTypes / nodeClassNames.
+// Notes:
+//
+// <nodeClassNameIndex>
+// - index into the "nodeClassNames" list.
+//
+// <internal>
+// - 0 = false, 1 = true.
+//
+// <edgeTypeIndex>
+// - index into the "edgeTypes" list.
+//
+// <edgeExtraData>
+// - for Internal edges this should be ignored (0).
+// - for Index edges this is the index value.
+// - for Property or Variable edges this is an index into the "edgeNames" list.
static uint8_t edgeTypeToNumber(EdgeType type)
{
@@ -186,8 +205,13 @@
// Build a list of used class names.
HashMap<const char*, unsigned> classNameIndexes;
- unsigned nextClassNameIndex = 0;
+ classNameIndexes.set("<root>", 0);
+ unsigned nextClassNameIndex = 1;
+ // Build a list of used edge names.
+ HashMap<UniquedStringImpl*, unsigned> edgeNameIndexes;
+ unsigned nextEdgeNameIndex = 0;
+
StringBuilder json;
auto appendNodeJSON = [&] (const HeapSnapshotNode& node) {
@@ -208,64 +232,48 @@
isInternal = !structure || !structure->globalObject();
}
- // [<nodeId>, <sizeInBytes>, <className>, <optionalInternalBoolean>]
+ // <nodeId>, <sizeInBytes>, <className>, <optionalInternalBoolean>
json.append(',');
- json.append('[');
json.appendNumber(node.identifier);
json.append(',');
json.appendNumber(node.cell->estimatedSizeInBytes());
json.append(',');
json.appendNumber(classNameIndex);
- if (isInternal)
- json.appendLiteral(",1");
- json.append(']');
+ json.append(',');
+ json.append(isInternal ? '1' : '0');
};
bool firstEdge = true;
auto appendEdgeJSON = [&] (const HeapSnapshotEdge& edge) {
- // If the from cell is null, this means a root edge.
- unsigned fromIdentifier = 0;
- if (edge.from) {
- auto fromLookup = allowedNodeIdentifiers.find(edge.from);
- if (fromLookup == allowedNodeIdentifiers.end())
- return;
- fromIdentifier = fromLookup->value;
- }
-
- unsigned toIdentifier = 0;
- if (edge.to) {
- auto toLookup = allowedNodeIdentifiers.find(edge.to);
- if (toLookup == allowedNodeIdentifiers.end())
- return;
- toIdentifier = toLookup->value;
- }
-
if (!firstEdge)
json.append(',');
firstEdge = false;
- // [<fromNodeId>, <toNodeId>, <edgeTypeIndex>, <optionalEdgeExtraData>],
- json.append('[');
- json.appendNumber(fromIdentifier);
+ // <fromNodeId>, <toNodeId>, <edgeTypeIndex>, <edgeExtraData>
+ json.appendNumber(edge.from.identifier);
json.append(',');
- json.appendNumber(toIdentifier);
+ json.appendNumber(edge.to.identifier);
json.append(',');
json.appendNumber(edgeTypeToNumber(edge.type));
+ json.append(',');
switch (edge.type) {
case EdgeType::Property:
- case EdgeType::Variable:
- json.append(',');
- json.appendQuotedJSONString(edge.u.name);
+ case EdgeType::Variable: {
+ auto result = edgeNameIndexes.add(edge.u.name, nextEdgeNameIndex);
+ if (result.isNewEntry)
+ nextEdgeNameIndex++;
+ unsigned edgeNameIndex = result.iterator->value;
+ json.appendNumber(edgeNameIndex);
break;
+ }
case EdgeType::Index:
- json.append(',');
json.appendNumber(edge.u.index);
break;
default:
// No data for this edge type.
+ json.append('0');
break;
}
- json.append(']');
};
json.append('{');
@@ -277,7 +285,7 @@
json.append(',');
json.appendLiteral("\"nodes\":");
json.append('[');
- json.appendLiteral("[0,0,\"<root>\"]");
+ json.appendLiteral("0,0,0,0"); // <root>
for (HeapSnapshot* snapshot = m_profiler.mostRecentSnapshot(); snapshot; snapshot = snapshot->previous()) {
for (auto& node : snapshot->m_nodes)
appendNodeJSON(node);
@@ -291,6 +299,7 @@
Vector<const char *> orderedClassNames(classNameIndexes.size());
for (auto& entry : classNameIndexes)
orderedClassNames[entry.value] = entry.key;
+ classNameIndexes.clear();
bool firstClassName = true;
for (auto& className : orderedClassNames) {
if (!firstClassName)
@@ -298,8 +307,42 @@
firstClassName = false;
json.appendQuotedJSONString(className);
}
+ orderedClassNames.clear();
json.append(']');
+ // Process edges.
+ // Replace pointers with identifiers.
+ // Remove any edges that we won't need.
+ m_edges.removeAllMatching([&] (HeapSnapshotEdge& edge) {
+ // If the from cell is null, this means a <root> edge.
+ if (!edge.from.cell)
+ edge.from.identifier = 0;
+ else {
+ auto fromLookup = allowedNodeIdentifiers.find(edge.from.cell);
+ if (fromLookup == allowedNodeIdentifiers.end())
+ return true;
+ edge.from.identifier = fromLookup->value;
+ }
+
+ if (!edge.to.cell)
+ edge.to.identifier = 0;
+ else {
+ auto toLookup = allowedNodeIdentifiers.find(edge.to.cell);
+ if (toLookup == allowedNodeIdentifiers.end())
+ return true;
+ edge.to.identifier = toLookup->value;
+ }
+
+ return false;
+ });
+ allowedNodeIdentifiers.clear();
+ m_edges.shrinkToFit();
+
+ // Sort edges based on from identifier.
+ std::sort(m_edges.begin(), m_edges.end(), [&] (const HeapSnapshotEdge& a, const HeapSnapshotEdge& b) {
+ return a.from.identifier < b.from.identifier;
+ });
+
// edges
json.append(',');
json.appendLiteral("\"edges\":");
@@ -321,6 +364,24 @@
json.appendQuotedJSONString(edgeTypeToString(EdgeType::Variable));
json.append(']');
+ // edge names
+ json.append(',');
+ json.appendLiteral("\"edgeNames\":");
+ json.append('[');
+ Vector<UniquedStringImpl*> orderedEdgeNames(edgeNameIndexes.size());
+ for (auto& entry : edgeNameIndexes)
+ orderedEdgeNames[entry.value] = entry.key;
+ edgeNameIndexes.clear();
+ bool firstEdgeName = true;
+ for (auto& edgeName : orderedEdgeNames) {
+ if (!firstEdgeName)
+ json.append(',');
+ firstEdgeName = false;
+ json.appendQuotedJSONString(edgeName);
+ }
+ orderedEdgeNames.clear();
+ json.append(']');
+
json.append('}');
return json.toString();
}
Modified: trunk/Source/_javascript_Core/heap/HeapSnapshotBuilder.h (198158 => 198159)
--- trunk/Source/_javascript_Core/heap/HeapSnapshotBuilder.h 2016-03-14 21:44:27 UTC (rev 198158)
+++ trunk/Source/_javascript_Core/heap/HeapSnapshotBuilder.h 2016-03-14 21:50:20 UTC (rev 198159)
@@ -57,36 +57,46 @@
};
struct HeapSnapshotEdge {
- HeapSnapshotEdge(JSCell* from, JSCell* to)
- : from(from)
- , to(to)
- , type(EdgeType::Internal)
- { }
+ HeapSnapshotEdge(JSCell* fromCell, JSCell* toCell)
+ : type(EdgeType::Internal)
+ {
+ from.cell = fromCell;
+ to.cell = toCell;
+ }
- HeapSnapshotEdge(JSCell* from, JSCell* to, EdgeType type, UniquedStringImpl* name)
- : from(from)
- , to(to)
- , type(type)
+ HeapSnapshotEdge(JSCell* fromCell, JSCell* toCell, EdgeType type, UniquedStringImpl* name)
+ : type(type)
{
ASSERT(type == EdgeType::Property || type == EdgeType::Variable);
+ from.cell = fromCell;
+ to.cell = toCell;
u.name = name;
}
- HeapSnapshotEdge(JSCell* from, JSCell* to, uint32_t index)
- : from(from)
- , to(to)
- , type(EdgeType::Index)
+ HeapSnapshotEdge(JSCell* fromCell, JSCell* toCell, uint32_t index)
+ : type(EdgeType::Index)
{
+ from.cell = fromCell;
+ to.cell = toCell;
u.index = index;
}
- JSCell* from;
- JSCell* to;
- EdgeType type;
union {
+ JSCell *cell;
+ unsigned identifier;
+ } from;
+
+ union {
+ JSCell *cell;
+ unsigned identifier;
+ } to;
+
+ union {
UniquedStringImpl* name;
uint32_t index;
} u;
+
+ EdgeType type;
};
class JS_EXPORT_PRIVATE HeapSnapshotBuilder {
Modified: trunk/Source/_javascript_Core/tests/heapProfiler/driver/driver.js (198158 => 198159)
--- trunk/Source/_javascript_Core/tests/heapProfiler/driver/driver.js 2016-03-14 21:44:27 UTC (rev 198158)
+++ trunk/Source/_javascript_Core/tests/heapProfiler/driver/driver.js 2016-03-14 21:50:20 UTC (rev 198159)
@@ -18,7 +18,7 @@
const nodeFirstEdgeOffset = 4;
const nodeNoEdgeValue = 0xffffffff; // UINT_MAX
-// [<0:from-id>, <1:to-id>, <2:typeTableIndex>, <3:data>]
+// [<0:fromId>, <1:toId>, <2:typeTableIndex>, <3:edgeDataIndexOrEdgeNameIndex>]
const edgeFieldCount = 4;
const edgeFromIdOffset = 0;
const edgeToIdOffset = 1;
@@ -60,7 +60,11 @@
this.fromId = edges[edgeIndex + edgeFromIdOffset];
this.toId = edges[edgeIndex + edgeToIdOffset];
this.type = snapshot.edgeTypeFromTableIndex(edges[edgeIndex + edgeTypeOffset]);
- this.data = "" + edgeDataOffset];
+
+ if (this.type === "Property" || this.type === "Variable")
+ this.data = "" + edgeDataOffset]);
+ else
+ this.data = "" + edgeDataOffset];
}
get from() { return this.snapshot.nodeWithIdentifier(this.fromId); }
@@ -71,41 +75,44 @@
{
constructor(json)
{
- let {nodes, nodeClassNames, edges, edgeTypes} = json;
+ let {nodes, nodeClassNames, edges, edgeTypes, edgeNames} = json;
this._nodes = new Uint32Array(nodes.length * nodeFieldCount);
this._edges = new Uint32Array(edges.length * edgeFieldCount);
this._nodeIdentifierToIndex = new Map; // <id> => index in _nodes
this._edgeTypesTable = edgeTypes;
+ this._edgeNamesTable = edgeNames;
this._nodeClassNamesTable = nodeClassNames;
let n = 0;
- nodes.forEach((nodePayload) => {
- let [id, size, classNameTableIndex, internal] = nodePayload;
- this._nodeIdentifierToIndex.set(id, n);
- this._nodes[n++] = id;
- this._nodes[n++] = size;
- this._nodes[n++] = classNameTableIndex;
- this._nodes[n++] = internal;
+ for (let i = 0; i < nodes.length;) {
+ this._nodeIdentifierToIndex.set(nodes[i], n);
+ this._nodes[n++] = nodes[i++]; // id
+ this._nodes[n++] = nodes[i++]; // size
+ this._nodes[n++] = nodes[i++]; // classNameTableIndex
+ this._nodes[n++] = nodes[i++]; // internal
this._nodes[n++] = nodeNoEdgeValue;
- });
+ }
let e = 0;
let lastNodeIdentifier = -1;
- edges.sort((a, b) => a[0] - b[0]).forEach((edgePayload) => {
- let [fromIdentifier, toIdentifier, edgeTypeTableIndex, data] = edgePayload;
+ for (let i = 0; i < edges.length;) {
+ let fromIdentifier = edges[i++]; // fromIdentifier
+ let toIdentifier = edges[i++]; // toIdentifier
+ assert(lastNodeIdentifier <= fromIdentifier, "Edge list should be ordered by from node identifier");
if (fromIdentifier !== lastNodeIdentifier) {
let nodeIndex = this._nodeIdentifierToIndex.get(fromIdentifier);
assert(this._nodes[nodeIndex + nodeIdOffset] === fromIdentifier, "Node lookup failed");
this._nodes[nodeIndex + nodeFirstEdgeOffset] = e;
lastNodeIdentifier = fromIdentifier;
}
+
this._edges[e++] = fromIdentifier;
this._edges[e++] = toIdentifier;
- this._edges[e++] = edgeTypeTableIndex;
- this._edges[e++] = data;
- });
+ this._edges[e++] = edges[i++]; // edgeTypeTableIndex
+ this._edges[e++] = edges[i++]; // data
+ }
}
get nodes() { return this._nodes; }
@@ -136,6 +143,11 @@
{
return this._edgeTypesTable[tableIndex];
}
+
+ edgeNameFromTableIndex(tableIndex)
+ {
+ return this._edgeNamesTable[tableIndex];
+ }
}
function createCheapHeapSnapshot() {
@@ -189,27 +201,39 @@
{
constructor(json)
{
- let {version, nodes, nodeClassNames, edges, edgeTypes} = json;
+ let {version, nodes, nodeClassNames, edges, edgeTypes, edgeNames} = json;
this.nodeMap = new Map;
- this.nodes = nodes.map((nodePayload) => {
- let [id, size, classNameIndex, internal] = nodePayload;
+ this.nodes = [];
+ for (let i = 0; i < nodes.length;) {
+ let id = nodes[i++];
+ let size = nodes[i++];
+ let classNameIndex = nodes[i++];
+ let internal = nodes[i++];
+
let node = new HeapSnapshotNode(id, nodeClassNames[classNameIndex], size, internal);
this.nodeMap.set(id, node);
- return node;
- });
+ this.nodes.push(node);
+ }
- edges.map((edgePayload) => {
- let [fromIdentifier, toIdentifier, edgeTypeIndex, data] = edgePayload;
+ for (let i = 0; i < edges.length;) {
+ let fromIdentifier = edges[i++];
+ let toIdentifier = edges[i++];
+ let edgeTypeIndex = edges[i++];
+ let data = ""
+
let from = this.nodeMap.get(fromIdentifier);
let to = this.nodeMap.get(toIdentifier);
assert(from, "Missing node for `from` part of edge");
assert(to, "Missing node for `to` part of edge");
+ let type = edgeTypes[edgeTypeIndex];
+ if (type === "Property" || type === "Variable")
+ data = ""
let edge = new HeapSnapshotEdge(from, to, edgeTypes[edgeTypeIndex], data);
from.outgoingEdges.push(edge);
to.incomingEdges.push(edge);
- });
+ }
this.rootNode = this.nodeMap.get(0);
assert(this.rootNode, "Missing <root> node with identifier 0");
Modified: trunk/Source/WebInspectorUI/ChangeLog (198158 => 198159)
--- trunk/Source/WebInspectorUI/ChangeLog 2016-03-14 21:44:27 UTC (rev 198158)
+++ trunk/Source/WebInspectorUI/ChangeLog 2016-03-14 21:50:20 UTC (rev 198159)
@@ -1,3 +1,14 @@
+2016-03-14 Joseph Pecoraro <[email protected]>
+
+ Reduce generated JSON HeapSnapshot size
+ https://bugs.webkit.org/show_bug.cgi?id=155460
+
+ Reviewed by Geoffrey Garen.
+
+ * UserInterface/Models/HeapSnapshot.js:
+ (WebInspector.HeapSnapshot.fromPayload):
+ Update for the slightly modified format.
+
2016-03-14 Commit Queue <[email protected]>
Unreviewed, rolling out r198095.
Modified: trunk/Source/WebInspectorUI/UserInterface/Models/HeapSnapshot.js (198158 => 198159)
--- trunk/Source/WebInspectorUI/UserInterface/Models/HeapSnapshot.js 2016-03-14 21:44:27 UTC (rev 198158)
+++ trunk/Source/WebInspectorUI/UserInterface/Models/HeapSnapshot.js 2016-03-14 21:50:20 UTC (rev 198159)
@@ -77,36 +77,39 @@
static fromPayload(payload)
{
- let {version, nodes, nodeClassNames, edges, edgeTypes} = payload;
+ let {version, nodes, nodeClassNames, edges, edgeTypes, edgeNames} = payload;
console.assert(version === 1, "Only know how to handle _javascript_Core Heap Snapshot Format Version 1");
console.assert(edgeTypes.every((type) => type in WebInspector.HeapSnapshotEdge.EdgeType), "Unexpected edge type", edgeTypes);
let nodeMap = new Map;
// Turn nodes into real nodes.
- for (let i = 0, length = nodes.length; i < length; ++i) {
- let nodePayload = nodes[i];
- let id = nodePayload[0];
- let size = nodePayload[1];
- let classNameIndex = nodePayload[2];
- let internal = nodePayload[3];
+ let processedNodes = [];
+ for (let i = 0, length = nodes.length; i < length;) {
+ let id = nodes[i++];
+ let size = nodes[i++];
+ let classNameIndex = nodes[i++];
+ let internal = nodes[i++];
let node = new WebInspector.HeapSnapshotNode(id, nodeClassNames[classNameIndex], size, !!internal);
nodeMap.set(id, node);
- nodes[i] = node;
+ processedNodes.push(node);
}
// Turn edges into real edges and set them on the nodes.
- for (let i = 0, length = edges.length; i < length; ++i) {
- let edgePayload = edges[i];
- let fromIdentifier = edgePayload[0];
- let toIdentifier = edgePayload[1];
- let edgeTypeIndex = edgePayload[2];
- let data = ""
+ for (let i = 0, length = edges.length; i < length;) {
+ let fromIdentifier = edges[i++];
+ let toIdentifier = edges[i++];
+ let edgeTypeIndex = edges[i++];
+ let data = ""
let from = nodeMap.get(fromIdentifier);
let to = nodeMap.get(toIdentifier);
- let edge = new WebInspector.HeapSnapshotEdge(from, to, edgeTypes[edgeTypeIndex], data);
+ let type = edgeTypes[edgeTypeIndex];
+ if (type === WebInspector.HeapSnapshotEdge.EdgeType.Property || type === WebInspector.HeapSnapshotEdge.EdgeType.Variable)
+ data = ""
+
+ let edge = new WebInspector.HeapSnapshotEdge(from, to, type, data);
from.outgoingEdges.push(edge);
to.incomingEdges.push(edge);
}
@@ -122,7 +125,7 @@
for (let i = 0, length = rootNodeEdges.length; i < length; ++i)
rootNodeEdges[i].to.gcRoot = true;
- return new WebInspector.HeapSnapshot(rootNode, nodes, nodeMap);
+ return new WebInspector.HeapSnapshot(rootNode, processedNodes, nodeMap);
}
// Public