Fasten up serialization
Summary:
The serialization algo was slow. The problem was that it used to just go to the first child which was not serializable and went back to the parent which then looked for first child from the remaining children and serialized it, and this happened at all levels. Instead, currently, the child fills the stack up with the children which needs to be serialized, so there is no back and forth for each child. This improved the speed a lot.
Test case: Serializing ~10000 rows of logs
Previous Iterative algo: 33215.3 ms
Iterative Algo with the above change: 2051.1 ms
Recursive Algo: ~1000 ms
New Algo Video:
{F164050963}
Prev Algo: Too Slow
{F164051027}
{F164051085}
Recursive Speed:
{F164051982}
Reviewed By: jknoxville
Differential Revision: D16037998
fbshipit-source-id: 70fae9a0073ff28d1a6528ec0dbddceb213b2c5f
This commit is contained in:
committed by
Facebook Github Bot
parent
641c9eee36
commit
004efcb7a8
@@ -18,26 +18,22 @@ function processArray(
|
|||||||
array: [any],
|
array: [any],
|
||||||
stack: Array<any>,
|
stack: Array<any>,
|
||||||
dict: Map<any, any>,
|
dict: Map<any, any>,
|
||||||
): {premature: boolean, outputArr: Array<any>} {
|
): {childNeedsIteration: boolean, outputArr: Array<any>} {
|
||||||
|
// Adds the array item to the stack if it needs to undergo iteration to serialise it. Otherwise it adds the serialized version of the item to the memoization dict
|
||||||
let outputArr = [];
|
let outputArr = [];
|
||||||
let premature = false;
|
let childNeedsIteration = false;
|
||||||
for (const item of array) {
|
for (const item of array) {
|
||||||
if (!dict.has(item)) {
|
const isItemInstanceOfObject = item instanceof Object;
|
||||||
if (!(item instanceof Object)) {
|
if (!dict.has(item) && isItemInstanceOfObject) {
|
||||||
dict.set(item, item);
|
stack.push(item);
|
||||||
} else {
|
childNeedsIteration = true;
|
||||||
stack.push(element);
|
continue;
|
||||||
stack.push(item);
|
} else if (!dict.has(item) && !isItemInstanceOfObject) {
|
||||||
premature = true;
|
dict.set(item, item);
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (dict.has(item)) {
|
|
||||||
dict.set(item, dict.get(item));
|
|
||||||
outputArr.push(dict.get(item));
|
|
||||||
}
|
}
|
||||||
|
outputArr.push(dict.get(item));
|
||||||
}
|
}
|
||||||
return {premature, outputArr};
|
return {childNeedsIteration, outputArr};
|
||||||
}
|
}
|
||||||
|
|
||||||
function processKeyValuePair(
|
function processKeyValuePair(
|
||||||
@@ -46,28 +42,74 @@ function processKeyValuePair(
|
|||||||
value: any,
|
value: any,
|
||||||
stack: Array<any>,
|
stack: Array<any>,
|
||||||
dict: Map<any, any>,
|
dict: Map<any, any>,
|
||||||
): {premature: boolean} {
|
): {childNeedsIteration: boolean} {
|
||||||
let premature = false;
|
// Adds the key and value to the stack if it needs to undergo iteration to serialise it. Otherwise it adds the serialized version of key and value to the memoization dict
|
||||||
if (!dict.has(key)) {
|
let childNeedsIteration = false;
|
||||||
if (!(key instanceof Object)) {
|
const isKeyInstanceOfObject = key instanceof Object;
|
||||||
dict.set(key, key);
|
if (!dict.has(key) && !isKeyInstanceOfObject) {
|
||||||
} else {
|
dict.set(key, key);
|
||||||
stack.push(element);
|
} else if (!dict.has(key) && isKeyInstanceOfObject) {
|
||||||
stack.push(key);
|
stack.push(key);
|
||||||
premature = true;
|
childNeedsIteration = true;
|
||||||
return {premature};
|
}
|
||||||
|
|
||||||
|
const isValueInstanceOfObject = value instanceof Object;
|
||||||
|
if (!dict.has(value) && !isValueInstanceOfObject) {
|
||||||
|
dict.set(value, value);
|
||||||
|
} else if (!dict.has(value) && isValueInstanceOfObject) {
|
||||||
|
stack.push(value);
|
||||||
|
childNeedsIteration = true;
|
||||||
|
}
|
||||||
|
return {childNeedsIteration};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function processMapElement(
|
||||||
|
obj: Map<any, any>,
|
||||||
|
dict: Map<any, any>,
|
||||||
|
stack: Array<any>,
|
||||||
|
): {childNeedsIteration: boolean, outputArray: Array<any>} {
|
||||||
|
const arr = [];
|
||||||
|
let childNeedsIteration = false;
|
||||||
|
for (const item of [...obj]) {
|
||||||
|
const key = item[0];
|
||||||
|
const value = item[1];
|
||||||
|
childNeedsIteration =
|
||||||
|
childNeedsIteration ||
|
||||||
|
processKeyValuePair(obj, key, value, stack, dict).childNeedsIteration;
|
||||||
|
if (!childNeedsIteration && dict.has(key) && dict.has(value)) {
|
||||||
|
dict.set(item, [dict.get(key), dict.get(value)]);
|
||||||
|
arr.push([dict.get(key), dict.get(value)]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!dict.has(value)) {
|
return {childNeedsIteration, outputArray: arr};
|
||||||
if (!(value instanceof Object)) {
|
}
|
||||||
dict.set(value, value);
|
|
||||||
} else {
|
export function processObjectToBeSerialized(
|
||||||
stack.push(element);
|
element: Object,
|
||||||
stack.push(value);
|
dict: Map<any, any>,
|
||||||
premature = true;
|
stack: Array<any>,
|
||||||
|
): {childNeedsIteration: boolean, outputObject: Object} {
|
||||||
|
const array = Object.entries(element);
|
||||||
|
let obj = {};
|
||||||
|
let childNeedsIteration = false;
|
||||||
|
for (const item of array) {
|
||||||
|
const key = item[0];
|
||||||
|
const value = item[1];
|
||||||
|
childNeedsIteration =
|
||||||
|
childNeedsIteration ||
|
||||||
|
processKeyValuePair(element, key, value, stack, dict).childNeedsIteration;
|
||||||
|
if (!childNeedsIteration && dict.has(key) && dict.has(value)) {
|
||||||
|
const serializedKey = dict.get(key);
|
||||||
|
const serializedValue = dict.get(value);
|
||||||
|
if (
|
||||||
|
typeof serializedKey !== 'undefined' &&
|
||||||
|
typeof serializedValue !== 'undefined'
|
||||||
|
) {
|
||||||
|
obj = {...obj, [serializedKey]: serializedValue};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return {premature};
|
return {childNeedsIteration, outputObject: obj};
|
||||||
}
|
}
|
||||||
|
|
||||||
export function makeObjectSerializable(obj: any): any {
|
export function makeObjectSerializable(obj: any): any {
|
||||||
@@ -77,38 +119,28 @@ export function makeObjectSerializable(obj: any): any {
|
|||||||
let stack = [obj];
|
let stack = [obj];
|
||||||
const dict: Map<any, any> = new Map();
|
const dict: Map<any, any> = new Map();
|
||||||
while (stack.length > 0) {
|
while (stack.length > 0) {
|
||||||
const element = stack.pop();
|
const element = stack[stack.length - 1];
|
||||||
if (element instanceof Map) {
|
if (element instanceof Map) {
|
||||||
const arr = [];
|
const {childNeedsIteration, outputArray} = processMapElement(
|
||||||
let premature = false;
|
element,
|
||||||
for (const item of [...element]) {
|
dict,
|
||||||
const key = item[0];
|
stack,
|
||||||
const value = item[1];
|
);
|
||||||
premature = processKeyValuePair(element, key, value, stack, dict)
|
if (childNeedsIteration) {
|
||||||
.premature;
|
|
||||||
if (premature) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (dict.has(key) && dict.has(value)) {
|
|
||||||
dict.set(item, [dict.get(key), dict.get(value)]);
|
|
||||||
arr.push([dict.get(key), dict.get(value)]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (premature) {
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
dict.set(element, {
|
dict.set(element, {
|
||||||
__flipper_object_type__: 'Map',
|
__flipper_object_type__: 'Map',
|
||||||
data: arr,
|
data: outputArray,
|
||||||
});
|
});
|
||||||
} else if (element instanceof Set) {
|
} else if (element instanceof Set) {
|
||||||
const {premature, outputArr} = processArray(
|
const {childNeedsIteration, outputArr} = processArray(
|
||||||
element,
|
element,
|
||||||
[...element],
|
[...element],
|
||||||
stack,
|
stack,
|
||||||
dict,
|
dict,
|
||||||
);
|
);
|
||||||
if (premature) {
|
if (childNeedsIteration) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
dict.set(element, {
|
dict.set(element, {
|
||||||
@@ -121,42 +153,28 @@ export function makeObjectSerializable(obj: any): any {
|
|||||||
data: element.toString(),
|
data: element.toString(),
|
||||||
});
|
});
|
||||||
} else if (element instanceof Array) {
|
} else if (element instanceof Array) {
|
||||||
const {premature, outputArr} = processArray(
|
const {childNeedsIteration, outputArr} = processArray(
|
||||||
element,
|
element,
|
||||||
element,
|
element,
|
||||||
stack,
|
stack,
|
||||||
dict,
|
dict,
|
||||||
);
|
);
|
||||||
if (premature) {
|
if (childNeedsIteration) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
dict.set(element, outputArr);
|
dict.set(element, outputArr);
|
||||||
} else if (element instanceof Object) {
|
} else if (element instanceof Object) {
|
||||||
const array = Object.entries(element);
|
const {childNeedsIteration, outputObject} = processObjectToBeSerialized(
|
||||||
let obj = {};
|
element,
|
||||||
let premature = false;
|
dict,
|
||||||
for (const item of array) {
|
stack,
|
||||||
const key = item[0];
|
);
|
||||||
const value = item[1];
|
if (childNeedsIteration) {
|
||||||
premature = processKeyValuePair(element, key, value, stack, dict)
|
|
||||||
.premature;
|
|
||||||
if (premature) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
const serializedKey = dict.get(key);
|
|
||||||
const serializedValue = dict.get(value);
|
|
||||||
if (
|
|
||||||
typeof serializedKey !== 'undefined' &&
|
|
||||||
typeof serializedValue !== 'undefined'
|
|
||||||
) {
|
|
||||||
obj = {...obj, [serializedKey]: serializedValue};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (premature) {
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
dict.set(element, obj);
|
dict.set(element, outputObject);
|
||||||
}
|
}
|
||||||
|
stack.pop();
|
||||||
}
|
}
|
||||||
return dict.get(obj);
|
return dict.get(obj);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user