CPPFormatLibary,以下简称FL,介绍:关于CPPFormatLibary

与stringstream,甚至C库的sprintf系列想比,FL在速度上都有优势,而且是在支持.net格式化风格的基础上。要实现这一点,需要多种优化结合在一起,有代码技巧方面的,也有设计策略上的。下面简要的对这些内容进行讲解:

1.  Pattern缓存

在C库函数sprintf中,比如这行代码:

          char szBuf[];
sprintf_s(szBuf, "%d--#--%8.2f--#--%s", , -40.2f, " String ");

格式化字符串"%d--#--%8.2f--#--%s"在每次函数调用的时候,都需要分析一次,依次找出对应的格式化符,在实际开发过程中,多数情况下,格式化字符串并没有任何不同,因此这个分析属于重复分析。因此在设计FL时将这样的格式化字符串称为PatternList,并使用Hash容器对这个PatternList进行存储,在每次格式化之前,首先在容器中查找对应字符串的Pattern是否已经存在,有的话则直接使用已经分析的结果。

下面的代码是Pattern的定义,PatternList则为对应的数组:

 1         /**
* @brief This is the description of a Format unit
* @example {0} {0:d}
*/
template < typename TCharType >
class TFormatPattern
{
public:
typedef TCharType CharType;
typedef unsigned char ByteType;
typedef std::size_t SizeType; enum EFormatFlag
{
FF_Raw,
FF_None,
FF_Decimal,
FF_Exponent,
FF_FixedPoint,
FF_General,
FF_CSV,
FF_Percentage,
FF_Hex
}; enum EAlignFlag
{
AF_Right,
AF_Left
}; TFormatPattern() :
Start((SizeType)-),
Len(),
Flag(FF_Raw),
Align(AF_Right),
Index((ByteType)-),
Precision((ByteType)-),
Width((ByteType)-) {
} SizeType GetLegnth() const
{
return Len;
} bool IsValid() const
{
return Start != - && Len != - && Index >= ;
} bool HasWidth() const
{
return Width != (ByteType)-;
} bool HasPrecision() const
{
return Precision != (ByteType)-;
} public:
SizeType Start;
SizeType Len;
EFormatFlag Flag;
EAlignFlag Align; ByteType Index;
ByteType Precision;
ByteType Width;
};

这个Pattern就代表了分析格式化字符串的每一个单元。

1         StandardLibrary::FormatTo(str, "{0}--#--{1,8}--#--{2}", , -40.2f, " String ");

在这行代码中,PatternList一共有5个Pattern,分别是:

    {} 参数0

     --#-- 原始类型

    {,} 参数1 宽度8

    --#-- 原始类型 纯字符串

    {} 参数2

这样设计可以优化掉重复的字符串Parse。

2.各种类型到字符串转换的算法优化

这部分代码完全存在于文件Algorithm.hpp中,这里面包含了诸如int、double等转换为字符串的快速算法,实测性能优于sprintf和atoi之类。通过这些基础算法的优化,性能可以得到相当不错的提升。

        template < typename TCharType >
inline void StringReverse(TCharType* Start, TCharType* End)
{
TCharType Aux; while (Start < End)
{
Aux = *End;
*End-- = *Start;
*Start++ = Aux;
}
} namespace Detail
{
const char DigitMap[] =
{
'', '', '', '', '', '', '',
'', '', '', 'A', 'B', 'C', 'D',
'E', 'F'
};
} template < typename TCharType >
inline SIZE_T Int64ToString(INT64 Value, TCharType* Buf, INT Base)
{
assert(Base > && static_cast<SIZE_T>(Base) <= _countof(Detail::DigitMap)); TCharType* Str = Buf; UINT64 UValue = (Value < ) ? -Value : Value; // Conversion. Number is reversed.
do
{
*Str++ = Detail::DigitMap[UValue%Base];
} while (UValue /= Base); if (Value < )
{
*Str++ = '-';
} *Str = '\0'; // Reverse string
StringReverse<TCharType>(Buf, Str - ); return Str - Buf;
}

