服务器同屏人数多的时候,出现了卡顿现象,服务器的一帧运行超过了1s,经过排查发现是人物计算属性的地方卡顿.服务器架构中玩家对应有SetupCharBase(),用来计算玩家攻防,玩家的普通攻击需要buff面板后计算伤害,会调用这个函数。
函数里面各个功能模块的计算写在了函数的最前面,计算宠物附加属性,地元附加属性各种功能模块。这块的原则应该是在玩家load的时候就计算好,在改变的时候,算好数值存到内存里,SetupCharBase()只作读取,进行简单的运算得到结果。在SetupCharBase进行运算,就会导致多人PK的时候耗费大量的运算资源。
进行的优化过程中,发现了一些平常不好习惯导致的性能消耗。cpuinfo中,stl容器的赋值操作,析构操作占用了大量的资源,这来自于读取配置xml结构的时候,auto后面没有加&符号,导致进行了赋值操作,有构造和析构的成本。而大型MMO的配置都很复杂,会很消耗性能。传值问题不仅仅存在于读取配置,在一些函数传参和返回值时也是一样的。《Effective C++》中“Prefer pass-by-reference-to-const to pass-by-value”和“Don't try to return a reference when you must return an object”这两个条款详细描述了引用,指针,传值需要注意的问题,这里不做展开。
为了配置方便,区服配置,策划喜欢用,表示多个区并列,例如1,2,3,4表示四个区都满足条件,而字符串切分很耗效率,这部分可以挪到服务器启动时,提前做好切分,存到内存对应的数据结构中,逻辑实现直接取对应的容器就可以了。为了满足能在线热载的需求,在对应的HUP信号处理函数中也调用相同函数提前初始化好即可。
不仅仅是字符串切分,有一些配置是excel配置的,逻辑中对应的Key是excel某一列的值,不是唯一的标识,这样取对应的行数据会取到多个,如果直接读取配置,需要for循环遍历excel配置的每一行找到满足条件的N行然后进行处理。这里优化可以在服务器启动以及HUP信号处理函数处,构建好对应的容器std::map<uint, std::vector<Info>>,在相应逻辑处调用find,就可以避免遍历整个excel了。
dynamic_cast的调用,会比较虚函数表,也就是一层一层匹配v-table,开销巨大。在npc伤害的时候调用了dynamic_cast,这里进行了优化,在基类设计虚函数返回nullptr,对应的子类进行实现,返回this。这样能直接取到虚函数地址,优化效率。以下是dynamic_cast rtti.cpp的实现。
#include <stdio.h>
#include <typeinfo>
#include "rtti.h"
#pragma warning(disable:4297)
static PVOID __cdecl FindCompleteObject(PVOID *);
static _RTTIBaseClassDescriptor * __cdecl FindSITargetTypeInstance(PVOID,_RTTICompleteObjectLocator *,TypeDescriptor *,int,TypeDescriptor *);
static _RTTIBaseClassDescriptor * __cdecl FindMITargetTypeInstance(PVOID,_RTTICompleteObjectLocator *,TypeDescriptor *,int,TypeDescriptor *);
static _RTTIBaseClassDescriptor * __cdecl FindVITargetTypeInstance(PVOID,_RTTICompleteObjectLocator *,TypeDescriptor *,int,TypeDescriptor *);
static ptrdiff_t __cdecl PMDtoOffset(PVOID pThis, const PMD& pmd);
extern "C" PVOID __cdecl __RTtypeid (PVOID inptr)
{
if (!inptr) {
throw std::bad_typeid ("Attempted a typeid of NULL pointer!");
return NULL;
}
__try {
// Ptr to CompleteObjectLocator should be stored at vfptr[-1]
_RTTICompleteObjectLocator *pCompleteLocator = (_RTTICompleteObjectLocator *) ((*((void***)inptr))[-1]);
return (PVOID) pCompleteLocator->pTypeDescriptor;
}
__except (GetExceptionCode() == EXCEPTION_ACCESS_VIOLATION ? EXCEPTION_EXECUTE_HANDLER: EXCEPTION_CONTINUE_SEARCH)
{
throw std::__non_rtti_object ("Access violation - no RTTI data!");
}
}
extern "C" PVOID __cdecl __RTDynamicCast (
PVOID inptr, // Pointer to polymorphic object
LONG VfDelta, // Offset of vfptr in object
PVOID SrcType, // Static type of object pointed to by inptr
PVOID TargetType, // Desired result of cast
BOOL isReference) // TRUE if input is reference, FALSE if input is ptr
{
PVOID pResult;
_RTTIBaseClassDescriptor *pBaseClass;
if (inptr == NULL)
return NULL;
__try {
PVOID pCompleteObject = FindCompleteObject((PVOID *)inptr);
_RTTICompleteObjectLocator *pCompleteLocator = (_RTTICompleteObjectLocator *) ((*((void***)inptr))[-1]);
// Adjust by vfptr displacement, if any
inptr = (PVOID *) ((char *)inptr - VfDelta);
// Calculate offset of source object in complete object
int inptr_delta = (char *)inptr - (char *)pCompleteObject;
if (!(CHD_ATTRIBUTES(*COL_PCHD(*pCompleteLocator)) & CHD_MULTINH)) { // if not multiple inheritance
pBaseClass = FindSITargetTypeInstance(pCompleteObject,
pCompleteLocator,
(TypeDescriptor *) SrcType,
inptr_delta,
(TypeDescriptor *) TargetType);
} else if (!(CHD_ATTRIBUTES(*COL_PCHD(*pCompleteLocator)) & CHD_VIRTINH)) { // if multiple, but not virtual, inheritance
pBaseClass = FindMITargetTypeInstance(pCompleteObject,
pCompleteLocator,
(TypeDescriptor *) SrcType,
inptr_delta,
(TypeDescriptor *) TargetType);
} else { // if virtual inheritance
pBaseClass = FindVITargetTypeInstance(pCompleteObject,
pCompleteLocator,
(TypeDescriptor *) SrcType,
inptr_delta,
(TypeDescriptor *) TargetType);
}
if (pBaseClass != NULL) {
// Calculate ptr to result base class from pBaseClass->where
pResult = ((char *) pCompleteObject) + PMDtoOffset(pCompleteObject, pBaseClass->where);
}else {
pResult = NULL;
if (isReference) {
throw std::bad_cast("Bad dynamic_cast!");
}
}
}
__except (GetExceptionCode() == EXCEPTION_ACCESS_VIOLATION ? EXCEPTION_EXECUTE_HANDLER: EXCEPTION_CONTINUE_SEARCH) {
pResult = NULL;
throw std::__non_rtti_object ("Access violation - no RTTI data!");
}
return pResult;
}
/
//
// FindCompleteObject - Calculate member offset from PMD & this
//
// Output: pointer to the complete object containing class *inptr
//
// Side-effects: NONE.
//
static PVOID __cdecl FindCompleteObject (PVOID *inptr) // Pointer to polymorphic object
{
// Ptr to CompleteObjectLocator should be stored at vfptr[-1]
_RTTICompleteObjectLocator *pCompleteLocator = (_RTTICompleteObjectLocator *) ((*((void***)inptr))[-1]);
char *pCompleteObject = (char *)inptr - pCompleteLocator->offset;
// Adjust by construction displacement, if any
if (pCompleteLocator->cdOffset)
pCompleteObject += *(ptrdiff_t *)((char *)inptr - pCompleteLocator->cdOffset);
return (PVOID) pCompleteObject;
}
static _RTTIBaseClassDescriptor * __cdecl FindSITargetTypeInstance (
PVOID pCompleteObject, // pointer to complete object
_RTTICompleteObjectLocator *pCOLocator, // pointer to Locator of complete object
TypeDescriptor *pSrcTypeID, // pointer to TypeDescriptor of source object
int SrcOffset, // offset of source object in complete object
TypeDescriptor *pTargetTypeID) // pointer to TypeDescriptor of result of cast
{
_RTTIBaseClassDescriptor *pBase;
_RTTIBaseClassDescriptor * const *pBasePtr;
DWORD i;
for (i = 0, pBasePtr = pCOLocator->pClassDescriptor->pBaseClassArray->arrayOfBaseClassDescriptors;
i < pCOLocator->pClassDescriptor->numBaseClasses;
i++, pBasePtr++) {
// Test type of selected base class
pBase = *pBasePtr;
if (TYPEIDS_EQ(pBase->pTypeDescriptor, pTargetTypeID) &&
!(BCD_ATTRIBUTES(*pBase) & BCD_NOTVISIBLE)) {
return pBase;
}
}
return NULL;
}
static _RTTIBaseClassDescriptor * __cdecl FindMITargetTypeInstance (
PVOID pCompleteObject, // pointer to complete object
_RTTICompleteObjectLocator *pCOLocator, // pointer to Locator of complete object
TypeDescriptor *pSrcTypeID, // pointer to TypeDescriptor of source object
int SrcOffset, // offset of source object in complete object
TypeDescriptor *pTargetTypeID) // pointer to TypeDescriptor of result of cast
{
_RTTIBaseClassDescriptor *pBase, *pSubBase;
_RTTIBaseClassDescriptor * const *pBasePtr, * const *pSubBasePtr;
DWORD i, j;
// First, try down-casts
for (i = 0, pBasePtr = pCOLocator->pClassDescriptor->pBaseClassArray->arrayOfBaseClassDescriptors;
i < pCOLocator->pClassDescriptor->numBaseClasses;
i++, pBasePtr++) {
pBase = *pBasePtr;
// Test type of selected base class
if (TYPEIDS_EQ(pBase->pTypeDescriptor, pTargetTypeID)) {
// If base class is proper type, see if it contains our instance of source class
for (j = 0, pSubBasePtr = pBasePtr+1;
j < pBase->numContainedBases;
j++, pSubBasePtr++) {
pSubBase = *pSubBasePtr;
if (TYPEIDS_EQ(pSubBase->pTypeDescriptor, pSrcTypeID) &&
(PMDtoOffset(pCompleteObject, pSubBase->where) == SrcOffset)) {
// Yes, this is the proper instance of source class
return pBase;
}
}
}
}
// Down-cast failed, try cross-cast
for (i = 0, pBasePtr = pCOLocator->pClassDescriptor->pBaseClassArray->arrayOfBaseClassDescriptors;
i < pCOLocator->pClassDescriptor->numBaseClasses;
i++, pBasePtr++) {
pBase = *pBasePtr;
// Check if base class has proper type, is accessible & is unambiguous
if (TYPEIDS_EQ(pBase->pTypeDescriptor, pTargetTypeID) &&
!(BCD_ATTRIBUTES(*pBase) & BCD_NOTVISIBLE) &&
!(BCD_ATTRIBUTES(*pBase) & BCD_AMBIGUOUS)) {
return pBase;
}
}
return NULL;
}
static _RTTIBaseClassDescriptor * __cdecl FindVITargetTypeInstance (
PVOID pCompleteObject, // pointer to complete object
_RTTICompleteObjectLocator *pCOLocator, // pointer to Locator of complete object
TypeDescriptor *pSrcTypeID, // pointer to TypeDescriptor of source object
int SrcOffset, // offset of source object in complete object
TypeDescriptor *pTargetTypeID) // pointer to TypeDescriptor of result of cast
{
_RTTIBaseClassDescriptor *pBase, *pSubBase;
_RTTIBaseClassDescriptor * const *pBasePtr, * const *pSubBasePtr;
_RTTIBaseClassDescriptor *pResult = NULL;
DWORD i, j;
// First, try down-casts
for (i = 0, pBasePtr = pCOLocator->pClassDescriptor->pBaseClassArray->arrayOfBaseClassDescriptors;
i < pCOLocator->pClassDescriptor->numBaseClasses;
i++, pBasePtr++) {
pBase = *pBasePtr;
// Test type of selected base class
if (TYPEIDS_EQ(pBase->pTypeDescriptor, pTargetTypeID)) {
// If base class is proper type, see if it contains our instance of source class
for (j = 0, pSubBasePtr = pBasePtr+1;
j < pBase->numContainedBases;
j++, pSubBasePtr++) {
pSubBase = *pSubBasePtr;
if (TYPEIDS_EQ(pSubBase->pTypeDescriptor, pSrcTypeID) &&
(PMDtoOffset(pCompleteObject, pSubBase->where) == SrcOffset)) {
// Yes, this is the proper instance of source class - make sure it is unambiguous
// Ambiguity now determined by inequality of offsets of source class within complete object, not pointer inequality
if ((pResult != NULL) && (PMDtoOffset(pCompleteObject, pResult->where) != PMDtoOffset(pCompleteObject, pBase->where))) {
// We already found an earlier instance, hence ambiguity
return NULL;
}
else {
// Unambiguous
pResult = pBase;
}
}
}
}
}
if (pResult != NULL)
return pResult;
// Down-cast failed, try cross-cast
for (i = 0, pBasePtr = pCOLocator->pClassDescriptor->pBaseClassArray->arrayOfBaseClassDescriptors;
i < pCOLocator->pClassDescriptor->numBaseClasses;
i++, pBasePtr++) {
pBase = *pBasePtr;
// Check if base class has proper type, is accessible & is unambiguous
if (TYPEIDS_EQ(pBase->pTypeDescriptor, pTargetTypeID) &&
!(BCD_ATTRIBUTES(*pBase) & BCD_NOTVISIBLE) &&
!(BCD_ATTRIBUTES(*pBase) & BCD_AMBIGUOUS)) {
return pBase;
}
}
return NULL;
}
static ptrdiff_t __cdecl PMDtoOffset(
PVOID pThis, // ptr to complete object
const PMD& pmd) // pointer-to-member-data structure
{
ptrdiff_t RetOff = 0;
if (pmd.pdisp >= 0) { // if base is in the virtual part of class
RetOff = pmd.pdisp;
RetOff += *(ptrdiff_t*)((char*)*(ptrdiff_t*)((char*)pThis + RetOff) + pmd.vdisp);
}
RetOff += pmd.mdisp;
return RetOff;
}