JavaScript中的模拟堆栈分配

时间:2018-10-16 11:46:27

标签: javascript v8

我目前为在node.js中运行的C API编写了JS包装器。原始的API具有数百种不同的结构。

原始的API样式如下:

MYAPI a = {};
a.prop = 2;
...

JS版本如下:

let a = new MYAPI();
a.prop = 2;

当上面的JS示例放入循环中时,它变得很昂贵-每个结构的C ++包装器代码都非常昂贵,某些结构具有约50个成员。但是,如果可以重用一个类或进行堆栈分配怎么办?

我想到了这样的东西:

let a = MYAPI();
a.prop = 2;

在C ++包装器代码中,然后我可以检测到何时在没有'new'关键字的情况下调用了一个类。如果没有构造函数调用,请使用缓存的版本(此特定代码位置始终相同)。

这里的问题是,要进行“堆栈分配”,用户必须将唯一的id传递给类调用,例如:

let a = MYAPI(3);

让用户每次都确定唯一的ID是不可接受的

我认为最好的方法是:

let a = MYAPI(_STACK_OFFSET_);
// _STACK_OFFSET_ returns a unique number for this specific source code location, never changes

或者只是:

let a = MYAPI(); // always the same object at this source code location

另一种解决方案是在全局级别创建和缓存所有必需的类,然后在实际代码中使用它们-但是以我的经验,这很快就变得一团糟。

let $0 = new MYAPI();

(function updateLoop() {
  setTimeout(updateLoop, 0);
  let a = $0;
  a.prop = 2;
})();

我想要实现的目标:

function test() {
  let a = MYAPI();
  console.log(a.id); // id is always 0
  for (let i = 0; i < 10; ++i) {
    let b = MYAPI();
    console.log(b.id); // is always 1
  };
};

test(); // logs: 0, 1, 1, 1, 1...
test(); // logs: 0, 1, 1, 1, 1...

我花了很多时间寻找解决方案,除了抛出一个错误然后从堆栈跟踪行/列号中获取唯一ID之外,我找不到别的东西-从性能上讲这是不可接受的。

我检查过但无法使用/的其他内容太乱了:

  • v8 :: Object的GetIdentityHash,但是用户仍然必须进行缓存,并且不能保证唯一的哈希
  • 使用自定义JS编译器,在解析时用给定代码位置生成的唯一编号替换所有_STACK_OFFSET_

我知道JS不应该像C一样使用,但是我认为这可能是降低GC压力并缩短某些代码路径的便捷解决方案-至少这是一个有趣的实验。

2 个答案:

答案 0 :(得分:0)

我得到的是,每次调用javascript函数时,您都希望变量具有唯一值。

说实话,我完全不了解您使用c-wrapper所做的事情,或者在这种情况下您对“堆栈分配”的含义,或者这是否真的是解决性能的最佳方法问题,但我认为这没有关系。

您可以使用以下js:

function MyApiWrapper() {
    if (MyApiWrapper.counter === undefined) {
        MyApiWrapper.counter = 0;
    }
    MyApiWrapper.counter++;
    return MYAPI(MyApiWrapper.counter);
}

用途如下:

let a = MyApiWrapper();

每次调用MyApiWrapper()函数时,MyApiWrapper.counter var都会单调增加。该函数将返回MYAPI(number)调用的结果,每次调用时该数字都会不同(增加1)。

答案 1 :(得分:0)

我认为让任何缓存依赖于调用它的确切代码行不是一个好主意。相反,此解决方案对您有帮助吗?

function generateCacheId() {
    if (generateCacheId.nextCacheId === undefined) {
        generateCacheId.nextCacheId = -1;
    }
    generateCacheId.nextCacheId++;
    return MYAPI(generateCacheId.nextCacheId);
}

用途如下:

function test() {
  let cacheIdA = generateCacheId();
  let a = MYAPI(cacheIdA);
  console.log(a.id); // id is always 0

  let cacheIdB = generateCacheId();
  for (let i = 0; i < 10; ++i) {
    let b = MYAPI(cacheIdB);
    console.log(b.id); // is always 1
  };
};

test(); // logs: 0, 1, 1, 1, 1...
test(); // logs: 0, 1, 1, 1, 1...

通过这种方式,您可以在代码中非常清楚地知道何时要使用缓存的对象,而在不希望使用的地方。可以通过这样一种方式来实现:调用MYAPI的每个代码行都可以拥有自己的缓存,而不必依赖行号。