上面这段代码展示的是快速整数到字符串的转换。据说基于sse指令的各种转换会更快,然而担心兼容性问题影响跨平台,我并未采用。

3. 栈容器和栈字符串

这部分代码存在于文件Utility.hpp中,这部分代码的优化原理就是在需要的动态内存并不大的时候,直接使用栈内存,当内存需求大到超过一定阀值的时候,自动申请堆内存并将栈数据转移到堆内存上。在大多数情况下,我们需要的内存都是很少,因此在绝大多数情况下,都能起到相当显著的优化效果。

         template <
typename T,
INT DefaultLength = 0xFF,
INT ExtraLength =
>
class TAutoArray
{
public:
typedef TAutoArray<T, DefaultLength, ExtraLength> SelfType; enum
{
DEFAULT_LENGTH = DefaultLength
}; class ConstIterator : Noncopyable
{
public:
ConstIterator(const SelfType& InRef) :
Ref(InRef),
Index( InRef.GetLength()>?:- )
{
} bool IsValid() const
{
return Index < Ref.GetLength();
} bool Next()
{
++Index; return IsValid();
} const T& operator *() const
{
const T* Ptr = Ref.GetDataPtr(); return Ptr[Index];
}
private:
ConstIterator& operator = (const ConstIterator&);
ConstIterator(ConstIterator&);
protected:
const SelfType& Ref;
SIZE_T Index;
}; TAutoArray() :
Count(),
AllocatedCount(),
HeapValPtr(NULL)
{
} ~TAutoArray()
{
ReleaseHeapData(); Count = ;
} TAutoArray(const SelfType& Other) :
Count(Other.Count),
AllocatedCount(Other.AllocatedCount),
HeapValPtr(NULL)
{
if (Count > )
{
if (Other.IsDataOnStack())
{
Algorithm::CopyArray(Other.StackVal, Other.StackVal + Count, StackVal);
}
else
{
HeapValPtr = Allocate(AllocatedCount);
Algorithm::CopyArray(Other.HeapValPtr, Other.HeapValPtr + Count, HeapValPtr);
}
}
} SelfType& operator = (const SelfType& Other)
{
if (this == &Other)
{
return *this;
} ReleaseHeapData(); Count = Other.Count;
AllocatedCount = Other.AllocatedCount;
HeapValPtr = NULL; if (Count > )
{
if (Other.IsDataOnStack())
{
Algorithm::CopyArray(Other.StackVal, Other.StackVal + Count, StackVal);
}
else
{
HeapValPtr = Allocate(AllocatedCount);
Algorithm::CopyArray(Other.HeapValPtr, Other.HeapValPtr + Count, HeapValPtr);
}
} return *this;
} SelfType& TakeFrom(SelfType& Other)
{
if (this == &Other)
{
return *this;
} Count = Other.Count;
AllocatedCount = Other.AllocatedCount;
HeapValPtr = Other.HeapValPtr; if (Count > && Other.IsDataOnStack())
{
Algorithm::MoveArray(Other.StackVal, Other.StackVal + Count, StackVal);
} Other.Count = ;
Other.AllocatedCount = ;
Other.HeapValPtr = NULL;
} void TakeTo(SelfType& Other)
{
Other.TakeFrom(*this);
} #if FL_PLATFORM_HAS_RIGHT_VALUE_REFERENCE
TAutoArray( SelfType && Other ) :
Count(Other.Count),
AllocatedCount(Other.AllocatedCount),
HeapValPtr(Other.HeapValPtr)
{
if (Count > && Other.IsDataOnStack())
{
Algorithm::MoveArray(Other.StackVal, Other.StackVal + Count, StackVal);
} Other.Count = ;
Other.AllocatedCount = ;
Other.HeapValPtr = NULL;
} SelfType& operator = (SelfType&& Other )
{
return TakeFrom(Other);
}
#endif bool IsDataOnStack() const
{
return HeapValPtr == NULL;
} void AddItem(const T& InValue)
{
if (IsDataOnStack())
{
if (Count < DEFAULT_LENGTH)
{
StackVal[Count] = InValue;
++Count;
}
else if (Count == DEFAULT_LENGTH)
{
InitialMoveDataToHeap(); assert(Count < AllocatedCount); HeapValPtr[Count] = InValue;
++Count;
}
else
{
assert(false && "internal error");
}
}
else
{
if (Count < AllocatedCount)
{
HeapValPtr[Count] = InValue;
++Count;
}
else
{
ExpandHeapSpace(); assert(Count < AllocatedCount);
HeapValPtr[Count] = InValue;
++Count;
}
}
} SIZE_T GetLength() const
{
return Count;
} SIZE_T GetAllocatedCount() const
{
return AllocatedCount;
} T* GetDataPtr()
{
return IsDataOnStack() ? StackVal : HeapValPtr;
} const T* GetDataPtr() const
{
return IsDataOnStack() ? StackVal : HeapValPtr;
} T* GetUnusedPtr()
{
return IsDataOnStack() ? StackVal + Count : HeapValPtr + Count;
} const T* GetUnusedPtr() const
{
return IsDataOnStack() ? StackVal + Count : HeapValPtr + Count;
} SIZE_T GetCapacity() const
{
return IsDataOnStack() ?
DEFAULT_LENGTH - Count :
AllocatedCount - Count;
} T& operator []( SIZE_T Index )
{
assert( Index < GetLength() ); return GetDataPtr()[Index];
} const T& operator []( SIZE_T Index ) const
{
assert( Index < GetLength() ); return GetDataPtr()[Index];
} protected:
void InitialMoveDataToHeap()
{
assert(HeapValPtr == NULL); AllocatedCount = DEFAULT_LENGTH * ; HeapValPtr = Allocate(AllocatedCount); #if FL_PLATFORM_HAS_RIGHT_VALUE_REFERENCE
Algorithm::MoveArray(StackVal, StackVal + Count, HeapValPtr);
#else
Algorithm::CopyArray(StackVal, StackVal + Count, HeapValPtr);
#endif
} void ExpandHeapSpace()
{
SIZE_T NewCount = AllocatedCount * ;
assert(NewCount > AllocatedCount); T* DataPtr = Allocate(NewCount); assert(DataPtr); #if FL_PLATFORM_HAS_RIGHT_VALUE_REFERENCE
Algorithm::MoveArray(HeapValPtr, HeapValPtr + Count, DataPtr);
#else
Algorithm::CopyArray(HeapValPtr, HeapValPtr + Count, DataPtr);
#endif
ReleaseHeapData(); HeapValPtr = DataPtr;
AllocatedCount = NewCount;
} void ReleaseHeapData()
{
if (HeapValPtr)
{
delete[] HeapValPtr;
HeapValPtr = NULL;
} AllocatedCount = ;
} static T* Allocate(const SIZE_T InAllocatedCount)
{
// +ExtraLength this is a hack method for saving string on it.
return new T[InAllocatedCount + ExtraLength];
} protected:
SIZE_T Count;
SIZE_T AllocatedCount; // +ExtraLength this is a hack method for saving string on it.
T StackVal[DEFAULT_LENGTH + ExtraLength]; T* HeapValPtr;
};

