Skip to content
This repository has been archived by the owner on Jun 11, 2024. It is now read-only.

Commit

Permalink
Merge branch 'development' of https://github.com/LiskHQ/lisk-sdk into…
Browse files Browse the repository at this point in the history
… feature/update_to_use_codec
  • Loading branch information
shuse2 committed May 30, 2020
2 parents 682e333 + 9159038 commit 0fdc707
Show file tree
Hide file tree
Showing 7 changed files with 543 additions and 6 deletions.
4 changes: 2 additions & 2 deletions elements/lisk-chain/benchmark/calculate_diff.js
Original file line number Diff line number Diff line change
Expand Up @@ -55,8 +55,8 @@ const diff = calculateDiff(
);

/**
* calculateDiff x 13.38 ops/sec ±1.96% (38 runs sampled)
* undo x 50,023 ops/sec ±0.52% (89 runs sampled)
* calculateDiff x 102,134 ops/sec ±0.99% (84 runs sampled)
* undo x 50,906 ops/sec ±0.80% (91 runs sampled)
*/
suite
.add('calculateDiff', () => {
Expand Down
101 changes: 97 additions & 4 deletions elements/lisk-chain/src/diff.ts
Original file line number Diff line number Diff line change
Expand Up @@ -130,15 +130,106 @@ const diffAlgo = (initial: Buffer, final: Buffer): HistoryType[] => {
return [];
};

/**
* This function returns the length of common prefix between two buffers
*/
const diffCommonPrefix = (buffer1: Buffer, buffer2: Buffer): number => {
// Quick check for common null cases.
if (buffer1[0] !== buffer2[0]) {
return 0;
}
// Binary search.
// Performance analysis: http://neil.fraser.name/news/2007/10/09/
let pointerMin = 0;
let pointerMax = Math.min(buffer1.length, buffer2.length);
let pointerMid = pointerMax;
let pointerstart = 0;

while (pointerMin < pointerMid) {
if (
buffer1
.slice(pointerstart, pointerMid)
.equals(buffer2.slice(pointerstart, pointerMid))
) {
pointerMin = pointerMid;
pointerstart = pointerMin;
} else {
pointerMax = pointerMid;
}
pointerMid = Math.floor((pointerMax - pointerMin) / 2 + pointerMin);
}

return pointerMid;
};

/**
* This function returns the length of common suffix between two buffers
*/
const diffCommonSuffix = (buffer1: Buffer, buffer2: Buffer): number => {
// Quick check for common null cases.
if (buffer1[buffer1.length - 1] !== buffer2[buffer2.length - 1]) {
return 0;
}
// Binary search.
// Performance analysis: http://neil.fraser.name/news/2007/10/09/
let pointerMin = 0;
let pointerMax = Math.min(buffer1.length, buffer2.length);
let pointerMid = pointerMax;
let pointerEnd = 0;

while (pointerMin < pointerMid) {
if (
buffer1
.slice(buffer1.length - pointerMid, buffer1.length - pointerEnd)
.equals(
buffer2.slice(
buffer2.length - pointerMid,
buffer2.length - pointerEnd,
),
)
) {
pointerMin = pointerMid;
pointerEnd = pointerMin;
} else {
pointerMax = pointerMid;
}
pointerMid = Math.floor((pointerMax - pointerMin) / 2 + pointerMin);
}

return pointerMid;
};

/**
* This function reduces the diff generated by the diff algorithm based on unchanged bytes
*/
export const calculateDiff = (
initial: Buffer,
final: Buffer,
): HistoryType[] => {
const longDiff = diffAlgo(initial, final);
const reducedDiff = [];
// When both the buffers are equal then return all '=' history
if (initial.equals(final)) {
return [['=', initial.length]];
}
const commonPrefix = diffCommonPrefix(initial, final);
const strippedPrefixInitial = initial.slice(commonPrefix, initial.length);
const strippedPrefixFinal = final.slice(commonPrefix, final.length);

const commonSuffix = diffCommonSuffix(
strippedPrefixInitial,
strippedPrefixFinal,
);
const strippedInitial = strippedPrefixInitial.slice(
0,
strippedPrefixInitial.length - commonSuffix,
);
const strippedFinal = strippedPrefixFinal.slice(
0,
strippedPrefixFinal.length - commonSuffix,
);
const longDiff = diffAlgo(strippedInitial, strippedFinal);

// Add common prefix to the reduced array in the start
const reducedDiff = commonPrefix > 0 ? [['=', commonPrefix]] : [];
let count = 0;

for (const b of longDiff) {
Expand All @@ -152,9 +243,11 @@ export const calculateDiff = (
count += 1;
}
}
if (count > 0) {
reducedDiff.push(['=', count]);
// When commonSuffix or last counts are greater zero then combine them
if (count > 0 || commonSuffix > 0) {
reducedDiff.push(['=', count + commonSuffix]);
}

return reducedDiff as HistoryType[];
};

Expand Down
15 changes: 15 additions & 0 deletions elements/lisk-codec/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,21 @@
$ npm install --save @liskhq/lisk-codec
```

## Benchmarks

The following are some benchmarks for version 0.1 of this library used for encoding and decoding different objects both generic and objects similar to the ones in the Lisk networks.

Node version used: v12.17.0. Computer Spec: SSD, 6 Core, 16 GB RAM. No special configuration for Node.

| Object Type | Encode (ops/sec) | Decode (ops/sec) |
| ------------- |:-------------:| -----:|
| Account | 75,081 | 86,908 |
| Transfer Transaction |225,229| 276,184 |
| Multi-signature registration (64 Members)| 23,539 | 44,231 |
| Block (15 KB payload)| 42,349 | 91,180 |

This and additional benchmarks can be found in the `benchmarks` folder

## License

Copyright 2016-2019 Lisk Foundation
Expand Down
177 changes: 177 additions & 0 deletions elements/lisk-codec/benchmark/encode_decode_account.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,177 @@
/*
* Copyright © 2020 Lisk Foundation
*
* See the LICENSE file at the top-level directory of this distribution
* for licensing information.
*
* Unless otherwise agreed in a custom licensing agreement with the Lisk Foundation,
* no part of this software, including this file, may be copied, modified,
* propagated, or distributed except according to the terms contained in the
* LICENSE file.
*
* Removal or modification of this copyright notice is prohibited.
*/

const { Suite } = require('benchmark');
const { codec } = require('../dist-node/codec');

const suite = new Suite();

const account = {
address: Buffer.from('e11a11364738225813f86ea85214400e5db08d6e', 'hex'),
balance: BigInt(10),
publicKey: Buffer.from(
'0fd3c50a6d3bd17ea806c0566cf6cf10f6e3697d9bda1820b00cb14746bcccef',
'hex',
),
nonce: 5,
keys: {
numberOfSignatures: 2,
mandatoryKeys: [
Buffer.from(
'c8b8fbe474a2b63ccb9744a409569b0a465ee1803f80435aec1c5e7fc2d4ee18',
'hex',
),
Buffer.from(
'6115424fec0ce9c3bac5a81b5c782827d1f956fb95f1ccfa36c566d04e4d7267',
'hex',
),
],
optionalKeys: [],
},
asset: {
delegate: {
username: 'DelegateA',
pomHeights: [85],
consecutiveMissedBlocks: 32,
lastForgedHeight: 64,
isBanned: false,
totalVotesReceived: BigInt(300000000),
},
sentVotes: [
{
delegateAddress: Buffer.from(
'cd32c73e9851c7137980063b8af64aa5a31651f8dcad258b682d2ddf091029e4',
'hex',
),
amount: BigInt(100000000),
},
{
delegateAddress: Buffer.from(
'9d86ad24a3f030e5522b6598115bb4d70c1692c9d8995ddfccb377379a2d86c6',
'hex',
),
amount: BigInt(250000000),
},
],
unlocking: [
{
delegateAddress: Buffer.from(
'655e665765e3c42712d9a425b5b720d10457a5e45de0d4420e7c53ad73b02ef5',
'hex',
),
amount: BigInt(400000000),
unvoteHeight: 128,
},
],
},
};

const testSchema = {
$id: 'accountSchema',
type: 'object',
properties: {
address: { dataType: 'bytes', fieldNumber: 1 },
balance: { dataType: 'uint64', fieldNumber: 2 },
publicKey: { dataType: 'bytes', fieldNumber: 3 },
nonce: { dataType: 'uint64', fieldNumber: 4 },
keys: {
fieldNumber: 5,
type: 'object',
properties: {
numberOfSignatures: { dataType: 'uint32', fieldNumber: 1 },
mandatoryKeys: {
type: 'array',
items: { dataType: 'bytes' },
fieldNumber: 2,
},
optionalKeys: {
type: 'array',
items: { dataType: 'bytes' },
fieldNumber: 3,
},
},
required: ['numberOfSignatures', 'mandatoryKeys', 'optionalKeys'],
},
asset: {
type: 'object',
fieldNumber: 6,
properties: {
delegate: {
type: 'object',
fieldNumber: 1,
properties: {
username: { dataType: 'string', fieldNumber: 1 },
pomHeights: {
type: 'array',
items: { dataType: 'uint32' },
fieldNumber: 2,
},
consecutiveMissedBlocks: { dataType: 'uint32', fieldNumber: 3 },
lastForgedHeight: { dataType: 'uint32', fieldNumber: 4 },
isBanned: { dataType: 'boolean', fieldNumber: 5 },
totalVotesReceived: { dataType: 'uint64', fieldNumber: 6 },
},
required: [
'username',
'pomHeights',
'consecutiveMissedBlocks',
'lastForgedHeight',
'isBanned',
'totalVotesReceived',
],
},
sentVotes: {
type: 'array',
fieldNumber: 2,
items: {
type: 'object',
properties: {
delegateAddress: { dataType: 'bytes', fieldNumber: 1 },
amount: { dataType: 'uint64', fieldNumber: 2 },
},
required: ['delegateAddress', 'amount'],
},
},
unlocking: {
type: 'array',
fieldNumber: 3,
items: {
type: 'object',
properties: {
delegateAddress: { dataType: 'bytes', fieldNumber: 1 },
amount: { dataType: 'uint64', fieldNumber: 2 },
unvoteHeight: { dataType: 'uint32', fieldNumber: 3 },
},
required: ['delegateAddress', 'amount', 'unvoteHeight'],
},
},
},
},
},
required: ['address', 'balance', 'publicKey', 'nonce', 'keys', 'asset'],
};

const accountEncoded = codec.encode(testSchema, account);

suite
.add('Encode Lisk account', () => {
codec.encode(testSchema, account);
})
.add('Decode Lisk account', () => {
codec.decode(testSchema, accountEncoded);
})
.on('cycle', function(event) {
console.log(String(event.target));
})
.run({ async: false });
Loading

0 comments on commit 0fdc707

Please sign in to comment.