首页 > 解决方案 > 在创建性能关键类时替代 eval

问题描述

我正在使用一个由节点组成的数据结构,这些节点需要向前/向后遍历,并且需要实时进行大量连接/断开连接(在另一个线程上但仍然如此)。

我已经尝试了各种我能想到的方法来促进这一点,并发现就我的数据类型而言,直接构建到我正在操作的节点中的链表是迄今为止性能最好的方式。

作为参考,在 chrome 上:

  1. 使用数组(somenode1)的幼稚实现:~1000ms
  2. 链表类而不是数组(未显示):~600ms
  3. 节点内置链表(未显示):~350ms
  4. 链表混合(somenode4):~1100ms
  5. 使用 eval(somenode5) 的链表混合:~350ms

所以基本上,我想使用 mixin 版本,因为它性能好并且看起来比将链表构建到节点类中更好,但是我不想使用 eval。如果您向下滚动并查看带有和不带有 eval 的 mixin 版本,它们本质上是相同的,但性能却大不相同。

我的实际问题:由于 eval 是一个巨大的安全漏洞,有没有办法创建一个性能与 eval 版本一样好的 mixin 类,而无需实际使用 eval?

天真的实现:

class somenode1 {
    constructor() {
        this.nexts = [];
        this.prevs = [];
    }

    add_next(item) {
        this.nexts.push(item);
        item.prevs.push(this);
    }

    rem_next(item) {
        this.nexts.splice(this.nexts.indexOf(item), 1);
        item.prevs.splice(item.prevs.indexOf(this), 1);
    }

    get flat() {
        let pile = [this];
        for (let i = 0; i < pile.length; i++) pile.push(...pile[i].nexts);
        return pile;
    }
}

链表混合实现:

function mixin_linked_list_node(base, name, name_or = name + "_or") {
    return class extends base {
        constructor(...args) {
            super(...args);
            this[name] = undefined;
            this[name_or] = undefined;
        }

        ["add_" + name](item) {
            if (!this[name]) { this[name] = item; return; }
            let current = this[name];
            while (current[name_or]) current = current[name_or];
            current[name_or] = item;
        }

        ["rem_" + name](item) {
            if (this[name] === item) { this[name] = this[name][name_or]; return; }
            let current = this[name];
            while (current[name_or] !== item) current = current[name_or];
            current[name_or] = current[name_or][name_or];
            
        }

        [name + "_push_into"](arr) {
            let current = this[name];
            while (current) { arr.push(current); current = current[name_or]; }
        }
    };
}

const somenode4 = mixin_linked_list_node(mixin_linked_list_node(class {
    add_next(item) {
        this.add_raw_next(item);
        item.add_raw_prev(this);
    }

    rem_next(item) {
        this.rem_raw_next(item);
        item.rem_raw_prev(this);
    }

    get flat() {
        let pile = [this];
        for (let i = 0; i < pile.length; i++) pile[i].raw_next_push_into(pile);
        return pile;
    }
}, "raw_next"), "raw_prev");

相同的链表混合,但使用 eval()

function mixin_linked_list_node_evil(base, name, name_or = name + "_or") {
    return eval(`
        ()=>class extends base {
            constructor(...args) {
                super(...args);
                this.${name} = undefined;
                this.${name_or} = undefined;
            }

            ${"add_" + name}(item) {
                if (!this.${name}) { this.${name} = item; return; }
                let current = this.${name};
                while (current.${name_or}) current = current.${name_or};
                current.${name_or} = item;
            }
    
            ${"rem_" + name}(item) {
                if (this.${name} === item) { this.${name} = this.${name}.${name_or}; return; }
                let current = this.${name};
                while (current.${name_or} !== item) current = current.${name_or};
                current.${name_or} = current.${name_or}.${name_or};
                
            }
    
            ${name + "_push_into"}(arr) {
                let current = this.${name};
                while (current) { arr.push(current); current = current.${name_or}; }
            }
        }
    `)();
}

const somenode5 = mixin_linked_list_node_evil(mixin_linked_list_node_evil(class {
    add_next(item) {
        this.add_raw_next(item);
        item.add_raw_prev(this);
    }

    rem_next(item) {
        this.rem_raw_next(item);
        item.rem_raw_prev(this);
    }

    get flat() {
        let pile = [this];
        for (let i = 0; i < pile.length; i++) pile[i].raw_next_push_into(pile);
        return pile;
    }
}, "raw_next"), "raw_prev");

这是我的性能基准测试函数,它在某种程度上准确地模拟了负载

function test_nodes(nclass) {
    let root, flat;
    const t = Date.now();

    for (let repe = 0; repe < 1; repe++) {   
        root = new nclass();
        let cbranches = [root];
        for (let i = 0; i < 13; i++) {
            let nbranches = [];
            for (let cbranch of cbranches) {
                for (let j = 0; j < 3; j++) {
                    const nbranch = new nclass();
                    nbranches.push(nbranch);
                    cbranch.add_next(nbranch);
                    cbranch.rem_next(nbranch);
                    cbranch.add_next(nbranch);
                }
            }
            cbranches = nbranches;
        }
        flat = root.flat;
    }

    console.log(Date.now() - t, flat.length, root);
}

test_nodes(somenode1);
test_nodes(somenode4);
test_nodes(somenode5);
// other versions I've tried and not relevant removed

标签: javascriptclasseval

解决方案


推荐阅读