上面这段代码展示的就是这个设想的实现。这是一个模板类,基于这个类实现了栈字符串。同时默认的PatternList也是使用这个模板来保存的,这样在节约了大量的内存分配操作之后,性能得到进一步的提升。

 // String Wrapper
template < typename TCharType >
class TAutoString :
public TAutoArray< TCharType, 0xFF, >
{
public:
typedef TAutoArray< TCharType, 0xFF, > Super;
typedef Mpl::TCharTraits<TCharType> CharTraits;
typedef TCharType CharType; #if !FL_COMPILER_MSVC
using Super::Count;
using Super::AllocatedCount;
using Super::HeapValPtr;
using Super::StackVal;
using Super::Allocate;
using Super::IsDataOnStack;
using Super::DEFAULT_LENGTH;
using Super::GetDataPtr;
using Super::ReleaseHeapData;
#endif TAutoString()
{
} TAutoString(const CharType* pszStr)
{
if (pszStr)
{
const SIZE_T Length = CharTraits::length(pszStr); Count = Length; if (Length <= DEFAULT_LENGTH)
{
CharTraits::copy(pszStr, pszStr + Length, StackVal);
StackVal[Count] = ;
}
else
{
HeapValPtr = Allocate(Length);
CharTraits::copy(pszStr, pszStr + Length, HeapValPtr);
HeapValPtr[Count] = ;
}
}
} void AddChar(CharType InValue)
{
AddItem(InValue); if (IsDataOnStack())
{
StackVal[Count] = ;
}
else
{
HeapValPtr[Count] = ;
}
} void AddStr(const CharType* pszStart, const CharType* pszEnd = NULL)
{
const SIZE_T Length = pszEnd ? pszEnd - pszStart : CharTraits::length(pszStart); if (IsDataOnStack())
{
if (Count + Length <= DEFAULT_LENGTH)
{
CharTraits::copy(StackVal+Count, pszStart, Length);
Count += Length; StackVal[Count] = ;
}
else
{
assert(!HeapValPtr); AllocatedCount = static_cast<SIZE_T>((Count + Length)*1.5f);
HeapValPtr = Allocate(AllocatedCount);
assert(HeapValPtr); if (Count > )
{
CharTraits::copy(HeapValPtr, StackVal, Count);
} CharTraits::copy(HeapValPtr+Count, pszStart, Length); Count += Length; HeapValPtr[Count] = ;
}
}
else
{
if (Count + Length <= AllocatedCount)
{
CharTraits::copy(HeapValPtr+Count, pszStart, Length);
Count += Length; HeapValPtr[Count] = ;
}
else
{
SIZE_T NewCount = static_cast<SIZE_T>((Count + Length)*1.5f); CharType* DataPtr = Allocate(NewCount); if (Count > )
{
CharTraits::copy(DataPtr, HeapValPtr, Count);
} ReleaseHeapData(); CharTraits::copy(DataPtr, pszStart, Length); Count += Length; AllocatedCount = NewCount;
HeapValPtr = DataPtr; HeapValPtr[Count] = ;
}
}
} const TCharType* CStr() const
{
return GetDataPtr();
} // is is a internal function
//
void InjectAdd(SIZE_T InCount)
{
Count += InCount; assert(IsDataOnStack() ? (Count <= DEFAULT_LENGTH) : (Count < AllocatedCount));
} protected:
void AddItem(const TCharType& InValue)
{
Super::AddItem(InValue);
}
};

