您可以检查下面的代码,通过在属性名称前添加+
或-
来定义排序顺序会导致排序速度明显变慢。有什么想法吗?
默认排序(值):5.84ms
升序排序(+值):58.59ms
降序排序(-value):49.28ms
value
和+value
之间存在差异(两者都返回完全相同的排序顺序)这一事实对我来说非常困惑。
let arr = []
for (let i = 0; i < 10000; ++i) {
arr.push({ _id: 'doc' + i, value: Math.random(), k: i % 20 })
}
function sort (...keys) {
let data = {}
data.sort = []
keys.forEach(key => {
const newKey = key.replace(/(\-|\+)/g, '')
const order = key[0] === '-' ? -1 : 1
data.sort.push({
key: newKey,
order: order
})
})
return data
}
function compare (sortData, a, b) {
for (let i = 0; i < sortData.length; ++i) {
const data = sortData[i]
const _a = a[data.key]
const _b = b[data.key]
if (_a !== _b) return _a > _b ? data.order : -data.order
}
return 0
}
function exec (data) {
let r = arr.slice()
if (data.sort) {
r.sort((a, b) => {
let r = compare(data.sort, a, b)
return r
})
}
return r
}
function time (f) {
let start = performance.now()
f()
return performance.now() - start
}
function test (f, n) {
let total = 0
for (let i = 0; i < n; ++i) total += time(f)
return total / n
}
const q1 = sort('value')
const q2 = sort('+value')
const q3 = sort('-value')
const sortDefault = () => exec(q1)
const sortAsc = () => exec(q2)
const sortDesc = () => exec(q3)
console.log(' Default Sort (value):', test(sortDefault, 10).toFixed(2) + 'ms')
console.log(' Ascending Sort (+value):', test(sortAsc, 10).toFixed(2) + 'ms')
console.log('Descending Sort (-value):', test(sortDesc, 10).toFixed(2) + 'ms')
答案 0 :(得分:1)
编写一个返回比较器的函数会更常见,尽管在这种情况下,性能差异似乎是V8 JS引擎优化代码的情况。正则表达式匹配。
因此更典型的用法可能是:
function buildSort(...keys) {
if (typeof keys[0] === "string") {
keys = keys.map(key => {
const order = key[0] === '-' ? -1 : 1
key = key.replace(/(\-|\+)/g, '')
return { key, order };
})
}
return (a, b) => {
for (let { key, order } of keys ) {
const _a = a[key];
const _b = b[key];
if (_a !== _b) return _a > _b ? order : -order
}
return 0
}
}
function exec(comparator) {
return arr.slice().sort(comparator);
}
...
const q1 = buildSort({ key: 'value', order: 1 });
const q2 = buildSort({ key: 'value', order: -1 });
const q3 = buildSort('+value')
在我的笔记本电脑上运行NodeJS 6.9.1传递{ key: 'value', order: 1 }
或"value"
给出0.015秒的运行时间,而传递"+value"
则在0.46秒内运行,即使名义上这些值导致相同的值出现在比较器中使用的keys
结构中。
答案 1 :(得分:1)
这更像是一个注释,但对于一个注释来说太长了:对于一个测试场景,看看如果后来密钥被中断,是否存在V8性能差异,我尝试存储一个原始密钥(正则表达式的结果)并在第一次比较中得到对象的实际键(理论上应该是一个实习或至少可以优化的字符串)。
下面是相同的代码,但是在sort函数中设置了属性rawkey
而不是'key',并且在与if(!data.key) data.key = Object.keys(a).filter(k=>k==data.rawkey)[0];
进行比较时获得了密钥
无论好坏,在Chrome中为我创造了相同的性能。
实现只是一个展示案例,并不安全,并非所有对象都可能具有实际属性
let arr = []
for (let i = 0; i < 10000; ++i) {
arr.push({ _id: 'doc' + i, value: Math.random(), k: i % 20 })
}
function sort (...keys) {
let data = {}
data.sort = []
keys.forEach(key => {
const newKey = key.replace(/(\-|\+)/g, '')
const order = key[0] === '-' ? -1 : 1
data.sort.push({
rawkey: newKey,
order: order
})
})
return data
}
function compare (sortData, a, b) {
for (let i = 0; i < sortData.length; ++i) {
const data = sortData[i];
if(!data.key) data.key = Object.keys(a).filter(k=>k==data.rawkey)[0];
const _a = a[data.key]
const _b = b[data.key]
if (_a !== _b) return _a > _b ? data.order : -data.order
}
return 0
}
function exec (data) {
let r = arr.slice()
if (data.sort) {
r.sort((a, b) => {
let r = compare(data.sort, a, b)
return r
})
}
return r
}
function time (f) {
let start = performance.now()
f()
return performance.now() - start
}
function test (f, n) {
let total = 0
for (let i = 0; i < n; ++i) total += time(f)
return total / n
}
const q1 = sort('value')
const q2 = sort('+value')
const q3 = sort('-value')
const sortDefault = () => exec(q1)
const sortAsc = () => exec(q2)
const sortDesc = () => exec(q3)
console.log(' Default Sort (value):', test(sortDefault, 10).toFixed(2) + 'ms')
console.log(' Ascending Sort (+value):', test(sortAsc, 10).toFixed(2) + 'ms')
console.log('Descending Sort (-value):', test(sortDesc, 10).toFixed(2) + 'ms')
答案 2 :(得分:0)
我发现动态构建比较函数可以避免减速,实际上在我(尽管是最小的)测试中几乎所有情况都要快得多。
Default ( value): 5.47ms
Default (+value): 54.17ms
Default (-value): 51.90ms
eval Function ( value): 3.25ms
eval Function (+value): 3.51ms
eval Function (-value): 2.99ms
let arr = []
for (let i = 0; i < 10000; ++i) {
let o = { _id: 'doc' + i, value: Math.random() }
arr.push(o)
}
function sortKeys (...keys) {
const data = {}
data.sort = []
keys.forEach(key => {
if (typeof key === 'string') key = getSortObject(key)
if (key.order === undefined) key.order = 1
data.sort.push(key)
})
return data
}
function getSortObject (str) {
if (str.match(/^(\+|\-)/)) {
return { key: str.substr(1), order: -1 }
} else {
return { key: str, order: 1 }
}
}
function exec (data, compFn) {
let r = arr.slice()
if (data.sort) {
r.sort(compFn(data.sort))
}
return r
}
// Old comparator, seems to cause optimization problems in the V8 engine. (Chrome/NodeJS/etc.)
function comparison (sdata) {
return function (a, b) {
for (let i = 0; i < sdata.length; ++i) {
const data = sdata[i]
const _a = a[data.key]
const _b = b[data.key]
if (_a !== _b) return _a > _b ? data.order : -data.order
}
return 0
}
}
// Builds a function from the sort keys.
function getCompFunc (sortData) {
let str = ''
sortData.forEach(sort => {
str += `if(a.${sort.key}${sort.order === 1 ? '>' : '<'}b.${sort.key})return 1;`
str += `if(a.${sort.key}${sort.order === 1 ? '<' : '>'}b.${sort.key})return -1;`
})
str += `return 0;`
return Function('a', 'b', str)
}
function time (f) {
let start = performance.now()
f()
return performance.now() - start
}
function test (f, n) {
let total = 0
for (let i = 0; i < n; ++i) total += time(f)
return total / n
}
const q1 = sortKeys('value')
const q2 = sortKeys('+value')
const q3 = sortKeys('-value')
const sortDefault = () => exec(q1, comparison)
const sortAsc = () => exec(q2, comparison)
const sortDesc = () => exec(q3, comparison)
const sortDefaultEval = () => exec(q1, getCompFunc)
const sortAscEval = () => exec(q2, getCompFunc)
const sortDescEval = () => exec(q3, getCompFunc)
console.log('A:', test(sortDefault, 5).toFixed(2) + 'ms')
console.log('B:', test(sortAsc, 5).toFixed(2) + 'ms')
console.log('C:', test(sortDesc, 5).toFixed(2) + 'ms')
console.log('-')
console.log('D:', test(sortDefaultEval, 5).toFixed(2) + 'ms')
console.log('E:', test(sortAscEval, 5).toFixed(2) + 'ms')
console.log('F:', test(sortDescEval, 5).toFixed(2) + 'ms')
&#13;