mirror of
https://github.com/ianstormtaylor/slate.git
synced 2025-08-18 21:21:21 +02:00
Benchmark consistence && Allow users to select benches to run (#1765)
* Use slate rather than relative path * Move benchmark to one dir * Use slate-* instead of relative path * Before and After Function * Remove un-necessary cross-env * Hard fix * Lint the hard fix * Reset memory in bench() * Working on Benchmark Frameworks * Rename to slate-dev-benchmark * Add packages * Fix prettier bug * Benchmark framework is in working * Do not log in test * max times test * mute logger in test * add hr time * Better support for maxTime; add support of split runs to save memory space * Fix maxTries * Add global.gc * Global gc for each bench * Better test interface * Test max-time * Test max-time done * Add Benchmark among packages * Starting to get benchmark running * Pure Node lib * Change babelrc for pure Node benchmark * Moving Benchmarks * Get benchmark and test running * Get benchmark for slate-html-serializer * add slate-react * add slate/changes * all benchmarks are converted * Run benchmark by yarn * Run benchmark with expose-gc * Annotate Bench.js * Do not bundle slate-dev-benchmark in rollup * Add annotation * Allow config file to enable part benchmark compare * Add config for compare * support compare.js * Do not re-allocate memory; due to a large heap taken influence result * Render with Decorations * get active marks at range * Fix bug in showing percents * Fix percent showing bug * chore: add more benches * Better output of benchmark * Fix linting * decoration and normal as different benchmark test * Fix deserialize benchmark * README.md * Fix Readme.md * README.md * block-spacing config * safer user config loading * use package.json to load package in test * Consistent linting * move components to parent directory * Annotation styling in package * margin line before multi-line block * Fix naive bug * Fix naive bug * Fix a blank line * only log user and hr * Better name * Better annotation for runBundleTasks * Fix typo * Better logger * Move async to test * Omit skip * Only log the user space time * Single line async sleep * file name fix * Fix annotation * Better output of compare * Remove get-characters(-at-range) benchmarks * Restore emoji * Capitalize types * Remove compare to another area * Add grep and config interface * Linting files * Linting benchmarks * Linting benchmarks * Update yarn.lock
This commit is contained in:
committed by
Ian Storm Taylor
parent
09c93a6cd4
commit
8f9bfdac2b
14
.babelrc
14
.babelrc
@@ -23,7 +23,19 @@
|
||||
},
|
||||
"test": {
|
||||
"presets": [
|
||||
"env",
|
||||
["env", {
|
||||
"exclude": ["transform-regenerator"]
|
||||
}],
|
||||
"react",
|
||||
"stage-0"
|
||||
],
|
||||
"plugins": ["transform-runtime"]
|
||||
},
|
||||
"benchmark": {
|
||||
"presets": [
|
||||
["env", {
|
||||
"exclude": ["transform-regenerator"]
|
||||
}],
|
||||
"react",
|
||||
"stage-0"
|
||||
],
|
||||
|
3
.gitignore
vendored
3
.gitignore
vendored
@@ -21,3 +21,6 @@ packages/*/yarn.lock
|
||||
.DS_Store
|
||||
.idea/
|
||||
.vscode/
|
||||
|
||||
# Editor files
|
||||
.tern-port
|
||||
|
@@ -90,6 +90,16 @@ yarn benchmark
|
||||
|
||||
There will be some subtle changes in iteration speed always, but the comparison reporter will highlight any changes that seem meaningful. You can run `benchmark` multiple times to ensure the speed up persists.
|
||||
|
||||
### Run Selected Benchmarks
|
||||
|
||||
To run selected benchmarks, create `tmp/benchmark-config.js` with `module.exports.include`. For example, to run slate-core benchmarks only with `get-*`, we can create a `tmp/benchmark-config.js` as
|
||||
|
||||
```
|
||||
module.exports.include = {
|
||||
slate: /^get/
|
||||
}
|
||||
```
|
||||
|
||||
## Adding Browser Support
|
||||
|
||||
Slate aims to targeted all of the modern browsers, and eventually the modern mobile platforms. Right now browser support is limited to the latest versions of [Chrome](https://www.google.com/chrome/browser/desktop/), [Firefox](https://www.mozilla.org/en-US/firefox/new/), and [Safari](http://www.apple.com/safari/), but if you are interested in adding support for another modern platform, that is welcomed!
|
||||
|
115
benchmark/compare.js
Normal file
115
benchmark/compare.js
Normal file
@@ -0,0 +1,115 @@
|
||||
/* eslint-disable no-console */
|
||||
|
||||
const chalk = require('chalk')
|
||||
const figures = require('figures')
|
||||
const emojis = require('emojis')
|
||||
const { resolve } = require('path')
|
||||
|
||||
const baseline = require(resolve(process.cwd(), 'tmp/benchmark-baseline'))
|
||||
const comparison = require(resolve(process.cwd(), 'tmp/benchmark-comparison'))
|
||||
const { existsSync } = require('fs')
|
||||
|
||||
/**
|
||||
* Constants.
|
||||
*/
|
||||
|
||||
let THRESHOLD = 0.333
|
||||
const configPath = '../../tmp/benchmark-config.js'
|
||||
|
||||
if (existsSync(configPath)) {
|
||||
const alternative = require(configPath).THRESHOLD
|
||||
|
||||
if (typeof alternative === 'number' && alternative > 0) {
|
||||
THRESHOLD = alternative
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Print.
|
||||
*/
|
||||
|
||||
console.log()
|
||||
console.log(` benchmarks`)
|
||||
|
||||
baseline.forEach((suite, i) => {
|
||||
console.log(` ${suite.name}`)
|
||||
|
||||
suite.benchmarks.forEach((base, j) => {
|
||||
const compared = { user: {}, hr: {} }
|
||||
|
||||
for (const key of Object.keys(compared)) {
|
||||
const comp = comparison[i].benchmarks[j]
|
||||
if (!comp) return
|
||||
const b = base.iterations / base[key] * 1000
|
||||
const c = comp.iterations / comp[key] * 1000
|
||||
const balancePercent =
|
||||
b > c ? Math.round(Math.abs(b - c) / c * 100) : (c - b) / b * 100
|
||||
|
||||
const output = `${b.toFixed(2)} -> ${c.toFixed(2)} ops/sec`
|
||||
compared[key].baseOutput = output
|
||||
|
||||
compared[key].percentOutput = `${balancePercent.toFixed(2)}% ${
|
||||
c > b ? 'faster' : 'slower'
|
||||
}`
|
||||
|
||||
compared[key].percentValue = balancePercent
|
||||
compared[key].b = b
|
||||
compared[key].c = c
|
||||
compared[key].isFaster = c > b
|
||||
|
||||
if (balancePercent > 1000) {
|
||||
compared[key].percentOutput += emojis.unicode(' :scream: ')
|
||||
} else if (balancePercent > 100) {
|
||||
if (c > b) {
|
||||
compared[key].percentOutput += emojis.unicode(' :raised_hands: ')
|
||||
} else {
|
||||
compared[key].percentOutput += emojis.unicode(' :worried: ')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const { user, hr } = compared
|
||||
|
||||
if (
|
||||
user.percentValue < THRESHOLD * 100 &&
|
||||
hr.percentValue < THRESHOLD * 100
|
||||
) {
|
||||
console.log(
|
||||
chalk.grey(
|
||||
` ${figures.tick} ${base.name}: ${user.baseOutput} (${
|
||||
user.percentOutput
|
||||
})`
|
||||
)
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
if (user.isFaster === hr.isFaster) {
|
||||
if (user.isFaster) {
|
||||
console.log(chalk.green(` ${figures.star} ${base.name}:`))
|
||||
|
||||
console.log(
|
||||
` user: ${user.baseOutput} (${user.percentOutput})`
|
||||
)
|
||||
|
||||
console.log(` real: ${hr.baseOutput} (${hr.percentOutput})`)
|
||||
return
|
||||
}
|
||||
|
||||
console.log(chalk.red(` ${figures.cross} ${base.name}:`))
|
||||
|
||||
console.log(
|
||||
` user: ${user.baseOutput} (${user.percentOutput})`
|
||||
)
|
||||
|
||||
console.log(` real: ${hr.baseOutput} (${hr.percentOutput})`)
|
||||
return
|
||||
}
|
||||
|
||||
console.log(chalk.red(` ${figures.questionMarkPrefix} ${base.name}:`))
|
||||
console.log(` user: ${user.baseOutput} (${user.percentOutput})`)
|
||||
console.log(` real: ${hr.baseOutput} (${hr.percentOutput})`)
|
||||
})
|
||||
})
|
||||
|
||||
console.log()
|
39
benchmark/config.js
Normal file
39
benchmark/config.js
Normal file
@@ -0,0 +1,39 @@
|
||||
const { resolve } = require('path')
|
||||
const { existsSync } = require('fs')
|
||||
const program = require('commander')
|
||||
|
||||
program
|
||||
.option('-g, --grep []', 'Add grep pattern to filter running benchmarks')
|
||||
.option('-c, --config [file]', 'Add config to filter running benchmarks')
|
||||
.parse(process.argv)
|
||||
|
||||
const { grep } = program
|
||||
|
||||
if (grep) {
|
||||
const pattern = new RegExp(grep)
|
||||
|
||||
module.exports.include = {
|
||||
slate: pattern,
|
||||
'slate-html-serializer': pattern,
|
||||
'slate-plain-serializer': pattern,
|
||||
'slate-react': pattern,
|
||||
}
|
||||
} else {
|
||||
let { config = 'tmp/benchmark-config.js' } = program
|
||||
config = resolve(config)
|
||||
|
||||
const userConfig = existsSync(config) ? require(config) : {}
|
||||
|
||||
if (userConfig.include) {
|
||||
module.exports.include = userConfig.include
|
||||
} else if (userConfig.default) {
|
||||
module.exports.inlcude = userConfig.default
|
||||
} else {
|
||||
module.exports.include = {
|
||||
slate: /^/,
|
||||
'slate-html-serializer': /^/,
|
||||
'slate-plain-serializer': /^/,
|
||||
'slate-react': /^/,
|
||||
}
|
||||
}
|
||||
}
|
57
benchmark/generate-report.js
Normal file
57
benchmark/generate-report.js
Normal file
@@ -0,0 +1,57 @@
|
||||
const { writeFileSync } = require('fs')
|
||||
|
||||
function convertRepo(report) {
|
||||
const result = []
|
||||
|
||||
for (const name in report) {
|
||||
const suite = report[name]
|
||||
|
||||
result.push({
|
||||
name,
|
||||
type: 'suite',
|
||||
benchmarks: convertSuite(suite),
|
||||
})
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
function convertSuite(suite) {
|
||||
const result = []
|
||||
|
||||
for (const name in suite) {
|
||||
const bench = suite[name]
|
||||
const { user, cycles } = bench
|
||||
|
||||
result.push({
|
||||
name,
|
||||
type: 'bench',
|
||||
elapsed: user,
|
||||
iterations: cycles,
|
||||
ops: 1000 * cycles / user,
|
||||
...bench,
|
||||
})
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
const IS_COMPARE = process.env.COMPARE
|
||||
const filePath = IS_COMPARE
|
||||
? './tmp/benchmark-comparison.json'
|
||||
: './tmp/benchmark-baseline.json'
|
||||
|
||||
function generateReport(repo) {
|
||||
repo
|
||||
.run()
|
||||
.then(report => {
|
||||
const data = JSON.stringify(convertRepo(report))
|
||||
writeFileSync(filePath, data)
|
||||
return report
|
||||
})
|
||||
.then(report => {
|
||||
if (IS_COMPARE) {
|
||||
require('./compare')
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
module.exports = { generateReport }
|
45
benchmark/helpers/h.js
Normal file
45
benchmark/helpers/h.js
Normal file
@@ -0,0 +1,45 @@
|
||||
/* eslint-disable import/no-extraneous-dependencies */
|
||||
const { createHyperscript } = require('slate-hyperscript')
|
||||
|
||||
/**
|
||||
* Define a hyperscript.
|
||||
*
|
||||
* @type {Function}
|
||||
*/
|
||||
|
||||
const h = createHyperscript({
|
||||
blocks: {
|
||||
line: 'line',
|
||||
paragraph: 'paragraph',
|
||||
quote: 'quote',
|
||||
code: 'code',
|
||||
list: 'list',
|
||||
item: 'item',
|
||||
image: {
|
||||
type: 'image',
|
||||
isVoid: true,
|
||||
},
|
||||
},
|
||||
inlines: {
|
||||
link: 'link',
|
||||
hashtag: 'hashtag',
|
||||
comment: 'comment',
|
||||
emoji: {
|
||||
type: 'emoji',
|
||||
isVoid: true,
|
||||
},
|
||||
},
|
||||
marks: {
|
||||
b: 'bold',
|
||||
i: 'italic',
|
||||
u: 'underline',
|
||||
},
|
||||
})
|
||||
|
||||
/**
|
||||
* Export.
|
||||
*
|
||||
* @type {Function}
|
||||
*/
|
||||
|
||||
module.exports = h
|
20
benchmark/index.js
Normal file
20
benchmark/index.js
Normal file
@@ -0,0 +1,20 @@
|
||||
const { repo } = require('slate-dev-benchmark')
|
||||
const { resolve } = require('path')
|
||||
const { readdirSync } = require('fs')
|
||||
const { generateReport } = require('./generate-report')
|
||||
const { include } = require('./config')
|
||||
|
||||
const categoryDir = resolve(__dirname)
|
||||
|
||||
const categories = readdirSync(categoryDir).filter(
|
||||
c => c[0] != '.' && c.match(/^slate/)
|
||||
)
|
||||
|
||||
categories.forEach(dir => {
|
||||
if (include && include[dir]) {
|
||||
const { run } = require(`./${dir}`)
|
||||
run(include[dir])
|
||||
}
|
||||
})
|
||||
|
||||
generateReport(repo)
|
@@ -0,0 +1,58 @@
|
||||
/** @jsx h */
|
||||
/* eslint-disable react/jsx-key */
|
||||
|
||||
const Html = require('slate-html-serializer').default
|
||||
const { JSDOM } = require('jsdom') // eslint-disable-line import/no-extraneous-dependencies
|
||||
|
||||
const html = new Html({
|
||||
parseHtml: JSDOM.fragment,
|
||||
rules: [
|
||||
{
|
||||
deserialize(el, next) {
|
||||
switch (el.tagName.toLowerCase()) {
|
||||
case 'blockquote':
|
||||
return {
|
||||
object: 'block',
|
||||
type: 'quote',
|
||||
nodes: next(el.childNodes),
|
||||
}
|
||||
case 'p': {
|
||||
return {
|
||||
object: 'block',
|
||||
type: 'paragraph',
|
||||
nodes: next(el.childNodes),
|
||||
}
|
||||
}
|
||||
case 'strong': {
|
||||
return {
|
||||
object: 'mark',
|
||||
type: 'bold',
|
||||
nodes: next(el.childNodes),
|
||||
}
|
||||
}
|
||||
case 'em': {
|
||||
return {
|
||||
object: 'mark',
|
||||
type: 'italic',
|
||||
nodes: next(el.childNodes),
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
],
|
||||
})
|
||||
|
||||
module.exports.default = function(string) {
|
||||
html.deserialize(string)
|
||||
}
|
||||
|
||||
module.exports.input = `
|
||||
<blockquote>
|
||||
<p>
|
||||
This is editable <strong>rich</strong> text, <em>much</em> better than a textarea!
|
||||
</p>
|
||||
</blockquote>
|
||||
`
|
||||
.trim()
|
||||
.repeat(10)
|
@@ -1,10 +1,10 @@
|
||||
/** @jsx h */
|
||||
/* eslint-disable react/jsx-key */
|
||||
|
||||
import Html from '../..'
|
||||
import React from 'react'
|
||||
import h from '../../test/helpers/h'
|
||||
import { JSDOM } from 'jsdom' // eslint-disable-line import/no-extraneous-dependencies
|
||||
const Html = require('slate-html-serializer').default
|
||||
const React = require('react')
|
||||
const h = require('../../helpers/h')
|
||||
const { JSDOM } = require('jsdom') // eslint-disable-line import/no-extraneous-dependencies
|
||||
|
||||
const html = new Html({
|
||||
parseHtml: JSDOM.fragment,
|
||||
@@ -34,11 +34,11 @@ const html = new Html({
|
||||
],
|
||||
})
|
||||
|
||||
export default function(state) {
|
||||
module.exports.default = function(state) {
|
||||
html.serialize(state)
|
||||
}
|
||||
|
||||
export const input = (
|
||||
module.exports.input = (
|
||||
<value>
|
||||
<document>
|
||||
{Array.from(Array(10)).map(() => (
|
37
benchmark/slate-html-serializer/index.js
Normal file
37
benchmark/slate-html-serializer/index.js
Normal file
@@ -0,0 +1,37 @@
|
||||
const { readdirSync } = require('fs')
|
||||
const { basename, extname, resolve } = require('path')
|
||||
const { resetMemoization } = require('slate')
|
||||
const { Suite, Bench } = require('slate-dev-benchmark')
|
||||
|
||||
/**
|
||||
* Benchmarks.
|
||||
*/
|
||||
|
||||
module.exports.run = function(include) {
|
||||
const categoryDir = resolve(__dirname)
|
||||
const categories = readdirSync(categoryDir).filter(
|
||||
c => c[0] != '.' && c != 'index.js'
|
||||
)
|
||||
|
||||
categories.forEach(category => {
|
||||
const suite = new Suite(category, { minTries: 100, minTime: 1000 })
|
||||
const benchmarkDir = resolve(categoryDir, category)
|
||||
const benchmarks = readdirSync(benchmarkDir)
|
||||
.filter(b => b[0] != '.' && !!~b.indexOf('.js'))
|
||||
.map(b => basename(b, extname(b)))
|
||||
|
||||
benchmarks.forEach(benchmark => {
|
||||
if (include && !benchmark.match(include)) return
|
||||
const bench = new Bench(suite, benchmark)
|
||||
const dir = resolve(benchmarkDir, benchmark)
|
||||
const module = require(dir)
|
||||
const fn = module.default
|
||||
bench.input(() => module.input)
|
||||
|
||||
bench.run(input => {
|
||||
fn(input)
|
||||
resetMemoization()
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
40
benchmark/slate-plain-serializer/index.js
Normal file
40
benchmark/slate-plain-serializer/index.js
Normal file
@@ -0,0 +1,40 @@
|
||||
const { basename, extname, resolve } = require('path')
|
||||
const { readdirSync } = require('fs')
|
||||
const { resetMemoization } = require('slate')
|
||||
const { Suite, Bench } = require('slate-dev-benchmark')
|
||||
|
||||
/**
|
||||
* Benchmarks.
|
||||
*/
|
||||
|
||||
module.exports.run = function(include) {
|
||||
const categoryDir = resolve(__dirname)
|
||||
const categories = readdirSync(categoryDir).filter(
|
||||
c => c[0] != '.' && c != 'index.js'
|
||||
)
|
||||
|
||||
categories.forEach(category => {
|
||||
const suite = new Suite(category, {
|
||||
minTries: 100,
|
||||
minTime: 1000,
|
||||
})
|
||||
const benchmarkDir = resolve(categoryDir, category)
|
||||
const benchmarks = readdirSync(benchmarkDir)
|
||||
.filter(b => b[0] != '.' && !!~b.indexOf('.js'))
|
||||
.map(b => basename(b, extname(b)))
|
||||
|
||||
benchmarks.forEach(benchmark => {
|
||||
if (include && !benchmark.match(include)) return
|
||||
const bench = new Bench(suite, benchmark)
|
||||
const dir = resolve(benchmarkDir, benchmark)
|
||||
const module = require(dir)
|
||||
const fn = module.default
|
||||
bench.input(() => module.input)
|
||||
|
||||
bench.run(input => {
|
||||
fn(input)
|
||||
resetMemoization()
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
@@ -1,14 +1,16 @@
|
||||
/** @jsx h */
|
||||
/* eslint-disable react/jsx-key */
|
||||
|
||||
import Plain from '../..'
|
||||
const Plain = require('slate-plain-serializer').default
|
||||
|
||||
export default function(string) {
|
||||
Plain.deserialize(string)
|
||||
}
|
||||
|
||||
export const input = `
|
||||
const input = `
|
||||
This is editable plain text, just like a text area.
|
||||
`
|
||||
.trim()
|
||||
.repeat(10)
|
||||
|
||||
module.exports.input = input
|
||||
|
||||
module.exports.default = function(string) {
|
||||
Plain.deserialize(string)
|
||||
}
|
@@ -1,14 +1,14 @@
|
||||
/** @jsx h */
|
||||
/* eslint-disable react/jsx-key */
|
||||
|
||||
import Plain from '../..'
|
||||
import h from '../../test/helpers/h'
|
||||
const Plain = require('slate-plain-serializer').default
|
||||
const h = require('../../helpers/h')
|
||||
|
||||
export default function(state) {
|
||||
module.exports.default = function(state) {
|
||||
Plain.serialize(state)
|
||||
}
|
||||
|
||||
export const input = (
|
||||
module.exports.input = (
|
||||
<value>
|
||||
<document>
|
||||
{Array.from(Array(10)).map(() => (
|
40
benchmark/slate-react/index.js
Normal file
40
benchmark/slate-react/index.js
Normal file
@@ -0,0 +1,40 @@
|
||||
const { basename, extname, resolve } = require('path')
|
||||
const { readdirSync } = require('fs')
|
||||
const { resetMemoization } = require('slate')
|
||||
const { Suite, Bench } = require('slate-dev-benchmark')
|
||||
|
||||
/**
|
||||
* Benchmarks.
|
||||
*/
|
||||
|
||||
module.exports.run = function(include) {
|
||||
const categoryDir = resolve(__dirname)
|
||||
const categories = readdirSync(categoryDir).filter(
|
||||
c => c[0] != '.' && c != 'index.js'
|
||||
)
|
||||
|
||||
categories.forEach(category => {
|
||||
const suite = new Suite(category, {
|
||||
minTries: 100,
|
||||
minTime: 1000,
|
||||
})
|
||||
const benchmarkDir = resolve(categoryDir, category)
|
||||
const benchmarks = readdirSync(benchmarkDir)
|
||||
.filter(b => b[0] != '.' && !!~b.indexOf('.js'))
|
||||
.map(b => basename(b, extname(b)))
|
||||
|
||||
benchmarks.forEach(benchmark => {
|
||||
if (include && !benchmark.match(include)) return
|
||||
const bench = new Bench(suite, benchmark)
|
||||
const dir = resolve(benchmarkDir, benchmark)
|
||||
const module = require(dir)
|
||||
const fn = module.default
|
||||
bench.input(() => module.input)
|
||||
|
||||
bench.run(input => {
|
||||
fn(input)
|
||||
resetMemoization()
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
46
benchmark/slate-react/rendering/decoration.js
Normal file
46
benchmark/slate-react/rendering/decoration.js
Normal file
@@ -0,0 +1,46 @@
|
||||
/** @jsx h */
|
||||
/* eslint-disable react/jsx-key */
|
||||
|
||||
const React = require('react')
|
||||
const ReactDOM = require('react-dom/server')
|
||||
const h = require('../../helpers/h')
|
||||
const { Editor } = require('slate-react')
|
||||
|
||||
module.exports.default = function(value) {
|
||||
const el = React.createElement(Editor, { value })
|
||||
ReactDOM.renderToStaticMarkup(el)
|
||||
}
|
||||
|
||||
const value = (
|
||||
<value>
|
||||
<document>
|
||||
{Array.from(Array(10)).map(() => (
|
||||
<quote>
|
||||
<paragraph>
|
||||
<paragraph>
|
||||
This is editable <b>rich</b> text, <i>much</i> better than a
|
||||
textarea!
|
||||
</paragraph>
|
||||
</paragraph>
|
||||
</quote>
|
||||
))}
|
||||
</document>
|
||||
</value>
|
||||
)
|
||||
|
||||
const texts = value.document.getTexts()
|
||||
const decorations = texts.flatMap((t, index) => {
|
||||
if (index % 4 !== 0) return []
|
||||
if (t.length === 0) return []
|
||||
return [
|
||||
{
|
||||
anchorKey: t.key,
|
||||
anchorOffset: 0,
|
||||
focusKey: t.key,
|
||||
focusOffset: 1,
|
||||
marks: [{ type: 'underline' }],
|
||||
},
|
||||
]
|
||||
})
|
||||
|
||||
module.exports.input = value.change().setValue({ decorations }).value
|
@@ -1,17 +1,17 @@
|
||||
/** @jsx h */
|
||||
/* eslint-disable react/jsx-key */
|
||||
|
||||
import React from 'react'
|
||||
import ReactDOM from 'react-dom/server'
|
||||
import h from '../../test/helpers/h'
|
||||
import { Editor } from '../..'
|
||||
const React = require('react')
|
||||
const ReactDOM = require('react-dom/server')
|
||||
const h = require('../../helpers/h')
|
||||
const { Editor } = require('slate-react')
|
||||
|
||||
export default function(value) {
|
||||
module.exports.default = function(value) {
|
||||
const el = React.createElement(Editor, { value })
|
||||
ReactDOM.renderToStaticMarkup(el)
|
||||
}
|
||||
|
||||
export const input = (
|
||||
module.exports.input = (
|
||||
<value>
|
||||
<document>
|
||||
{Array.from(Array(10)).map(() => (
|
@@ -1,18 +1,13 @@
|
||||
/** @jsx h */
|
||||
/* eslint-disable react/jsx-key */
|
||||
|
||||
import h from '../../test/helpers/h'
|
||||
const h = require('../../helpers/h')
|
||||
|
||||
export default function(change) {
|
||||
module.exports.default = function(change) {
|
||||
change.deleteBackward()
|
||||
}
|
||||
|
||||
export function before(value) {
|
||||
const change = value.change()
|
||||
return change
|
||||
}
|
||||
|
||||
export const input = (
|
||||
const value = (
|
||||
<value>
|
||||
<document>
|
||||
{Array.from(Array(10)).map((v, i) => (
|
||||
@@ -29,3 +24,7 @@ export const input = (
|
||||
</document>
|
||||
</value>
|
||||
)
|
||||
|
||||
module.exports.input = () => {
|
||||
return value.change()
|
||||
}
|
@@ -1,18 +1,13 @@
|
||||
/** @jsx h */
|
||||
/* eslint-disable react/jsx-key */
|
||||
|
||||
import h from '../../test/helpers/h'
|
||||
const h = require('../../helpers/h')
|
||||
|
||||
export default function(change) {
|
||||
module.exports.default = function(change) {
|
||||
change.deleteForward()
|
||||
}
|
||||
|
||||
export function before(value) {
|
||||
const change = value.change()
|
||||
return change
|
||||
}
|
||||
|
||||
export const input = (
|
||||
const value = (
|
||||
<value>
|
||||
<document>
|
||||
{Array.from(Array(10)).map((v, i) => (
|
||||
@@ -29,3 +24,7 @@ export const input = (
|
||||
</document>
|
||||
</value>
|
||||
)
|
||||
|
||||
module.exports.input = () => {
|
||||
return value.change()
|
||||
}
|
32
benchmark/slate/changes/insert-node-by-key.js
Normal file
32
benchmark/slate/changes/insert-node-by-key.js
Normal file
@@ -0,0 +1,32 @@
|
||||
/** @jsx h */
|
||||
/* eslint-disable react/jsx-key */
|
||||
|
||||
const h = require('../../helpers/h')
|
||||
|
||||
module.exports.default = function({ change, block }) {
|
||||
change.insertNodeByKey(block.key, 0, <paragraph>Hello world</paragraph>)
|
||||
}
|
||||
|
||||
const value = (
|
||||
<value>
|
||||
<document>
|
||||
{Array.from(Array(10)).map((v, i) => (
|
||||
<quote>
|
||||
<paragraph>
|
||||
<paragraph>
|
||||
This is editable <b>rich</b> text, <i>much</i> better than a
|
||||
textarea!
|
||||
{i == 0 ? <cursor /> : ''}
|
||||
</paragraph>
|
||||
</paragraph>
|
||||
</quote>
|
||||
))}
|
||||
</document>
|
||||
</value>
|
||||
)
|
||||
const block = value.document.getBlocks().last()
|
||||
|
||||
module.exports.input = function() {
|
||||
const change = value.change()
|
||||
return { change, block }
|
||||
}
|
@@ -1,26 +1,15 @@
|
||||
/** @jsx h */
|
||||
/* eslint-disable react/jsx-key */
|
||||
|
||||
import h from '../../test/helpers/h'
|
||||
import { resetMemoization } from '../..'
|
||||
const h = require('../../helpers/h')
|
||||
|
||||
export default function({ change, keys }) {
|
||||
module.exports.default = function({ change, keys }) {
|
||||
for (const key of keys) {
|
||||
change.insertTextByKey(key, 0, 'a')
|
||||
}
|
||||
}
|
||||
|
||||
export function before(value) {
|
||||
const change = value.change()
|
||||
const keys = value.document
|
||||
.getTexts()
|
||||
.toArray()
|
||||
.map(t => t.key)
|
||||
resetMemoization()
|
||||
return { change, keys }
|
||||
}
|
||||
|
||||
export const input = (
|
||||
const value = (
|
||||
<value>
|
||||
<document>
|
||||
{Array.from(Array(10)).map((v, i) => (
|
||||
@@ -37,3 +26,12 @@ export const input = (
|
||||
</document>
|
||||
</value>
|
||||
)
|
||||
const keys = value.document
|
||||
.getTexts()
|
||||
.toArray()
|
||||
.map(t => t.key)
|
||||
|
||||
module.exports.input = function() {
|
||||
const change = value.change()
|
||||
return { change, keys }
|
||||
}
|
@@ -1,21 +1,13 @@
|
||||
/** @jsx h */
|
||||
/* eslint-disable react/jsx-key */
|
||||
|
||||
import h from '../../test/helpers/h'
|
||||
import { resetMemoization } from '../..'
|
||||
const h = require('../../helpers/h')
|
||||
|
||||
export default function({ change, text }) {
|
||||
module.exports.default = function({ change, text }) {
|
||||
change.insertTextByKey(text.key, 0, 'a')
|
||||
}
|
||||
|
||||
export function before(value) {
|
||||
const change = value.change()
|
||||
const text = value.document.getLastText()
|
||||
resetMemoization()
|
||||
return { change, text }
|
||||
}
|
||||
|
||||
export const input = (
|
||||
const value = (
|
||||
<value>
|
||||
<document>
|
||||
{Array.from(Array(10)).map((v, i) => (
|
||||
@@ -32,3 +24,9 @@ export const input = (
|
||||
</document>
|
||||
</value>
|
||||
)
|
||||
const text = value.document.getLastText()
|
||||
|
||||
module.exports.input = function() {
|
||||
const change = value.change()
|
||||
return { change, text }
|
||||
}
|
@@ -1,18 +1,13 @@
|
||||
/** @jsx h */
|
||||
/* eslint-disable react/jsx-key */
|
||||
|
||||
import h from '../../test/helpers/h'
|
||||
const h = require('../../helpers/h')
|
||||
|
||||
export default function(change) {
|
||||
module.exports.default = function(change) {
|
||||
change.insertText('a')
|
||||
}
|
||||
|
||||
export function before(value) {
|
||||
const change = value.change()
|
||||
return change
|
||||
}
|
||||
|
||||
export const input = (
|
||||
const value = (
|
||||
<value>
|
||||
<document>
|
||||
{Array.from(Array(10)).map((v, i) => (
|
||||
@@ -29,3 +24,7 @@ export const input = (
|
||||
</document>
|
||||
</value>
|
||||
)
|
||||
|
||||
module.exports.input = function() {
|
||||
return value.change()
|
||||
}
|
@@ -1,17 +1,13 @@
|
||||
/** @jsx h */
|
||||
/* eslint-disable react/jsx-key */
|
||||
|
||||
import h from '../../test/helpers/h'
|
||||
const h = require('../../helpers/h')
|
||||
|
||||
export default function(change) {
|
||||
module.exports.default = function(change) {
|
||||
change.normalize()
|
||||
}
|
||||
|
||||
export function before(value) {
|
||||
return value.change()
|
||||
}
|
||||
|
||||
export const input = (
|
||||
const value = (
|
||||
<value>
|
||||
<document>
|
||||
{Array.from(Array(10)).map((v, i) => (
|
||||
@@ -28,3 +24,7 @@ export const input = (
|
||||
</document>
|
||||
</value>
|
||||
)
|
||||
|
||||
module.exports.input = function() {
|
||||
return value.change()
|
||||
}
|
32
benchmark/slate/changes/remove-node-by-key.js
Normal file
32
benchmark/slate/changes/remove-node-by-key.js
Normal file
@@ -0,0 +1,32 @@
|
||||
/** @jsx h */
|
||||
/* eslint-disable react/jsx-key */
|
||||
|
||||
const h = require('../../helpers/h')
|
||||
|
||||
module.exports.default = function({ change, text }) {
|
||||
change.removeNodeByKey(text.key)
|
||||
}
|
||||
|
||||
const value = (
|
||||
<value>
|
||||
<document>
|
||||
{Array.from(Array(10)).map((v, i) => (
|
||||
<quote>
|
||||
<paragraph>
|
||||
<paragraph>
|
||||
This is editable <b>rich</b> text, <i>much</i> better than a
|
||||
textarea!
|
||||
{i == 0 ? <cursor /> : ''}
|
||||
</paragraph>
|
||||
</paragraph>
|
||||
</quote>
|
||||
))}
|
||||
</document>
|
||||
</value>
|
||||
)
|
||||
const text = value.document.getLastText()
|
||||
|
||||
module.exports.input = function() {
|
||||
const change = value.change()
|
||||
return { change, text }
|
||||
}
|
@@ -1,18 +1,13 @@
|
||||
/** @jsx h */
|
||||
/* eslint-disable react/jsx-key */
|
||||
|
||||
import h from '../../test/helpers/h'
|
||||
const h = require('../../helpers/h')
|
||||
|
||||
export default function(change) {
|
||||
module.exports.default = function(change) {
|
||||
change.splitBlock()
|
||||
}
|
||||
|
||||
export function before(value) {
|
||||
const change = value.change()
|
||||
return change
|
||||
}
|
||||
|
||||
export const input = (
|
||||
const value = (
|
||||
<value>
|
||||
<document>
|
||||
{Array.from(Array(10)).map((v, i) => (
|
||||
@@ -29,3 +24,7 @@ export const input = (
|
||||
</document>
|
||||
</value>
|
||||
)
|
||||
|
||||
module.exports.input = function() {
|
||||
return value.change()
|
||||
}
|
37
benchmark/slate/index.js
Normal file
37
benchmark/slate/index.js
Normal file
@@ -0,0 +1,37 @@
|
||||
const { readdirSync } = require('fs')
|
||||
const { basename, extname, resolve } = require('path')
|
||||
const { resetMemoization } = require('slate')
|
||||
const { Suite, Bench } = require('slate-dev-benchmark')
|
||||
|
||||
/**
|
||||
* Benchmarks.
|
||||
*/
|
||||
|
||||
module.exports.run = function(include) {
|
||||
const categoryDir = resolve(__dirname)
|
||||
const categories = readdirSync(categoryDir).filter(
|
||||
c => c[0] != '.' && c != 'index.js'
|
||||
)
|
||||
|
||||
categories.forEach(category => {
|
||||
const suite = new Suite(category, { minTries: 100, minTime: 1000 })
|
||||
const benchmarkDir = resolve(categoryDir, category)
|
||||
const benchmarks = readdirSync(benchmarkDir)
|
||||
.filter(b => b[0] != '.' && !!~b.indexOf('.js'))
|
||||
.map(b => basename(b, extname(b)))
|
||||
|
||||
benchmarks.forEach(benchmark => {
|
||||
if (include && !benchmark.match(include)) return
|
||||
const bench = new Bench(suite, benchmark)
|
||||
const dir = resolve(benchmarkDir, benchmark)
|
||||
const module = require(dir)
|
||||
const fn = module.default
|
||||
bench.input(module.input)
|
||||
|
||||
bench.run(input => {
|
||||
fn(input)
|
||||
resetMemoization()
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
@@ -1,12 +1,12 @@
|
||||
/* eslint-disable react/jsx-key */
|
||||
|
||||
import { Value } from '../..'
|
||||
const { Value } = require('slate')
|
||||
|
||||
export default function(json) {
|
||||
module.exports.default = function(json) {
|
||||
Value.fromJSON(json)
|
||||
}
|
||||
|
||||
export const input = {
|
||||
const input = {
|
||||
document: {
|
||||
nodes: Array.from(Array(100)).map(() => ({
|
||||
type: 'list',
|
||||
@@ -64,3 +64,7 @@ export const input = {
|
||||
})),
|
||||
},
|
||||
}
|
||||
|
||||
module.exports.input = function() {
|
||||
return input
|
||||
}
|
@@ -1,12 +1,12 @@
|
||||
/* eslint-disable react/jsx-key */
|
||||
|
||||
import { Value } from '../..'
|
||||
const { Value } = require('slate')
|
||||
|
||||
export default function(json) {
|
||||
module.exports.default = function(json) {
|
||||
Value.fromJSON(json)
|
||||
}
|
||||
|
||||
export const input = {
|
||||
const input = {
|
||||
document: {
|
||||
nodes: Array.from(Array(10)).map(() => ({
|
||||
object: 'block',
|
||||
@@ -44,3 +44,7 @@ export const input = {
|
||||
})),
|
||||
},
|
||||
}
|
||||
|
||||
module.exports.input = function() {
|
||||
return input
|
||||
}
|
@@ -1,17 +1,13 @@
|
||||
/** @jsx h */
|
||||
/* eslint-disable react/jsx-key */
|
||||
|
||||
import h from '../../test/helpers/h'
|
||||
const h = require('../../helpers/h')
|
||||
|
||||
export default function(value) {
|
||||
value.document.getCharactersAtRange(value.selection)
|
||||
module.exports.default = function(value) {
|
||||
value.document.getActiveMarksAtRange(value.selection)
|
||||
}
|
||||
|
||||
export function before(value) {
|
||||
return value.change().selectAll().value
|
||||
}
|
||||
|
||||
export const input = (
|
||||
const value = (
|
||||
<value>
|
||||
<document>
|
||||
{Array.from(Array(10)).map(() => (
|
||||
@@ -27,3 +23,9 @@ export const input = (
|
||||
</document>
|
||||
</value>
|
||||
)
|
||||
.change()
|
||||
.selectAll().value
|
||||
|
||||
module.exports.input = function() {
|
||||
return value
|
||||
}
|
30
benchmark/slate/models/get-ancestors.js
Normal file
30
benchmark/slate/models/get-ancestors.js
Normal file
@@ -0,0 +1,30 @@
|
||||
/** @jsx h */
|
||||
/* eslint-disable react/jsx-key */
|
||||
|
||||
const h = require('../../helpers/h')
|
||||
|
||||
module.exports.default = function({ value, text }) {
|
||||
value.document.getAncestors(text.key)
|
||||
}
|
||||
|
||||
const value = (
|
||||
<value>
|
||||
<document>
|
||||
{Array.from(Array(10)).map(() => (
|
||||
<quote>
|
||||
<paragraph>
|
||||
<paragraph>
|
||||
This is editable <b>rich</b> text, <i>much</i> better than a
|
||||
textarea!
|
||||
</paragraph>
|
||||
</paragraph>
|
||||
</quote>
|
||||
))}
|
||||
</document>
|
||||
</value>
|
||||
)
|
||||
const text = value.document.getLastText()
|
||||
|
||||
module.exports.input = function() {
|
||||
return { value, text }
|
||||
}
|
@@ -1,17 +1,13 @@
|
||||
/** @jsx h */
|
||||
/* eslint-disable react/jsx-key */
|
||||
|
||||
import h from '../../test/helpers/h'
|
||||
const h = require('../../helpers/h')
|
||||
|
||||
export default function(value) {
|
||||
module.exports.default = function(value) {
|
||||
value.document.getBlocksAtRange(value.selection)
|
||||
}
|
||||
|
||||
export function before(value) {
|
||||
return value.change().selectAll().value
|
||||
}
|
||||
|
||||
export const input = (
|
||||
const value = (
|
||||
<value>
|
||||
<document>
|
||||
{Array.from(Array(10)).map(() => (
|
||||
@@ -27,3 +23,9 @@ export const input = (
|
||||
</document>
|
||||
</value>
|
||||
)
|
||||
.change()
|
||||
.selectAll().value
|
||||
|
||||
module.exports.input = () => {
|
||||
return value
|
||||
}
|
@@ -1,13 +1,13 @@
|
||||
/** @jsx h */
|
||||
/* eslint-disable react/jsx-key */
|
||||
|
||||
import h from '../../test/helpers/h'
|
||||
const h = require('../../helpers/h')
|
||||
|
||||
export default function(value) {
|
||||
module.exports.default = function(value) {
|
||||
value.document.getBlocks()
|
||||
}
|
||||
|
||||
export const input = (
|
||||
const value = (
|
||||
<value>
|
||||
<document>
|
||||
{Array.from(Array(10)).map(() => (
|
||||
@@ -23,3 +23,7 @@ export const input = (
|
||||
</document>
|
||||
</value>
|
||||
)
|
||||
|
||||
module.exports.input = function() {
|
||||
return value
|
||||
}
|
@@ -1,20 +1,13 @@
|
||||
/** @jsx h */
|
||||
/* eslint-disable react/jsx-key */
|
||||
|
||||
import h from '../../test/helpers/h'
|
||||
import { resetMemoization } from '../..'
|
||||
const h = require('../../helpers/h')
|
||||
|
||||
export default function(text) {
|
||||
text.getLeaves()
|
||||
module.exports.default = function({ value, first, last }) {
|
||||
value.document.getCommonAncestor(first.key, last.key)
|
||||
}
|
||||
|
||||
export function before(value) {
|
||||
const text = value.document.getFirstText()
|
||||
resetMemoization()
|
||||
return text
|
||||
}
|
||||
|
||||
export const input = (
|
||||
const value = (
|
||||
<value>
|
||||
<document>
|
||||
{Array.from(Array(10)).map(() => (
|
||||
@@ -30,3 +23,10 @@ export const input = (
|
||||
</document>
|
||||
</value>
|
||||
)
|
||||
|
||||
const first = value.document.getFirstText()
|
||||
const last = value.document.getLastText()
|
||||
|
||||
module.exports.input = function() {
|
||||
return { value, first, last }
|
||||
}
|
30
benchmark/slate/models/get-furthest-ancestor.js
Normal file
30
benchmark/slate/models/get-furthest-ancestor.js
Normal file
@@ -0,0 +1,30 @@
|
||||
/** @jsx h */
|
||||
/* eslint-disable react/jsx-key */
|
||||
|
||||
const h = require('../../helpers/h')
|
||||
|
||||
module.exports.default = function({ value, text }) {
|
||||
value.document.getFurthestAncestor(text.key)
|
||||
}
|
||||
|
||||
const value = (
|
||||
<value>
|
||||
<document>
|
||||
{Array.from(Array(10)).map(() => (
|
||||
<quote>
|
||||
<paragraph>
|
||||
<paragraph>
|
||||
This is editable <b>rich</b> text, <i>much</i> better than a
|
||||
textarea!
|
||||
</paragraph>
|
||||
</paragraph>
|
||||
</quote>
|
||||
))}
|
||||
</document>
|
||||
</value>
|
||||
)
|
||||
const text = value.document.getLastText()
|
||||
|
||||
module.exports.input = function() {
|
||||
return { value, text }
|
||||
}
|
@@ -1,17 +1,13 @@
|
||||
/** @jsx h */
|
||||
/* eslint-disable react/jsx-key */
|
||||
|
||||
import h from '../../test/helpers/h'
|
||||
const h = require('../../helpers/h')
|
||||
|
||||
export default function(value) {
|
||||
module.exports.default = function(value) {
|
||||
value.document.getInlinesAtRange(value.selection)
|
||||
}
|
||||
|
||||
export function before(value) {
|
||||
return value.change().selectAll().value
|
||||
}
|
||||
|
||||
export const input = (
|
||||
const value = (
|
||||
<value>
|
||||
<document>
|
||||
{Array.from(Array(10)).map(() => (
|
||||
@@ -27,3 +23,9 @@ export const input = (
|
||||
</document>
|
||||
</value>
|
||||
)
|
||||
.change()
|
||||
.selectAll().value
|
||||
|
||||
module.exports.input = function() {
|
||||
return value
|
||||
}
|
@@ -1,13 +1,13 @@
|
||||
/** @jsx h */
|
||||
/* eslint-disable react/jsx-key */
|
||||
|
||||
import h from '../../test/helpers/h'
|
||||
const h = require('../../helpers/h')
|
||||
|
||||
export default function(value) {
|
||||
module.exports.default = function(value) {
|
||||
value.document.getInlines()
|
||||
}
|
||||
|
||||
export const input = (
|
||||
const value = (
|
||||
<value>
|
||||
<document>
|
||||
{Array.from(Array(10)).map(() => (
|
||||
@@ -23,3 +23,7 @@ export const input = (
|
||||
</document>
|
||||
</value>
|
||||
)
|
||||
|
||||
module.exports.input = function() {
|
||||
return value
|
||||
}
|
@@ -1,13 +1,13 @@
|
||||
/** @jsx h */
|
||||
/* eslint-disable react/jsx-key */
|
||||
|
||||
import h from '../../test/helpers/h'
|
||||
const h = require('../../helpers/h')
|
||||
|
||||
export default function(value) {
|
||||
value.document.getCharacters()
|
||||
module.exports.default = function(text) {
|
||||
text.getLeaves()
|
||||
}
|
||||
|
||||
export const input = (
|
||||
const value = (
|
||||
<value>
|
||||
<document>
|
||||
{Array.from(Array(10)).map(() => (
|
||||
@@ -23,3 +23,8 @@ export const input = (
|
||||
</document>
|
||||
</value>
|
||||
)
|
||||
const text = value.document.getFirstText()
|
||||
|
||||
module.exports.input = function() {
|
||||
return text
|
||||
}
|
@@ -1,17 +1,13 @@
|
||||
/** @jsx h */
|
||||
/* eslint-disable react/jsx-key */
|
||||
|
||||
import h from '../../test/helpers/h'
|
||||
const h = require('../../helpers/h')
|
||||
|
||||
export default function(value) {
|
||||
module.exports.default = function(value) {
|
||||
value.document.getMarksAtRange(value.selection)
|
||||
}
|
||||
|
||||
export function before(value) {
|
||||
return value.change().selectAll().value
|
||||
}
|
||||
|
||||
export const input = (
|
||||
const value = (
|
||||
<value>
|
||||
<document>
|
||||
{Array.from(Array(10)).map(() => (
|
||||
@@ -27,3 +23,9 @@ export const input = (
|
||||
</document>
|
||||
</value>
|
||||
)
|
||||
.change()
|
||||
.selectAll().value
|
||||
|
||||
module.exports.input = function() {
|
||||
return value
|
||||
}
|
@@ -1,13 +1,13 @@
|
||||
/** @jsx h */
|
||||
/* eslint-disable react/jsx-key */
|
||||
|
||||
import h from '../../test/helpers/h'
|
||||
const h = require('../../helpers/h')
|
||||
|
||||
export default function(value) {
|
||||
module.exports.default = function(value) {
|
||||
value.document.getMarks()
|
||||
}
|
||||
|
||||
export const input = (
|
||||
const value = (
|
||||
<value>
|
||||
<document>
|
||||
{Array.from(Array(10)).map(() => (
|
||||
@@ -23,3 +23,7 @@ export const input = (
|
||||
</document>
|
||||
</value>
|
||||
)
|
||||
|
||||
module.exports.input = function() {
|
||||
return value
|
||||
}
|
30
benchmark/slate/models/get-parent.js
Normal file
30
benchmark/slate/models/get-parent.js
Normal file
@@ -0,0 +1,30 @@
|
||||
/** @jsx h */
|
||||
/* eslint-disable react/jsx-key */
|
||||
|
||||
const h = require('../../helpers/h')
|
||||
|
||||
module.exports.default = function({ value, text }) {
|
||||
value.document.getParent(text.key)
|
||||
}
|
||||
|
||||
const value = (
|
||||
<value>
|
||||
<document>
|
||||
{Array.from(Array(10)).map(() => (
|
||||
<quote>
|
||||
<paragraph>
|
||||
<paragraph>
|
||||
This is editable <b>rich</b> text, <i>much</i> better than a
|
||||
textarea!
|
||||
</paragraph>
|
||||
</paragraph>
|
||||
</quote>
|
||||
))}
|
||||
</document>
|
||||
</value>
|
||||
)
|
||||
const text = value.document.getLastText()
|
||||
|
||||
module.exports.input = () => {
|
||||
return { value, text }
|
||||
}
|
@@ -1,20 +1,13 @@
|
||||
/** @jsx h */
|
||||
/* eslint-disable react/jsx-key */
|
||||
|
||||
import h from '../../test/helpers/h'
|
||||
import { resetMemoization } from '../..'
|
||||
const h = require('../../helpers/h')
|
||||
|
||||
export default function({ value, text }) {
|
||||
module.exports.default = function({ value, text }) {
|
||||
value.document.getPath(text.key)
|
||||
}
|
||||
|
||||
export function before(value) {
|
||||
const text = value.document.getLastText()
|
||||
resetMemoization()
|
||||
return { value, text }
|
||||
}
|
||||
|
||||
export const input = (
|
||||
const value = (
|
||||
<value>
|
||||
<document>
|
||||
{Array.from(Array(10)).map(() => (
|
||||
@@ -30,3 +23,8 @@ export const input = (
|
||||
</document>
|
||||
</value>
|
||||
)
|
||||
const text = value.document.getLastText()
|
||||
|
||||
module.exports.input = () => {
|
||||
return { value, text }
|
||||
}
|
@@ -1,17 +1,13 @@
|
||||
/** @jsx h */
|
||||
/* eslint-disable react/jsx-key */
|
||||
|
||||
import h from '../../test/helpers/h'
|
||||
const h = require('../../helpers/h')
|
||||
|
||||
export default function(value) {
|
||||
module.exports.default = function(value) {
|
||||
value.document.getTextsAtRange(value.selection)
|
||||
}
|
||||
|
||||
export function before(value) {
|
||||
return value.change().selectAll().value
|
||||
}
|
||||
|
||||
export const input = (
|
||||
const value = (
|
||||
<value>
|
||||
<document>
|
||||
{Array.from(Array(10)).map(() => (
|
||||
@@ -27,3 +23,9 @@ export const input = (
|
||||
</document>
|
||||
</value>
|
||||
)
|
||||
.change()
|
||||
.selectAll().value
|
||||
|
||||
module.exports.input = function() {
|
||||
return value
|
||||
}
|
@@ -1,13 +1,13 @@
|
||||
/** @jsx h */
|
||||
/* eslint-disable react/jsx-key */
|
||||
|
||||
import h from '../../test/helpers/h'
|
||||
const h = require('../../helpers/h')
|
||||
|
||||
export default function(value) {
|
||||
module.exports.default = function(value) {
|
||||
value.document.getTexts()
|
||||
}
|
||||
|
||||
export const input = (
|
||||
const value = (
|
||||
<value>
|
||||
<document>
|
||||
{Array.from(Array(10)).map(() => (
|
||||
@@ -23,3 +23,7 @@ export const input = (
|
||||
</document>
|
||||
</value>
|
||||
)
|
||||
|
||||
module.exports.input = function() {
|
||||
return value
|
||||
}
|
30
benchmark/slate/models/getDescendant.js
Normal file
30
benchmark/slate/models/getDescendant.js
Normal file
@@ -0,0 +1,30 @@
|
||||
/** @jsx h */
|
||||
/* eslint-disable react/jsx-key */
|
||||
|
||||
const h = require('../../helpers/h')
|
||||
|
||||
module.exports.default = function({ value, text }) {
|
||||
value.document.getDescendant(text.key)
|
||||
}
|
||||
|
||||
const value = (
|
||||
<value>
|
||||
<document>
|
||||
{Array.from(Array(10)).map(() => (
|
||||
<quote>
|
||||
<paragraph>
|
||||
<paragraph>
|
||||
This is editable <b>rich</b> text, <i>much</i> better than a
|
||||
textarea!
|
||||
</paragraph>
|
||||
</paragraph>
|
||||
</quote>
|
||||
))}
|
||||
</document>
|
||||
</value>
|
||||
)
|
||||
const text = value.document.getLastText()
|
||||
|
||||
module.exports.input = function() {
|
||||
return { value, text }
|
||||
}
|
@@ -1,25 +1,15 @@
|
||||
/** @jsx h */
|
||||
/* eslint-disable react/jsx-key */
|
||||
|
||||
import h from '../../test/helpers/h'
|
||||
import { resetMemoization } from '../..'
|
||||
const h = require('../../helpers/h')
|
||||
|
||||
export default function({ value, keys }) {
|
||||
module.exports.default = function({ value, keys }) {
|
||||
keys.forEach(key => {
|
||||
value.document.hasNode(key)
|
||||
})
|
||||
}
|
||||
|
||||
export function before(value) {
|
||||
const keys = value.document
|
||||
.getTexts()
|
||||
.toArray()
|
||||
.map(t => t.key)
|
||||
resetMemoization()
|
||||
return { value, keys }
|
||||
}
|
||||
|
||||
export const input = (
|
||||
const value = (
|
||||
<value>
|
||||
<document>
|
||||
{Array.from(Array(10)).map(() => (
|
||||
@@ -35,3 +25,11 @@ export const input = (
|
||||
</document>
|
||||
</value>
|
||||
)
|
||||
const keys = value.document
|
||||
.getTexts()
|
||||
.toArray()
|
||||
.map(t => t.key)
|
||||
|
||||
module.exports.input = function() {
|
||||
return { value, keys }
|
||||
}
|
@@ -1,20 +1,13 @@
|
||||
/** @jsx h */
|
||||
/* eslint-disable react/jsx-key */
|
||||
|
||||
import h from '../../test/helpers/h'
|
||||
import { resetMemoization } from '../..'
|
||||
const h = require('../../helpers/h')
|
||||
|
||||
export default function({ value, text }) {
|
||||
module.exports.default = function({ value, text }) {
|
||||
value.document.hasNode(text.key)
|
||||
}
|
||||
|
||||
export function before(value) {
|
||||
const text = value.document.getLastText()
|
||||
resetMemoization()
|
||||
return { value, text }
|
||||
}
|
||||
|
||||
export const input = (
|
||||
const value = (
|
||||
<value>
|
||||
<document>
|
||||
{Array.from(Array(10)).map(() => (
|
||||
@@ -30,3 +23,8 @@ export const input = (
|
||||
</document>
|
||||
</value>
|
||||
)
|
||||
const text = value.document.getLastText()
|
||||
|
||||
module.exports.input = function() {
|
||||
return { value, text }
|
||||
}
|
@@ -1,13 +1,13 @@
|
||||
/** @jsx h */
|
||||
/* eslint-disable react/jsx-key */
|
||||
|
||||
import h from '../../test/helpers/h'
|
||||
const h = require('../../helpers/h')
|
||||
|
||||
export default function(value) {
|
||||
module.exports.default = function(value) {
|
||||
value.toJSON()
|
||||
}
|
||||
|
||||
export const input = (
|
||||
const value = (
|
||||
<value>
|
||||
<document>
|
||||
{Array.from(Array(10)).map(() => (
|
||||
@@ -23,3 +23,7 @@ export const input = (
|
||||
</document>
|
||||
</value>
|
||||
)
|
||||
|
||||
module.exports.input = function() {
|
||||
return value
|
||||
}
|
@@ -1,23 +1,13 @@
|
||||
/** @jsx h */
|
||||
/* eslint-disable react/jsx-key */
|
||||
|
||||
import h from '../../test/helpers/h'
|
||||
import { resetMemoization } from '../..'
|
||||
const h = require('../../helpers/h')
|
||||
|
||||
export default function({ value, next }) {
|
||||
module.exports.default = function({ value, next }) {
|
||||
value.document.updateNode(next)
|
||||
}
|
||||
|
||||
export function before(value) {
|
||||
const texts = value.document.getTexts()
|
||||
const { size } = texts
|
||||
const text = texts.get(Math.round(size / 2))
|
||||
const next = text.insertText(0, 'some text')
|
||||
resetMemoization()
|
||||
return { value, next }
|
||||
}
|
||||
|
||||
export const input = (
|
||||
const value = (
|
||||
<value>
|
||||
<document>
|
||||
{Array.from(Array(10)).map(() => (
|
||||
@@ -33,3 +23,12 @@ export const input = (
|
||||
</document>
|
||||
</value>
|
||||
)
|
||||
|
||||
const texts = value.document.getTexts()
|
||||
const { size } = texts
|
||||
const text = texts.get(Math.round(size / 2))
|
||||
const next = text.insertText(0, 'some text')
|
||||
|
||||
module.exports.input = function() {
|
||||
return { value, next }
|
||||
}
|
12
package.json
12
package.json
@@ -16,17 +16,20 @@
|
||||
"babel-preset-stage-0": "^6.24.1",
|
||||
"babel-runtime": "^6.26.0",
|
||||
"chalk": "^1.1.3",
|
||||
"commander": "^2.15.1",
|
||||
"copy-webpack-plugin": "^4.4.1",
|
||||
"cross-env": "^5.1.3",
|
||||
"css-loader": "^0.28.9",
|
||||
"emotion": "^9.2.4",
|
||||
"eslint": "^4.19.1",
|
||||
"emojis": "^1.0.10",
|
||||
"eslint-config-prettier": "^2.9.0",
|
||||
"eslint-plugin-import": "^2.8.0",
|
||||
"eslint-plugin-prettier": "^2.5.0",
|
||||
"eslint-plugin-react": "^7.6.0",
|
||||
"extract-text-webpack-plugin": "^3.0.2",
|
||||
"faker": "^3.1.0",
|
||||
"figures": "^2.0.0",
|
||||
"fs-promise": "^1.0.0",
|
||||
"gh-pages": "^0.11.0",
|
||||
"html-webpack-plugin": "^2.30.1",
|
||||
@@ -62,6 +65,7 @@
|
||||
"rollup-plugin-sourcemaps": "^0.4.2",
|
||||
"rollup-plugin-uglify": "^3.0.0",
|
||||
"slate-collapse-on-escape": "^0.6.0",
|
||||
"slate-dev-benchmark": "*",
|
||||
"slate-soft-break": "^0.6.0",
|
||||
"source-map-loader": "^0.2.3",
|
||||
"source-map-support": "^0.4.0",
|
||||
@@ -79,13 +83,15 @@
|
||||
"slate-schema-violations": "*"
|
||||
},
|
||||
"scripts": {
|
||||
"benchmark": "mkdir -p ./tmp && cross-env BABEL_ENV=test babel-node ./node_modules/.bin/_matcha --reporter ./support/benchmark/reporter ./packages/*/benchmark/index.js > ./tmp/benchmark-comparison.json && cross-env BABEL_ENV=test babel-node ./support/benchmark/compare",
|
||||
"benchmark:save": "mkdir -p ./tmp && cross-env BABEL_ENV=test babel-node ./node_modules/.bin/_matcha --reporter ./support/benchmark/reporter ./packages/*/benchmark/index.js > ./tmp/benchmark-baseline.json",
|
||||
"benchmark": "cross-env COMPARE=compare node --expose-gc ./tmp/benchmark/index.js",
|
||||
"benchmark:save": " yarn benchmark:prepare && node --expose-gc ./tmp/benchmark/index.js",
|
||||
"benchmark:prepare": "mkdir -p ./tmp && cross-env BABEL_ENV=benchmark babel benchmark --out-dir tmp/benchmark/",
|
||||
"bootstrap": "lerna bootstrap && yarn build",
|
||||
"build": "rollup --config ./support/rollup/config.js",
|
||||
"build:production": "cross-env NODE_ENV=production rollup --config ./support/rollup/config.js && cross-env NODE_ENV=production webpack --config support/webpack/config.js",
|
||||
"clean": "lerna run clean && rm -rf ./node_modules ./dist ./build",
|
||||
"gh-pages": "gh-pages --dist ./build",
|
||||
"lint": "eslint packages/*/src packages/*/test examples/*/*.js examples/dev/*/*.js && prettier --list-different '**/*.{js,jsx,md,json,css}'",
|
||||
"lint": "eslint benchmark packages/*/src packages/*/test examples/*/*.js examples/dev/*/*.js && prettier --list-different '**/*.{js,jsx,md,json,css}'",
|
||||
"open": "open http://localhost:8080",
|
||||
"prettier": "prettier --write '**/*.{js,jsx,md,json,css}'",
|
||||
"release": "yarn build:production && yarn test && yarn lint && lerna publish && yarn gh-pages",
|
||||
|
1
packages/slate-dev-benchmark/Readme.md
Normal file
1
packages/slate-dev-benchmark/Readme.md
Normal file
@@ -0,0 +1 @@
|
||||
This package contains 'Suite' and 'Bench' that Slate uses to compare computation efficiency under nodeJS.
|
15
packages/slate-dev-benchmark/package.json
Normal file
15
packages/slate-dev-benchmark/package.json
Normal file
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"name": "slate-dev-benchmark",
|
||||
"description": "A simple, development-only benchmark for Slate",
|
||||
"version": "0.0.1",
|
||||
"license": "MIT",
|
||||
"repository": "git://github.com/ianstormtaylor/slate.git",
|
||||
"main": "src/index.js",
|
||||
"devDependencies": {
|
||||
"mocha": "^2.5.3"
|
||||
},
|
||||
"scripts": {
|
||||
"clean": "rm -rf ./dist ./lib ./node_modules"
|
||||
},
|
||||
"keywords": ["slate"]
|
||||
}
|
227
packages/slate-dev-benchmark/src/Bench.js
Normal file
227
packages/slate-dev-benchmark/src/Bench.js
Normal file
@@ -0,0 +1,227 @@
|
||||
/* global Promise */
|
||||
const { BenchType } = require('./types')
|
||||
const { makeOptions } = require('./makeOptions')
|
||||
const { Timer } = require('./Timer')
|
||||
const { logger } = require('./logger')
|
||||
|
||||
const errorReport = {
|
||||
cycles: NaN,
|
||||
user: NaN,
|
||||
system: NaN,
|
||||
all: NaN,
|
||||
}
|
||||
|
||||
/**
|
||||
* Run a task and calculate the time consuming of tasks
|
||||
*/
|
||||
|
||||
class Bench {
|
||||
/**
|
||||
* Construct a bench and register it to a Suite
|
||||
* @param {Suite} suite
|
||||
* @param {string} name
|
||||
* @param {Object} options
|
||||
*/
|
||||
|
||||
constructor(suite, name, options = {}) {
|
||||
this.name = name
|
||||
this.options = makeOptions({ ...suite.options, ...options })
|
||||
this.isFinished = false
|
||||
this.inputer = () => undefined
|
||||
this.runner = () => {}
|
||||
this.report = { ...errorReport }
|
||||
suite.addBench(this)
|
||||
}
|
||||
|
||||
/**
|
||||
* Is a Bench?
|
||||
* @param {any} obj
|
||||
* @return {boolean}
|
||||
*/
|
||||
|
||||
isBench(obj) {
|
||||
return obj && obj[BenchType]
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the method to generate (different} inputs for each run
|
||||
* @param {Array|Function|Scalar} inputer
|
||||
* @return {void}
|
||||
*/
|
||||
|
||||
input(inputer) {
|
||||
if (Array.isArray(inputer)) {
|
||||
this.inputer = index => inputer[index % inputer.length]
|
||||
return
|
||||
}
|
||||
|
||||
if (typeof inputer === 'function') {
|
||||
this.inputer = inputer
|
||||
return
|
||||
}
|
||||
|
||||
this.inputer = () => inputer
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the task runner
|
||||
* @param {Function} runner
|
||||
* @return {void}
|
||||
*/
|
||||
|
||||
run(runner) {
|
||||
this.runner = runner
|
||||
}
|
||||
|
||||
/**
|
||||
* Tries to run tasks in `times`, if the time consuming excedes the max-time, then stop;
|
||||
* After run, generate report and return
|
||||
* If initial is the initial index to run the task, for continueing a task in adaptive mode
|
||||
* @param {number} times
|
||||
* @param {number} initial
|
||||
*/
|
||||
|
||||
async compose(times, initial) {
|
||||
times = Math.floor(times)
|
||||
const isAsync = this.options.async
|
||||
const { runner, inputer } = this
|
||||
|
||||
const { maxTime } = this.options
|
||||
let seq = Number.isFinite(this.options.maxTries) ? 1 : NaN
|
||||
let nextCheckIndex = seq
|
||||
const hrStart = process.hrtime()
|
||||
|
||||
if (global.gc) {
|
||||
global.gc()
|
||||
}
|
||||
|
||||
const report = { user: 0, system: 0, all: 0, hr: 0, cycles: 0 }
|
||||
|
||||
for (
|
||||
let initialIndex = initial;
|
||||
initialIndex < times;
|
||||
initialIndex += this.options.allocationTries
|
||||
) {
|
||||
const tries = Math.min(times - initialIndex, this.options.allocationTries)
|
||||
const thisTryReport = await runBundleTasks.call(this, tries, initialIndex)
|
||||
|
||||
if (global.gc) {
|
||||
global.gc()
|
||||
}
|
||||
|
||||
for (const key in report) {
|
||||
report[key] += thisTryReport[key]
|
||||
}
|
||||
}
|
||||
return report
|
||||
|
||||
/**
|
||||
* Run a bundle of tasks;
|
||||
* the Bench estimate the time consuming of every `tries` tasks, then explictly run gc, and caculate the time consuming of next bundle tasks
|
||||
* @param {number} tries
|
||||
* @param {number} initialIndex
|
||||
* @return {Promise< Object , *>}
|
||||
*/
|
||||
|
||||
function runBundleTasks(tries, initialIndex) {
|
||||
const inputs = Array.from({ length: tries }).map(index =>
|
||||
inputer(index + initialIndex)
|
||||
)
|
||||
const timer = new Timer()
|
||||
timer.start()
|
||||
return runFrom(0).then(cycles => {
|
||||
timer.end()
|
||||
const { elapsed } = timer
|
||||
return { ...elapsed, cycles }
|
||||
})
|
||||
|
||||
/**
|
||||
* Run a single task run; If the task is end, return a Promise with the index when the task ends
|
||||
* @param {number} index
|
||||
* @return {Promise<number, *>}
|
||||
*/
|
||||
|
||||
function runFrom(index) {
|
||||
if (index === tries) return Promise.resolve(tries)
|
||||
|
||||
if (index === nextCheckIndex) {
|
||||
const hrEnd = process.hrtime(hrStart)
|
||||
const elapsed = hrEnd[0] * 1e3 + hrEnd[1] / 1e6
|
||||
|
||||
if (elapsed > maxTime) {
|
||||
return Promise.resolve(index)
|
||||
} else {
|
||||
if (elapsed < maxTime / 20) {
|
||||
seq *= 2
|
||||
}
|
||||
|
||||
nextCheckIndex = seq + nextCheckIndex
|
||||
}
|
||||
}
|
||||
|
||||
if (!isAsync) {
|
||||
const inputVar = inputs[index]
|
||||
runner(inputVar)
|
||||
return runFrom(index + 1)
|
||||
} else {
|
||||
return Promise.resolve(runner(inputs[index])).then(() =>
|
||||
runFrom(index + 1)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Run the bench
|
||||
* @return {void}
|
||||
*/
|
||||
|
||||
makeRun() {
|
||||
if (this.isFinished) return true
|
||||
logger(this)
|
||||
const { options } = this
|
||||
const { minTries, maxTime, maxTries } = options
|
||||
|
||||
let { minTime } = options
|
||||
if (minTime > maxTime) minTime = maxTime
|
||||
|
||||
return this.compose(minTries, 0)
|
||||
.then(report => {
|
||||
if (this.options.mode === 'static') return report
|
||||
const { all } = report
|
||||
if (all > minTime) return report
|
||||
const times = (minTime / all - 1) * minTries
|
||||
return this.compose(Math.min(times, maxTries), minTries).then(
|
||||
newReport => {
|
||||
return mergeResults(report, newReport)
|
||||
}
|
||||
)
|
||||
})
|
||||
.then(report => {
|
||||
this.report = report
|
||||
this.isFinished = true
|
||||
logger(this)
|
||||
return true
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
Bench.prototype[BenchType] = true
|
||||
|
||||
/*
|
||||
* Merge two different report
|
||||
* @param {Object} res1
|
||||
* @param {Object} res2
|
||||
*/
|
||||
|
||||
function mergeResults(res1, res2) {
|
||||
const result = {}
|
||||
|
||||
for (const key in res1) {
|
||||
result[key] = res1[key] + res2[key]
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
module.exports = { Bench }
|
73
packages/slate-dev-benchmark/src/Repository.js
Normal file
73
packages/slate-dev-benchmark/src/Repository.js
Normal file
@@ -0,0 +1,73 @@
|
||||
/* global Promise */
|
||||
const { RepositoryType } = require('./types')
|
||||
const { logger } = require('./logger')
|
||||
const { compose } = require('./compose')
|
||||
|
||||
/**
|
||||
* Repository Class for holding Suite
|
||||
*/
|
||||
|
||||
class Repository {
|
||||
/**
|
||||
* Construct a Repository with a name
|
||||
* @param {string} name
|
||||
*/
|
||||
|
||||
constructor(name = 'default') {
|
||||
this.name = name
|
||||
this.suites = []
|
||||
this.report = {}
|
||||
this.isFinished = false
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether {obj} is repository
|
||||
* @param {any} obj
|
||||
* @return {boolean}
|
||||
*/
|
||||
|
||||
isRepository(obj) {
|
||||
return obj && obj[RepositoryType]
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a suite to the repository
|
||||
* @param {Suite} suite
|
||||
* @return {void}
|
||||
*/
|
||||
|
||||
addSuite(suite) {
|
||||
this.isFinished = false
|
||||
this.suites.push(suite)
|
||||
}
|
||||
|
||||
/**
|
||||
* Run all suites (and all benches under suites) and generate a report
|
||||
* @return {Promise<Object, *>}
|
||||
*/
|
||||
|
||||
run() {
|
||||
if (this.isFinished) return Promise.resolve(this.report)
|
||||
logger(this)
|
||||
return compose(this.suites).then(() => {
|
||||
this.isFinished = true
|
||||
const report = {}
|
||||
|
||||
for (const suite of this.suites) {
|
||||
report[suite.name] = suite.report
|
||||
}
|
||||
|
||||
this.report = report
|
||||
return report
|
||||
})
|
||||
}
|
||||
}
|
||||
Repository.prototype[RepositoryType] = true
|
||||
|
||||
/**
|
||||
* By default, all suites are registers to the following {repo}
|
||||
*/
|
||||
|
||||
const repo = new Repository()
|
||||
|
||||
module.exports = { Repository, repo }
|
85
packages/slate-dev-benchmark/src/Suite.js
Normal file
85
packages/slate-dev-benchmark/src/Suite.js
Normal file
@@ -0,0 +1,85 @@
|
||||
/* global Promise */
|
||||
const { repo } = require('./Repository.js')
|
||||
const { SuiteType } = require('./types')
|
||||
const { logger } = require('./logger')
|
||||
const { compose } = require('./compose')
|
||||
const { makeOptions } = require('./makeOptions')
|
||||
|
||||
/**
|
||||
* Suite is for holding Benches
|
||||
*/
|
||||
|
||||
class Suite {
|
||||
/**
|
||||
* Construct a Suite and regiester it to repository
|
||||
* @param {string} name
|
||||
* @param {Object} options
|
||||
* @property {void|Repository} repository
|
||||
* @property {any} ...rest
|
||||
*/
|
||||
|
||||
constructor(name, options = {}) {
|
||||
const { repository = repo } = options
|
||||
|
||||
if (repository[name]) {
|
||||
throw Error(`The suite name ${name} has benn occupied in repository`)
|
||||
}
|
||||
|
||||
if (typeof name !== 'string') {
|
||||
throw Error(`The suite name must be a string`)
|
||||
}
|
||||
|
||||
this.name = name
|
||||
this.options = makeOptions(options)
|
||||
this.isFinished = false
|
||||
this.benches = []
|
||||
this.report = {}
|
||||
repository.addSuite(this)
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether it is a Suite
|
||||
* @param {any} obj
|
||||
* @return {boolean}
|
||||
*/
|
||||
|
||||
isSuite(obj) {
|
||||
return obj && obj[SuiteType]
|
||||
}
|
||||
|
||||
/**
|
||||
* Register an bench to the repository
|
||||
* @param {Bench} bench
|
||||
* @return {void}
|
||||
*/
|
||||
|
||||
addBench(bench) {
|
||||
this.isFinished = false
|
||||
this.benches.push(bench)
|
||||
}
|
||||
|
||||
/**
|
||||
* Run all benches, and generate report for consumed time
|
||||
* @return {Promise<Object, *>
|
||||
*/
|
||||
|
||||
makeRun() {
|
||||
if (this.isFinished) return Promise.resolve(this.report)
|
||||
logger(this)
|
||||
return compose(this.benches).then(() => {
|
||||
this.isFinished = true
|
||||
const report = {}
|
||||
|
||||
for (const bench of this.benches) {
|
||||
report[bench.name] = bench.report
|
||||
}
|
||||
|
||||
this.report = report
|
||||
return report
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
Suite.prototype[SuiteType] = true
|
||||
|
||||
module.exports = { Suite }
|
66
packages/slate-dev-benchmark/src/Timer.js
Normal file
66
packages/slate-dev-benchmark/src/Timer.js
Normal file
@@ -0,0 +1,66 @@
|
||||
const { TimerType } = require('./types')
|
||||
|
||||
class Timer {
|
||||
constructor() {
|
||||
this.cpuStartTime = {}
|
||||
this.hrStartTime = null
|
||||
this.isStopped = false
|
||||
this.elapsed = {}
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether it is a Timer
|
||||
* @param {any} obj
|
||||
*/
|
||||
|
||||
isTimer(obj) {
|
||||
return obj && obj[TimerType]
|
||||
}
|
||||
|
||||
/**
|
||||
* Start the timer
|
||||
* @return {void}
|
||||
*/
|
||||
|
||||
start() {
|
||||
this.isStopped = false
|
||||
this.cpuStartTime = process.cpuUsage()
|
||||
this.hrStartTime = process.hrtime()
|
||||
this.elapsed = {}
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop the timer and store restore in this.elapsed
|
||||
* @return {Object}
|
||||
*/
|
||||
|
||||
end() {
|
||||
if (this.isStopped) return this.elapsed
|
||||
const cpuElapsed = process.cpuUsage(this.cpuStartTime)
|
||||
const hrElapsed = process.hrtime(this.hrStartTime)
|
||||
const { user, system } = cpuElapsed
|
||||
const hr = hrElapsed[0] * 1000 + hrElapsed[1] / 1e6
|
||||
|
||||
/**
|
||||
* user: cpu time consumed in user space
|
||||
* system: cpu time consumed in system space
|
||||
* all: user+system
|
||||
* hr: real world time
|
||||
* (unit): ms
|
||||
*/
|
||||
|
||||
this.elapsed = {
|
||||
user: user / 1000,
|
||||
system: system / 1000,
|
||||
all: (user + system) / 1000,
|
||||
hr,
|
||||
}
|
||||
|
||||
this.isStopped = true
|
||||
return this.elapsed
|
||||
}
|
||||
}
|
||||
|
||||
Timer.prototype[TimerType] = true
|
||||
|
||||
module.exports = { Timer }
|
22
packages/slate-dev-benchmark/src/compose.js
Normal file
22
packages/slate-dev-benchmark/src/compose.js
Normal file
@@ -0,0 +1,22 @@
|
||||
/* global Promise */
|
||||
const { errorLog } = require('./logger')
|
||||
|
||||
/**
|
||||
* Run all benches/suites with Promise; Ensure an error would not block the whole process
|
||||
* @param {Array<Suite>|Array<Bench>}
|
||||
* @param {string} name; where to call the run method
|
||||
*/
|
||||
|
||||
function compose(list, name = 'makeRun') {
|
||||
return dispatch(0)
|
||||
|
||||
function dispatch(index) {
|
||||
if (index === list.length) return Promise.resolve(true)
|
||||
const node = list[index]
|
||||
return new Promise(resolve => resolve(node[name]()))
|
||||
.catch(err => errorLog(err))
|
||||
.then(() => dispatch(index + 1))
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = { compose }
|
5
packages/slate-dev-benchmark/src/index.js
Normal file
5
packages/slate-dev-benchmark/src/index.js
Normal file
@@ -0,0 +1,5 @@
|
||||
const { Repository, repo } = require('./Repository')
|
||||
const { Suite } = require('./Suite')
|
||||
const { Bench } = require('./Bench')
|
||||
|
||||
module.exports = { Repository, Suite, Bench, repo }
|
74
packages/slate-dev-benchmark/src/logger.js
Normal file
74
packages/slate-dev-benchmark/src/logger.js
Normal file
@@ -0,0 +1,74 @@
|
||||
/* eslint-disable no-console */
|
||||
|
||||
/**
|
||||
* IS in test
|
||||
*/
|
||||
|
||||
const IS_TEST =
|
||||
typeof process !== 'undefined' &&
|
||||
process.env &&
|
||||
process.env.BABEL_ENV === 'test'
|
||||
|
||||
/**
|
||||
* Log a `message`
|
||||
*
|
||||
* @param {String} message
|
||||
* @param {Any} ...args
|
||||
* @retrun {void}
|
||||
*/
|
||||
|
||||
function log(message, ...args) {
|
||||
if (IS_TEST) return
|
||||
|
||||
return console.log(message, ...args)
|
||||
}
|
||||
|
||||
/*
|
||||
* Log a error `message`
|
||||
*/
|
||||
|
||||
function errorLog(message, ...args) {
|
||||
console.error(message, ...args)
|
||||
}
|
||||
|
||||
/**
|
||||
* Logging benchmark result
|
||||
*/
|
||||
|
||||
function logger(obj) {
|
||||
const prefix = ' '
|
||||
|
||||
if (obj.isRepository) {
|
||||
return log(`Repository ${obj.name} is running`)
|
||||
}
|
||||
|
||||
if (obj.isSuite) {
|
||||
return log(`${prefix}- Suite ${obj.name} is running`)
|
||||
}
|
||||
|
||||
if (obj.isBench) {
|
||||
if (!obj.isFinished) {
|
||||
return log(`${prefix + prefix}- Bench ${obj.name} is running`)
|
||||
}
|
||||
|
||||
const { report } = obj
|
||||
const { cycles } = report
|
||||
|
||||
const header = {
|
||||
user: 'user:',
|
||||
hr: 'real:',
|
||||
}
|
||||
|
||||
for (const key of ['user', 'hr']) {
|
||||
log(
|
||||
`${prefix + prefix + prefix}${header[key]} * ${cycles} cycles: ${
|
||||
report[key]
|
||||
} ms; ( ${cycles * 1000 / report[key]} ops/sec)`
|
||||
)
|
||||
}
|
||||
return log(`${prefix + prefix + prefix}cycles: ${cycles}`)
|
||||
}
|
||||
return log(obj)
|
||||
}
|
||||
|
||||
module.exports = { logger, errorLog, log }
|
40
packages/slate-dev-benchmark/src/makeOptions.js
Normal file
40
packages/slate-dev-benchmark/src/makeOptions.js
Normal file
@@ -0,0 +1,40 @@
|
||||
const defaultOptions = {
|
||||
minTime: 1000,
|
||||
maxTime: 2000,
|
||||
minTries: 100,
|
||||
maxTries: Infinity,
|
||||
allocationTries: 1000,
|
||||
async: false,
|
||||
mode: 'adaptive',
|
||||
}
|
||||
|
||||
/**
|
||||
* Merge two options for configuring a bench run
|
||||
* @param {Object} options
|
||||
* @returns {Object}
|
||||
* @property {number} minTime
|
||||
* @property {number} maxTime
|
||||
* @property {number} minTries
|
||||
* @property {number} maxTries
|
||||
* @property {number} allocationTries
|
||||
* @property {boolean} async
|
||||
* @property {"static"|"adaptive"} mode
|
||||
*/
|
||||
|
||||
function makeOptions(options) {
|
||||
const result = { ...defaultOptions, ...options }
|
||||
|
||||
for (const key in defaultOptions) {
|
||||
const shallType = typeof defaultOptions[key]
|
||||
const inputType = typeof result[key]
|
||||
|
||||
if (shallType !== inputType) {
|
||||
throw TypeError(
|
||||
`Wrong Input in Config Suite, options[${key}] should be ${shallType}, but the input type is ${inputType}`
|
||||
)
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
module.exports = { makeOptions }
|
11
packages/slate-dev-benchmark/src/types.js
Normal file
11
packages/slate-dev-benchmark/src/types.js
Normal file
@@ -0,0 +1,11 @@
|
||||
const RepositoryType = '@@__SLATE_REPOSITORY__@@'
|
||||
const SuiteType = '@@__SLATE_SUITE__@@'
|
||||
const BenchType = '@@__SLATE_BENCH__@@'
|
||||
const TimerType = '@@__SLATE_BENCH_TIMER_@@'
|
||||
|
||||
module.exports = {
|
||||
RepositoryType,
|
||||
SuiteType,
|
||||
BenchType,
|
||||
TimerType,
|
||||
}
|
12
packages/slate-dev-benchmark/test/index.js
Normal file
12
packages/slate-dev-benchmark/test/index.js
Normal file
@@ -0,0 +1,12 @@
|
||||
/**
|
||||
* Dependencies.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Tests.
|
||||
*/
|
||||
|
||||
describe('slate-dev-benchmark', () => {
|
||||
require('./tries/')
|
||||
require('./time/')
|
||||
})
|
22
packages/slate-dev-benchmark/test/time/index.js
Normal file
22
packages/slate-dev-benchmark/test/time/index.js
Normal file
@@ -0,0 +1,22 @@
|
||||
import { repo, Suite } from '../..'
|
||||
import fs from 'fs'
|
||||
import { resolve } from 'path'
|
||||
|
||||
describe('time', async () => {
|
||||
const suite = new Suite('tries')
|
||||
const testDir = resolve(__dirname)
|
||||
const files = fs
|
||||
.readdirSync(testDir)
|
||||
.filter(x => x[0] !== '.' && x !== 'index.js')
|
||||
|
||||
for (const file of files) {
|
||||
const module = require(`./${file}`)
|
||||
|
||||
it(module.experiment, () => {
|
||||
module.default(suite)
|
||||
const { expected } = module
|
||||
repo.isFinished = false
|
||||
return repo.run().then(() => expected())
|
||||
})
|
||||
}
|
||||
})
|
31
packages/slate-dev-benchmark/test/time/max-time-async.js
Normal file
31
packages/slate-dev-benchmark/test/time/max-time-async.js
Normal file
@@ -0,0 +1,31 @@
|
||||
/* global Promise */
|
||||
import { Bench } from '../..'
|
||||
import assert from 'assert'
|
||||
|
||||
export const experiment = 'max-time-async'
|
||||
|
||||
let index = 0
|
||||
|
||||
// A wider range than sync, becuase Promise intialization, babel-node takes time
|
||||
export function expected() {
|
||||
assert(
|
||||
index > 5 && index < 12,
|
||||
`index should be 10, but is actually ${index}`
|
||||
)
|
||||
return true
|
||||
}
|
||||
|
||||
export default function(suite) {
|
||||
const bench = new Bench(suite, experiment, {
|
||||
mode: 'adaptive',
|
||||
minTries: 100,
|
||||
maxTries: 200,
|
||||
minTime: 1,
|
||||
maxTime: 100,
|
||||
async: true,
|
||||
})
|
||||
|
||||
bench.run(
|
||||
() => new Promise(resolve => setTimeout(() => resolve(index++), 10))
|
||||
)
|
||||
}
|
@@ -0,0 +1,31 @@
|
||||
import { Bench } from '../..'
|
||||
import { syncSleep } from '../utils/sleep'
|
||||
import assert from 'assert'
|
||||
|
||||
export const experiment = 'max-time'
|
||||
|
||||
let index = 0
|
||||
|
||||
export function expected() {
|
||||
assert(
|
||||
index > 85 && index < 115,
|
||||
`index should be around 100, but is actually ${index}`
|
||||
)
|
||||
return true
|
||||
}
|
||||
|
||||
export default function(suite) {
|
||||
const bench = new Bench(suite, experiment, {
|
||||
mode: 'adaptive',
|
||||
minTries: 1000,
|
||||
maxTries: 2000,
|
||||
minTime: 1,
|
||||
maxTime: 1000,
|
||||
async: false,
|
||||
})
|
||||
|
||||
bench.run(() => {
|
||||
syncSleep(10)
|
||||
index++
|
||||
})
|
||||
}
|
28
packages/slate-dev-benchmark/test/time/max-time.js
Normal file
28
packages/slate-dev-benchmark/test/time/max-time.js
Normal file
@@ -0,0 +1,28 @@
|
||||
import { Bench } from '../..'
|
||||
import { syncSleep } from '../utils/sleep'
|
||||
import assert from 'assert'
|
||||
|
||||
export const experiment = 'max-time'
|
||||
|
||||
let index = 0
|
||||
|
||||
export function expected() {
|
||||
assert(index === 10, `index should be 10, but is actually ${index}`)
|
||||
return true
|
||||
}
|
||||
|
||||
export default function(suite) {
|
||||
const bench = new Bench(suite, experiment, {
|
||||
mode: 'adaptive',
|
||||
minTries: 100,
|
||||
maxTries: 200,
|
||||
minTime: 1,
|
||||
maxTime: 100,
|
||||
async: false,
|
||||
})
|
||||
|
||||
bench.run(() => {
|
||||
syncSleep(10)
|
||||
index++
|
||||
})
|
||||
}
|
25
packages/slate-dev-benchmark/test/tries/index.js
Normal file
25
packages/slate-dev-benchmark/test/tries/index.js
Normal file
@@ -0,0 +1,25 @@
|
||||
import assert from 'assert'
|
||||
import { repo, Suite } from '../..'
|
||||
import fs from 'fs'
|
||||
import { resolve } from 'path'
|
||||
|
||||
describe('tries', async () => {
|
||||
const suite = new Suite('tries')
|
||||
const testDir = resolve(__dirname)
|
||||
const files = fs
|
||||
.readdirSync(testDir)
|
||||
.filter(x => x[0] !== '.' && x !== 'index.js')
|
||||
|
||||
for (const file of files) {
|
||||
const module = require(`./${file}`)
|
||||
|
||||
it(module.experiment, () => {
|
||||
module.default(suite)
|
||||
const { actual, expected } = module
|
||||
repo.isFinished = false
|
||||
return repo.run().then(() => {
|
||||
assert.deepEqual(actual, expected)
|
||||
})
|
||||
})
|
||||
}
|
||||
})
|
19
packages/slate-dev-benchmark/test/tries/max-tries.js
Normal file
19
packages/slate-dev-benchmark/test/tries/max-tries.js
Normal file
@@ -0,0 +1,19 @@
|
||||
import { Bench } from '../..'
|
||||
|
||||
export const experiment = 'max-tries adaptive mode'
|
||||
export const actual = { index: 0 }
|
||||
export const expected = { index: 200 }
|
||||
|
||||
export default function(suite) {
|
||||
const bench = new Bench(suite, experiment, {
|
||||
mode: 'adaptive',
|
||||
minTries: 100,
|
||||
maxTries: 200,
|
||||
minTime: 100,
|
||||
maxTime: Infinity,
|
||||
})
|
||||
|
||||
bench.run(() => {
|
||||
actual.index++
|
||||
})
|
||||
}
|
18
packages/slate-dev-benchmark/test/tries/min-tries.js
Normal file
18
packages/slate-dev-benchmark/test/tries/min-tries.js
Normal file
@@ -0,0 +1,18 @@
|
||||
import { Bench } from '../..'
|
||||
|
||||
export const experiment = 'min-tries static mode'
|
||||
export const actual = { index: 0 }
|
||||
export const expected = { index: 100 }
|
||||
|
||||
export default function(suite) {
|
||||
const bench = new Bench(suite, experiment, {
|
||||
mode: 'static',
|
||||
minTries: 100,
|
||||
maxTries: 200,
|
||||
minTime: 100,
|
||||
})
|
||||
|
||||
bench.run(() => {
|
||||
actual.index++
|
||||
})
|
||||
}
|
10
packages/slate-dev-benchmark/test/utils/sleep.js
Normal file
10
packages/slate-dev-benchmark/test/utils/sleep.js
Normal file
@@ -0,0 +1,10 @@
|
||||
function syncSleep(ms) {
|
||||
const start = process.hrtime()
|
||||
|
||||
while (true) {
|
||||
const end = process.hrtime(start)
|
||||
if (end[0] * 1000 + end[1] / 1e6 > ms) return undefined
|
||||
}
|
||||
}
|
||||
|
||||
export { syncSleep }
|
@@ -1,48 +0,0 @@
|
||||
/** @jsx h */
|
||||
/* eslint-disable react/jsx-key */
|
||||
|
||||
import Html from '../..'
|
||||
import React from 'react'
|
||||
import { JSDOM } from 'jsdom' // eslint-disable-line import/no-extraneous-dependencies
|
||||
|
||||
const html = new Html({
|
||||
parseHtml: JSDOM.fragment,
|
||||
rules: [
|
||||
{
|
||||
serialize(obj, children) {
|
||||
switch (obj.object) {
|
||||
case 'block': {
|
||||
switch (obj.type) {
|
||||
case 'paragraph':
|
||||
return React.createElement('p', {}, children)
|
||||
case 'quote':
|
||||
return React.createElement('blockquote', {}, children)
|
||||
}
|
||||
}
|
||||
case 'mark': {
|
||||
switch (obj.type) {
|
||||
case 'bold':
|
||||
return React.createElement('strong', {}, children)
|
||||
case 'italic':
|
||||
return React.createElement('em', {}, children)
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
],
|
||||
})
|
||||
|
||||
export default function(string) {
|
||||
html.deserialize(string)
|
||||
}
|
||||
|
||||
export const input = `
|
||||
<blockquote>
|
||||
<p>
|
||||
This is editable <strong>rich</strong> text, <em>much</em> better than a textarea!
|
||||
</p>
|
||||
</blockquote>
|
||||
`
|
||||
.trim()
|
||||
.repeat(10)
|
@@ -1,46 +0,0 @@
|
||||
/* global suite, set, bench */
|
||||
|
||||
import fs from 'fs'
|
||||
import { basename, extname, resolve } from 'path'
|
||||
import { resetMemoization } from 'slate'
|
||||
|
||||
/**
|
||||
* Benchmarks.
|
||||
*/
|
||||
|
||||
const categoryDir = resolve(__dirname)
|
||||
const categories = fs
|
||||
.readdirSync(categoryDir)
|
||||
.filter(c => c[0] != '.' && c != 'index.js')
|
||||
|
||||
categories.forEach(category => {
|
||||
suite(category, () => {
|
||||
set('iterations', 50)
|
||||
set('mintime', 1000)
|
||||
|
||||
if (category == 'models') {
|
||||
after(() => {
|
||||
resetMemoization()
|
||||
})
|
||||
}
|
||||
|
||||
const benchmarkDir = resolve(categoryDir, category)
|
||||
const benchmarks = fs
|
||||
.readdirSync(benchmarkDir)
|
||||
.filter(b => b[0] != '.' && !!~b.indexOf('.js'))
|
||||
.map(b => basename(b, extname(b)))
|
||||
|
||||
benchmarks.forEach(benchmark => {
|
||||
const dir = resolve(benchmarkDir, benchmark)
|
||||
const module = require(dir)
|
||||
const fn = module.default
|
||||
let { input, before, after } = module
|
||||
if (before) input = before(input)
|
||||
|
||||
bench(benchmark, () => {
|
||||
fn(input)
|
||||
if (after) after()
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
@@ -1,46 +0,0 @@
|
||||
/* global suite, set, bench */
|
||||
|
||||
import fs from 'fs'
|
||||
import { basename, extname, resolve } from 'path'
|
||||
import { resetMemoization } from 'slate'
|
||||
|
||||
/**
|
||||
* Benchmarks.
|
||||
*/
|
||||
|
||||
const categoryDir = resolve(__dirname)
|
||||
const categories = fs
|
||||
.readdirSync(categoryDir)
|
||||
.filter(c => c[0] != '.' && c != 'index.js')
|
||||
|
||||
categories.forEach(category => {
|
||||
suite(category, () => {
|
||||
set('iterations', 50)
|
||||
set('mintime', 1000)
|
||||
|
||||
if (category == 'models') {
|
||||
after(() => {
|
||||
resetMemoization()
|
||||
})
|
||||
}
|
||||
|
||||
const benchmarkDir = resolve(categoryDir, category)
|
||||
const benchmarks = fs
|
||||
.readdirSync(benchmarkDir)
|
||||
.filter(b => b[0] != '.' && !!~b.indexOf('.js'))
|
||||
.map(b => basename(b, extname(b)))
|
||||
|
||||
benchmarks.forEach(benchmark => {
|
||||
const dir = resolve(benchmarkDir, benchmark)
|
||||
const module = require(dir)
|
||||
const fn = module.default
|
||||
let { input, before, after } = module
|
||||
if (before) input = before(input)
|
||||
|
||||
bench(benchmark, () => {
|
||||
fn(input)
|
||||
if (after) after()
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
@@ -1,39 +0,0 @@
|
||||
/* global suite, set, bench */
|
||||
|
||||
import fs from 'fs'
|
||||
import { basename, extname, resolve } from 'path'
|
||||
|
||||
/**
|
||||
* Benchmarks.
|
||||
*/
|
||||
|
||||
const categoryDir = resolve(__dirname)
|
||||
const categories = fs
|
||||
.readdirSync(categoryDir)
|
||||
.filter(c => c[0] != '.' && c != 'index.js')
|
||||
|
||||
categories.forEach(category => {
|
||||
suite(category, () => {
|
||||
set('iterations', 50)
|
||||
set('mintime', 1000)
|
||||
|
||||
const benchmarkDir = resolve(categoryDir, category)
|
||||
const benchmarks = fs
|
||||
.readdirSync(benchmarkDir)
|
||||
.filter(b => b[0] != '.' && !!~b.indexOf('.js'))
|
||||
.map(b => basename(b, extname(b)))
|
||||
|
||||
benchmarks.forEach(benchmark => {
|
||||
const dir = resolve(benchmarkDir, benchmark)
|
||||
const module = require(dir)
|
||||
const fn = module.default
|
||||
let { input, before, after } = module
|
||||
if (before) input = before(input)
|
||||
|
||||
bench(benchmark, () => {
|
||||
fn(input)
|
||||
if (after) after()
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
@@ -1,46 +0,0 @@
|
||||
/* global suite, set, bench */
|
||||
|
||||
import fs from 'fs'
|
||||
import { basename, extname, resolve } from 'path'
|
||||
import { resetMemoization } from '..'
|
||||
|
||||
/**
|
||||
* Benchmarks.
|
||||
*/
|
||||
|
||||
const categoryDir = resolve(__dirname)
|
||||
const categories = fs
|
||||
.readdirSync(categoryDir)
|
||||
.filter(c => c[0] != '.' && c != 'index.js')
|
||||
|
||||
categories.forEach(category => {
|
||||
suite(category, () => {
|
||||
set('iterations', 100)
|
||||
set('mintime', 1000)
|
||||
|
||||
if (category == 'models') {
|
||||
after(() => {
|
||||
resetMemoization()
|
||||
})
|
||||
}
|
||||
|
||||
const benchmarkDir = resolve(categoryDir, category)
|
||||
const benchmarks = fs
|
||||
.readdirSync(benchmarkDir)
|
||||
.filter(b => b[0] != '.' && !!~b.indexOf('.js'))
|
||||
.map(b => basename(b, extname(b)))
|
||||
|
||||
benchmarks.forEach(benchmark => {
|
||||
const dir = resolve(benchmarkDir, benchmark)
|
||||
const module = require(dir)
|
||||
const fn = module.default
|
||||
let { input, before, after } = module
|
||||
if (before) input = before(input)
|
||||
|
||||
bench(benchmark, () => {
|
||||
fn(input)
|
||||
if (after) after()
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
@@ -1,14 +1,24 @@
|
||||
/* eslint-disable no-console */
|
||||
|
||||
import chalk from 'chalk'
|
||||
import figures from 'figures'
|
||||
import emojis from 'emojis'
|
||||
import baseline from '../../tmp/benchmark-baseline'
|
||||
import comparison from '../../tmp/benchmark-comparison'
|
||||
import { existsSync } from 'fs'
|
||||
|
||||
/**
|
||||
* Constants.
|
||||
*/
|
||||
|
||||
const THRESHOLD = 0.333
|
||||
let THRESHOLD = 0.333
|
||||
const configPath = '../../tmp/benchmark-config.js'
|
||||
if (existsSync(configPath)) {
|
||||
const alternative = require(configPath).THRESHOLD
|
||||
if (typeof alternative === 'number' && alternative > 0) {
|
||||
THRESHOLD = alternative
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Print.
|
||||
@@ -21,27 +31,72 @@ baseline.forEach((suite, i) => {
|
||||
console.log(` ${suite.name}`)
|
||||
|
||||
suite.benchmarks.forEach((base, j) => {
|
||||
const comp = comparison[i].benchmarks[j]
|
||||
if (!comp) return
|
||||
const compared = { user: {}, hr: {} }
|
||||
|
||||
const b = base.iterations / base.elapsed * 1000
|
||||
const c = comp.iterations / comp.elapsed * 1000
|
||||
const threshold = b * THRESHOLD
|
||||
const slower = b - c > threshold
|
||||
const faster = b - c < 0 - threshold
|
||||
const percent = Math.round(Math.abs(b - c) / b * 100)
|
||||
for (const key of Object.keys(compared)) {
|
||||
const comp = comparison[i].benchmarks[j]
|
||||
if (!comp) return
|
||||
const b = base.iterations / base[key] * 1000
|
||||
const c = comp.iterations / comp[key] * 1000
|
||||
const balancePercent =
|
||||
b > c ? Math.round(Math.abs(b - c) / c * 100) : (c - b) / b * 100
|
||||
|
||||
let output = `${b.toFixed(2)} → ${c.toFixed(2)} ops/sec`
|
||||
if (slower) output = chalk.red(`${output} (${percent}% slower)`)
|
||||
else if (faster) output = chalk.green(`${output} (${percent}% faster)`)
|
||||
else output = chalk.gray(output)
|
||||
const output = `${b.toFixed(2)} -> ${c.toFixed(2)} ops/sec`
|
||||
compared[key].baseOutput = output
|
||||
compared[key].percentOutput = `${balancePercent.toFixed(2)}% ${
|
||||
c > b ? 'faster' : 'slower'
|
||||
}`
|
||||
compared[key].percentValue = balancePercent
|
||||
compared[key].b = b
|
||||
compared[key].c = c
|
||||
compared[key].isFaster = c > b
|
||||
if (balancePercent > 1000) {
|
||||
compared[key].percentOutput += emojis.unicode(' :scream: ')
|
||||
} else if (balancePercent > 100) {
|
||||
if (c > b) {
|
||||
compared[key].percentOutput += emojis.unicode(' :raised_hands: ')
|
||||
} else {
|
||||
compared[key].percentOutput += emojis.unicode(' :worried: ')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (percent > 1000) output += ' 😱'
|
||||
else if (faster && percent > 100) output += ' 🙌'
|
||||
else if (slower && percent > 100) output += ' 😟'
|
||||
const { user, hr } = compared
|
||||
|
||||
console.log(` ${base.title}`)
|
||||
console.log(` ${output}`)
|
||||
if (
|
||||
user.percentValue < THRESHOLD * 100 &&
|
||||
hr.percentValue < THRESHOLD * 100
|
||||
) {
|
||||
console.log(
|
||||
chalk.grey(
|
||||
` ${figures.tick} ${base.name}: ${user.baseOutput} (${
|
||||
user.percentOutput
|
||||
})`
|
||||
)
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
if (user.isFaster === hr.isFaster) {
|
||||
if (user.isFaster) {
|
||||
console.log(chalk.green(` ${figures.star} ${base.name}:`))
|
||||
console.log(
|
||||
` user: ${user.baseOutput} (${user.percentOutput})`
|
||||
)
|
||||
console.log(` real: ${hr.baseOutput} (${hr.percentOutput})`)
|
||||
return
|
||||
}
|
||||
console.log(chalk.red(` ${figures.cross} ${base.name}:`))
|
||||
console.log(
|
||||
` user: ${user.baseOutput} (${user.percentOutput})`
|
||||
)
|
||||
console.log(` real: ${hr.baseOutput} (${hr.percentOutput})`)
|
||||
return
|
||||
}
|
||||
|
||||
console.log(chalk.red(` ${figures.questionMarkPrefix} ${base.name}:`))
|
||||
console.log(` user: ${user.baseOutput} (${user.percentOutput})`)
|
||||
console.log(` real: ${hr.baseOutput} (${hr.percentOutput})`)
|
||||
})
|
||||
})
|
||||
|
||||
|
@@ -1,31 +0,0 @@
|
||||
const { stdout } = process
|
||||
|
||||
module.exports = function(runner, utils) {
|
||||
let hasSuite = false
|
||||
let hasBench = false
|
||||
|
||||
runner.on('start', () => {
|
||||
stdout.write('[')
|
||||
})
|
||||
|
||||
runner.on('end', () => {
|
||||
stdout.write(']')
|
||||
})
|
||||
|
||||
runner.on('suite start', suite => {
|
||||
if (hasSuite) stdout.write(',')
|
||||
stdout.write(`{"name":"${suite.title}","benchmarks":[`)
|
||||
hasSuite = true
|
||||
})
|
||||
|
||||
runner.on('suite end', suite => {
|
||||
hasBench = false
|
||||
stdout.write(']}')
|
||||
})
|
||||
|
||||
runner.on('bench end', bench => {
|
||||
if (hasBench) stdout.write(',')
|
||||
stdout.write(JSON.stringify(bench))
|
||||
hasBench = true
|
||||
})
|
||||
}
|
@@ -11,6 +11,7 @@ import slatePropTypes from '../../packages/slate-prop-types/package.json'
|
||||
import slateReact from '../../packages/slate-react/package.json'
|
||||
import slateSchemaViolations from '../../packages/slate-schema-violations/package.json'
|
||||
import slateSimulator from '../../packages/slate-simulator/package.json'
|
||||
// Do not import slateDevBenchmark here. The benchmark shall be a pure nodeJS program and can be run without babel-node
|
||||
|
||||
const configurations = [
|
||||
...factory(slate),
|
||||
|
@@ -1926,6 +1926,10 @@ commander@2.9.0:
|
||||
dependencies:
|
||||
graceful-readlink ">= 1.0.0"
|
||||
|
||||
commander@^2.15.1:
|
||||
version "2.16.0"
|
||||
resolved "https://registry.yarnpkg.com/commander/-/commander-2.16.0.tgz#f16390593996ceb4f3eeb020b31d78528f7f8a50"
|
||||
|
||||
commander@~2.13.0:
|
||||
version "2.13.0"
|
||||
resolved "https://registry.yarnpkg.com/commander/-/commander-2.13.0.tgz#6964bca67685df7c1f1430c584f07d7597885b9c"
|
||||
@@ -2796,6 +2800,10 @@ emojis-list@^2.0.0:
|
||||
version "2.1.0"
|
||||
resolved "https://registry.yarnpkg.com/emojis-list/-/emojis-list-2.1.0.tgz#4daa4d9db00f9819880c79fa457ae5b09a1fd389"
|
||||
|
||||
emojis@^1.0.10:
|
||||
version "1.0.10"
|
||||
resolved "https://registry.yarnpkg.com/emojis/-/emojis-1.0.10.tgz#2558133df0dff13313c99531647f693d7adb57da"
|
||||
|
||||
emotion@^9.2.4:
|
||||
version "9.2.4"
|
||||
resolved "https://registry.yarnpkg.com/emotion/-/emotion-9.2.4.tgz#0139e7cc154b2845f4b9afaa996dd4de13bb90e3"
|
||||
|
Reference in New Issue
Block a user