上面展示的是基于栈内存容器实现的栈字符串,在大多数情况下,我们格式化字符串时都采用栈字符串来保存结果,这样可以显著的提升性能。

同时栈容器和栈字符串,都特别适合于当临时容器和临时字符串,因为多数情况下它们都优化掉了可能需要动态内存分配的操作。所以它们的使用并不局限于这一个小地方。

4. 基于C++ 11的优化

除了引入了C++ 11的容器unordered_map之外,还引入了右值引用等新内容,在某些情况下,可以带来一定的性能提升。

 #if FL_PLATFORM_HAS_RIGHT_VALUE_REFERENCE
TAutoArray( SelfType && Other ) :
Count(Other.Count),
AllocatedCount(Other.AllocatedCount),
HeapValPtr(Other.HeapValPtr)
{
if (Count > && Other.IsDataOnStack())
{
Algorithm::MoveArray(Other.StackVal, Other.StackVal + Count, StackVal);
} Other.Count = ;
Other.AllocatedCount = ;
Other.HeapValPtr = NULL;
} SelfType& operator = (SelfType&& Other )
{
return TakeFrom(Other);
}
#endif

上面展示的是基于右值引用的优化。

除此之外还是用了线程局部存储(TLS),这依赖于编译器是否支持。前面提到了我们采用Hash容器来存储Pattern缓存,然而在单线程的时候自然无需多余考虑,当需要支持多线程时,则全局唯一的Hash容器的访问都需要加锁,而加锁是有性能开销的。幸好C++ 11带来了内置的TLS支持,其结果就是每个线程会独立保存一份这样的Pattern缓存,因此无需对其访问加锁,这样无疑效率会更高。缺陷则是会损失部分内存。所有的这些都可以通过预先的宏定义来进行开关,使用者可以自行决定使用TLS还是Lock,或者不支持多线程。

         template < typename TPolicy >
