对cocos2dx-lite的JSB做针对性优化

前言

cocos2dx-lite(creator 2.0之前版本)同时支持多种js的vm,所以也就基本没有做针对特定vm的优化,项目优化时决定坚定的走V8路线,启动了针对V8的专门优化。当然这些优化都用宏定义分离开了,保障V8不可用时原有逻辑仍然不变。

具体优化条目

  • jsb绑定部分,避开se::State和se::Value直接生成V8形式的绑定,实测简单情况如 setPosition 函数效率提升50%左右。

  • 更新V8到8.0以上,开启指针压缩,降低内存 20%左右,实际项目不同会有差别,别问我怎么编译的

  • 所有源码使用v8::String::ExternalOneByteStringResource管理,减少内存copy(c++ -> v8),避免字符转码(ut8 -> utf16)消耗,实际降低内存 40M ,当然这取决于脚本文件的多少,这个项目有大量脚本。为什么是OneByteString,因为js是utf16内码,但v8有个优化处理对于纯单字节的字符串是可以用OneByteString表示的可以省一半内存,当然源码需要预处理一下,使用unicode转意表示

    1
    2
    3
    4
    5
    6
    7
    function to_latin1(str) {
    return str.replace(/[\u007f-\uffff]/g, function(ch) {
    var code = ch.charCodeAt(0).toString(16);
    while (code.length < 4) code = "0" + code;
    return "\\u" + code;
    });
    }
  • 使用v8::ArrayBuffer::Allocator使得部分ArrayBuffer可以使用宿主内存,避免宿主与VM间的copy

  • 对纹理等大内存对象通过Isolate::AdjustAmountOfExternalAllocatedMemory告知V8外部内存占用情况

  • 启用WeakRef启动参数增加--harmony-weak-refs,可以使用新标准中的WeakRef,解决动态加载的图片释放问题(项目有大量网络下载的图片)。目前会偶有崩溃,不确定是接入方式不对,还是什么原因,仍在观察中

其它

  • 通用性和效率总是需要平衡的两个方面,针对具体项目是可以做一些更激进的选择
  • V8是个好项目,但文档还不够细,读代码可以了解细节
  • 实在搞不清楚了可以参考node.js与V8结合的代码

一段应急的C++对象检查代码

一个JNI项目,偶发离奇崩溃,怀疑是野指针类的问题。开address sanitizer本地难复现,测试版开as太卡,大部分测试机还是6、7的系统。

还好大部分都对象都派生自引用计数的基类,于是按fail-fast的思路憋出了以下代码,在加减引用怀疑区域手动插桩检查。

附上代码,无关部分做了简化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
#include <type_traits>
#include <stdio.h>
#include <assert.h>

#define LOG(...) do { fprintf(stderr, __VA_ARGS__); fflush(stderr); } while (false)

class Object
{
public:
Object()
: _falgs(0xcafecafe)
{
}
virtual ~Object()
{
_falgs = 0xdeaddead;
}
// is not strict but help avoid most of error
bool isValid() const
{
return static_cast<const void *>(this) && (_falgs == 0xcafecafe);
}

private:
int _falgs;
};

class ObjectCheck
{
public:
template <typename T>
static bool Check(T obj)
{
return doCheck<T>(obj);
}

template <typename T>
static bool CheckNullable(T obj)
{
return obj == nullptr || doCheck<T>(obj);
}

private:
template <typename T>
static bool doCheck(typename std::enable_if<std::is_base_of<Object, typename std::remove_pointer<T>::type>::value && !std::is_void<typename std::remove_pointer<T>::type>::value, T>::type obj)
{
LOG("dynamic_cast && isValid %p %p ", dynamic_cast<T>(reinterpret_cast<Object*>(obj)), obj);
return (obj == dynamic_cast<T>(reinterpret_cast<Object*>(obj)) && obj->isValid());
}

template <typename T>
static bool doCheck(typename std::enable_if<!std::is_base_of<Object, typename std::remove_pointer<T>::type>::value && std::is_polymorphic<typename std::remove_pointer<T>::type>::value, T>::type obj)
{
LOG("dynamic_cast %p %p ", dynamic_cast<T>(obj), obj);
return (obj == dynamic_cast<T>(obj));
}

template <typename T>
static bool doCheck(typename std::enable_if<std::is_void<typename std::remove_pointer<T>::type>::value, T>::type obj)
{
LOG("static_cast void* ");
return (obj == static_cast<void *>(obj));
}
};

class Dummy
{
public:
virtual ~Dummy() = default;
int dummy;
};
class SubObject : public Object
{
public:
int dummy;
};

int main()
{
void *t1 = nullptr;
Object *t2 = nullptr;
Dummy *t3 = nullptr;
SubObject *t4 = nullptr;
Object *t5 = new Object();
Dummy *t6 = new Dummy();
SubObject *t7 = new SubObject();
SubObject *t8 = reinterpret_cast<SubObject *>(t6);
SubObject *t9 = reinterpret_cast<SubObject *>(t5);
SubObject *t10 = new SubObject();
delete t10;

LOG("result %d \n", ObjectCheck::CheckNullable(t1));
LOG("result %d \n", ObjectCheck::CheckNullable(t2));
LOG("result %d \n", ObjectCheck::CheckNullable(t3));
LOG("result %d \n", ObjectCheck::CheckNullable(t4));
LOG("result %d \n", ObjectCheck::Check(t5));
LOG("result %d \n", ObjectCheck::Check(t6));
LOG("result %d \n", ObjectCheck::Check(t7));
LOG("result %d \n", ObjectCheck::Check(t8));
LOG("result %d \n", ObjectCheck::Check(t9));
LOG("result %d \n", ObjectCheck::Check(t10));
}

输出

1
2
3
4
5
6
7
8
9
10
result 1 
result 1
result 1
result 1
dynamic_cast && isValid 0x7fca1cc02b40 0x7fca1cc02b40 result 1
dynamic_cast 0x7fca1cc02b50 0x7fca1cc02b50 result 1
dynamic_cast && isValid 0x7fca1cc02b60 0x7fca1cc02b60 result 1
dynamic_cast && isValid 0x0 0x7fca1cc02b50 result 0
dynamic_cast && isValid 0x0 0x7fca1cc02b40 result 0
[1] 30935 segmentation fault ./a.out

Android Studio 3.6.X 不能直接运行老项目的问题

今天升级了Android Studio到 3.6.2,结果项目不能直接run到手机里了,报错如下

ApkProvisionException: No outputs for the main artifact of variant: debug

降级 AS 或 升级 Gradle 成本都颇高

可以通过修改 Installation Options 临时解决
具体修改是 Deploy 设置为 Nothing 并增加一个 Before lanuch 的 gradle task
参考下图:

androidstudio

Vue+three.js

朋友要做个h5 app项目,里面需要显示3D模型,问我这个做游戏的,用什么合适,是不是得用游戏引擎。

想了想觉得 Vue|React+three.js 配合起来应该合适。

做了个demo验证一下。

Demo的GitHub仓库传送门