class TGlobalPatternStorage :
public TPatternStorage<TPolicy>
{
public:
static TGlobalPatternStorage* GetStorage()
{
#if FL_WITH_THREAD_LOCAL
struct ManagedStorage
{
typedef Utility::TScopedLocker<System::CriticalSection> LockerType; System::CriticalSection ManagedCS;
Utility::TAutoArray<TGlobalPatternStorage*> Storages; ~ManagedStorage()
{
LockerType Locker(ManagedCS); for( SIZE_T i=; i<Storages.GetLength(); ++i )
{
delete Storages[i];
}
} void AddStorage( TGlobalPatternStorage* Storage )
{
assert(Storage); LockerType Locker(ManagedCS); Storages.AddItem(Storage);
}
}; static ManagedStorage StaticManager; static FL_THREAD_LOCAL TGlobalPatternStorage* StaticStorage = NULL; if( !StaticStorage )
{
StaticStorage = new TGlobalPatternStorage(); StaticManager.AddStorage(StaticStorage);
} return StaticStorage;
#else
static TGlobalPatternStorage StaticStorage;
return &StaticStorage;
#endif
}
};

如上所示为项目中使用TLS的代码。

总结

在将这一系列的优化结合起来之后,可以使得FL的整体效率处于较高水平,不低于C库函数,同时还具备其它格式化库不具备的功能,对于代码安全性等各方面的增强,都有帮助。下面是Test.cpp的测试结果,FL代表的是使用FL库的耗时,CL代表的C库的耗时,同时此测试模拟了多线程环境。

Windows Visual Studio 2013 Release下的输出:

0x64
Test20, -10.0050, X , X
0x64
Test20, -10.0050, X , X
1920 FLElapse:0.0762746
1920 CLElapse:0.269722
1636 FLElapse:0.0756153
7732 FLElapse:0.0766446
7956 FLElapse:0.0762051
7956 CLElapse:0.285714
1636 CLElapse:0.288648
7732 CLElapse:0.289193

Mac Xcode Release:

99
Test20, -10.0050, X , X
18446744073709551615 FLElapse:0.0901681
18446744073709551615 CLElapse:0.19329
18446744073709551615 FLElapse:0.147378
18446744073709551615 FLElapse:0.150375
18446744073709551615 FLElapse:0.153342
18446744073709551615 CLElapse:0.303508
18446744073709551615 CLElapse:0.308418
18446744073709551615 CLElapse:0.307407

这并非完全的测试,更多的测试需要在实际使用过程中来验证。

CPPFormatLibary提升效率的优化原理的更多相关文章

  1. paip.提升效率--数据绑定到table原理和流程Angular js jquery实现

    paip.提升效率--数据绑定到table原理和流程Angular js  jquery实现 html #--keyword 1 #---原理和流程 1 #----jq实现的代码 1 #-----An ...

  2. MySQL Optimization 优化原理

    MySQL Optimization 优化原理 MySQL逻辑架构 如果能在头脑中构建一幅MySQL各组件之间如何协同工作的架构图,有助于深入理解MySQL服务器.下图展示了MySQL的逻辑架构图. ...

  3. MySQL架构及优化原理

    1 MySQL架构原理 1.1 MySQL架构原理参看下述链接: https://blog.csdn.net/hguisu/article/details/7106342 1.2 MySQL优化详解参 ...

  4. atitit groovy 总结java 提升效率

    atitit groovy 总结java 提升效率 #---环境配置 1 #------安装麻烦的 2 三.创建groovy项目 2 3.  添加 Groovy 类 2 4.  编译运行groovy类 ...

  5. paip.提升效率--僵尸代码的迷思

    paip.提升效率--僵尸代码的迷思 僵尸代码是指你的代码库里被注释掉的那部分代码, 很少去使用它,就像僵尸一样, 看雷kill-the-zombies-in-your-code ========== ...

  6. mysql数据库的优化和查询效率的优化

    一.数据库的优化 1.优化索引.SQL 语句.分析慢查询: 2.设计表的时候严格根据数据库的设计范式来设计数据库: 3.使用缓存,把经常访问到的数据而且不需要经常变化的数据放在缓存中,能节约磁盘IO: ...

  7. 纯前端表格控件SpreadJS以专注业务、提升效率赢得用户与市场

    提起华为2012实验室,你可能有点陌生. 但你一定还对前段时间华为的那封<海思总裁致员工的一封信>记忆犹新,就在那篇饱含深情的信中,我们知道了华为为确保公司大部分产品的战略安全和连续供应, ...

  8. deeplearning算法优化原理

    deeplearning算法优化原理目录· 量化原理介绍 · 剪裁原理介绍 · 蒸馏原理介绍 · 轻量级模型结构搜索原理介绍 1. Quantization Aware Training量化介绍1.1 ...

  9. sql语句优化原理

    前言 网上有很多关于sql语句优化的文章,我这里想说下为什么这样...写sql语句,能够提高查询的效率. 1 sql语句优化原理 要想写出好的sql,就要学会用数据库的方式来思考如何执行sql,那么什 ...

随机推荐

  1. phpexcel 字符串转码

    问题状况:在导入excel的时候会出现 PHPExcel_RichText Object ( [_richTextElements:PHPExcel_RichText:] => PHPExcel ...

  2. gevent

    gevent是一个基于协程的python网络库. 特性: 1.基于libev的事件循环 2.基于greenlet 轻量级的执行单元  (what is greenlet ?) 3.来自python标准 ...

  3. 批量解密SQLSERVER数据库中的各种对象的工具dbForge SQL Decryptor

    批量解密SQLSERVER数据库中的各种对象的工具dbForge SQL Decryptor2.1.11 之前写过一篇文章,使用redgate公司的SQL PROMPT工具,但是不太方便 SQLPRO ...

  4. Quartz 学习记录1

    原因 公司有一些批量定时任务可能需要在夜间执行,用的是quartz和spring batch两个框架.quartz是个定时任务框架,spring batch是个批处理框架. 虽然我自己的小玩意儿平时不 ...

  5. Bubble Cup 8 finals I. Robots protection (575I)

    题意: 有一个正方形区域, 要求支持两个操作: 1.放置三角形,给定放置方向(有4个方向,直角边与坐标轴平行),直角顶点坐标,边长 2.查询一个点被覆盖了多少次 1<=正方形区域边长n<= ...

  6. vtkQuadratic创建半球面

    用的关键类:vtkQuadric.vtkSampleFunction.vtkContourFilter:用于创建方框的类vtkOutlineFilter #ifndef INITIAL_OPENGL ...

  7. 一个ListView怎么展示两种样式

    private class MyBaseMsgAdapter extends BaseAdapter { //获取数据适配器中条目类型的总数,修改成两种(纯文本,输入+文字) @Override pu ...

  8. REDHAT一总复习1 ssh配置 禁用root用户SSH连接

    生成SSH公钥 $ ssh-keygen 生成的公钥安装到指定的服务器上,这里安装到desktop0上的student账户 $ ssh-copy-id desktop0 $ su - 禁用root用户 ...

  9. AngularJS2.0 教程系列(一)

    http://my.oschina.net/u/2275217/blog/482178

  10. 解决 release-stripped.ap_' specified for property 'resourceFile' does not exist.

    设置buildTypes里的release的shrinkResources为false即可,如果是 release-stripped.ap_' specified for property 'reso ...