Changed the FastFormatString functions into nifty little classes that use TLS for their buffer workspaces. Result: a fully concurrent printf with zero malloc/free overhead. Use the pxsFmt macro for them -- which is a fully working alternative to wxsFormat().

(pxsFmt has been applied to console/logging only for now.  Will apply it to more later, once the code is confirmed stable)

git-svn-id: http://pcsx2.googlecode.com/svn/trunk@3596 96395faa-99c1-11dd-bbfe-3dabce05a288
This commit is contained in:
Jake.Stine 2010-08-03 22:18:19 +00:00
parent c2e687ad27
commit e5f87e068b
7 changed files with 304 additions and 128 deletions

View File

@ -410,6 +410,8 @@ protected:
template< typename T, uint Alignment >
class SafeAlignedArray : public SafeArray<T>
{
typedef SafeArray<T> _parent;
protected:
T* _virtual_realloc( int newsize )
{
@ -423,6 +425,8 @@ protected:
// Maybe useful,maybe not... no harm in attaching it. :D
public:
using _parent::operator[];
virtual ~SafeAlignedArray()
{
safe_aligned_free( this->m_ptr );

View File

@ -155,17 +155,10 @@ public:
m_valid_range = 0xffffffff;
}
wxEXPLICIT ScopedArray( int size ) :
m_array( pxAssertDev( size >= 0, "Invalid negative size specified." ) ? new T[size] : NULL )
, m_valid_range( (uint)size )
{
}
// For breaking the 2gb barrier, lets provision this:
wxEXPLICIT ScopedArray( s64 size ) :
m_array( pxAssertDev( size >= 0 && (size < UINT_MAX), "Invalid negative size specified to ScopedArray." ) ? new T[size] : NULL )
, m_valid_range( (uint)size )
wxEXPLICIT ScopedArray( size_t size )
{
m_array = new T[size];
m_valid_range = size;
}
~ScopedArray() throw()

View File

@ -16,6 +16,7 @@
#pragma once
#include "Dependencies.h"
#include "SafeArray.h"
#include <wx/tokenzr.h>
@ -106,12 +107,66 @@ struct ParsedAssignmentString
ParsedAssignmentString( const wxString& src );
};
// --------------------------------------------------------------------------------------
// FastFormatAscii
// --------------------------------------------------------------------------------------
// Fast formatting of ASCII text. This class uses a process-wide format buffer that is
// allocated only once and grown to accommodate string formatting needs. The buffer is
// thread-safe. This technique reduces the overhead of formatting strings by eliminating
// most or all heap allocation actions.
//
class FastFormatAscii
{
protected:
SafeArray<char>* m_dest;
bool m_deleteDest;
public:
FastFormatAscii();
~FastFormatAscii() throw();
FastFormatAscii& Write( const char* fmt, ... );
FastFormatAscii& WriteV( const char* fmt, va_list argptr );
const char* GetResult() const;
operator const char*() const;
const wxString GetString() const;
operator wxString() const;
};
class FastFormatUnicode
{
protected:
SafeArray<char>* m_dest;
bool m_deleteDest;
public:
FastFormatUnicode();
~FastFormatUnicode() throw();
FastFormatUnicode& Write( const char* fmt, ... );
FastFormatUnicode& Write( const wxChar* fmt, ... );
FastFormatUnicode& WriteV( const char* fmt, va_list argptr );
FastFormatUnicode& WriteV( const wxChar* fmt, va_list argptr );
const wxChar* GetResult() const;
const wxString GetString() const;
operator const wxChar*() const
{
return (const wxChar*)m_dest->GetPtr();
}
operator wxString() const
{
return (const wxChar*)m_dest->GetPtr();
}
};
extern bool pxParseAssignmentString( const wxString& src, wxString& ldest, wxString& rdest );
extern int FastFormatString_AsciiRaw(wxCharBuffer& dest, const char* fmt, va_list argptr);
extern wxString FastFormatString_Ascii(const char* fmt, va_list argptr);
extern wxString FastFormatString_Unicode(const wxChar* fmt, va_list argptr);
#define pxsFmt FastFormatUnicode().Write
#define pxsFmtV FastFormatUnicode().WriteV
//////////////////////////////////////////////////////////////////////////////////////////
// Custom internal sprintf functions, which are ASCII only (even in UNICODE builds)

View File

@ -369,7 +369,7 @@ bool IConsoleWriter::Write( const char* fmt, ... ) const
va_list args;
va_start(args,fmt);
DoWrite( FastFormatString_Ascii(fmt, args) );
DoWrite( pxsFmtV(fmt, args) );
va_end(args);
return false;
@ -382,7 +382,7 @@ bool IConsoleWriter::Write( ConsoleColors color, const char* fmt, ... ) const
va_list args;
va_start(args,fmt);
ConsoleColorScope cs( color );
DoWrite( FastFormatString_Ascii(fmt, args) );
DoWrite( pxsFmtV(fmt, args) );
va_end(args);
return false;
@ -394,7 +394,7 @@ bool IConsoleWriter::WriteLn( const char* fmt, ... ) const
va_list args;
va_start(args,fmt);
DoWriteLn( _addIndentation( FastFormatString_Ascii(fmt, args), conlog_Indent ) );
DoWriteLn( _addIndentation( pxsFmtV(fmt, args), conlog_Indent ) );
va_end(args);
return false;
@ -406,7 +406,7 @@ bool IConsoleWriter::WriteLn( ConsoleColors color, const char* fmt, ... ) const
va_list args;
va_start(args,fmt);
ConsoleColorScope cs( color );
DoWriteLn( _addIndentation( FastFormatString_Ascii(fmt, args), conlog_Indent ) );
DoWriteLn( _addIndentation( pxsFmtV(fmt, args), conlog_Indent ) );
va_end(args);
return false;
@ -419,7 +419,7 @@ bool IConsoleWriter::Error( const char* fmt, ... ) const
va_list args;
va_start(args,fmt);
ConsoleColorScope cs( Color_StrongRed );
DoWriteLn( _addIndentation( FastFormatString_Ascii(fmt, args) ) );
DoWriteLn( _addIndentation( pxsFmtV(fmt, args) ) );
va_end(args);
return false;
@ -432,7 +432,7 @@ bool IConsoleWriter::Warning( const char* fmt, ... ) const
va_list args;
va_start(args,fmt);
ConsoleColorScope cs( Color_StrongOrange );
DoWriteLn( _addIndentation( FastFormatString_Ascii(fmt, args) ) );
DoWriteLn( _addIndentation( pxsFmtV(fmt, args) ) );
va_end(args);
return false;
@ -448,7 +448,7 @@ bool IConsoleWriter::Write( const wxChar* fmt, ... ) const
va_list args;
va_start(args,fmt);
DoWrite( FastFormatString_Unicode( fmt, args ) );
DoWrite( pxsFmtV( fmt, args ) );
va_end(args);
return false;
@ -461,7 +461,7 @@ bool IConsoleWriter::Write( ConsoleColors color, const wxChar* fmt, ... ) const
va_list args;
va_start(args,fmt);
ConsoleColorScope cs( color );
DoWrite( FastFormatString_Unicode( fmt, args ) );
DoWrite( pxsFmtV( fmt, args ) );
va_end(args);
return false;
@ -473,7 +473,7 @@ bool IConsoleWriter::WriteLn( const wxChar* fmt, ... ) const
va_list args;
va_start(args,fmt);
DoWriteLn( _addIndentation( FastFormatString_Unicode( fmt, args ), conlog_Indent ) );
DoWriteLn( _addIndentation( pxsFmtV( fmt, args ), conlog_Indent ) );
va_end(args);
return false;
@ -486,7 +486,7 @@ bool IConsoleWriter::WriteLn( ConsoleColors color, const wxChar* fmt, ... ) cons
va_list args;
va_start(args,fmt);
ConsoleColorScope cs( color );
DoWriteLn( _addIndentation( FastFormatString_Unicode( fmt, args ), conlog_Indent ) );
DoWriteLn( _addIndentation( pxsFmtV( fmt, args ), conlog_Indent ) );
va_end(args);
return false;
@ -499,7 +499,7 @@ bool IConsoleWriter::Error( const wxChar* fmt, ... ) const
va_list args;
va_start(args,fmt);
ConsoleColorScope cs( Color_StrongRed );
DoWriteLn( _addIndentation( FastFormatString_Unicode( fmt, args ) ) );
DoWriteLn( _addIndentation( pxsFmtV( fmt, args ) ) );
va_end(args);
return false;
@ -512,7 +512,7 @@ bool IConsoleWriter::Warning( const wxChar* fmt, ... ) const
va_list args;
va_start(args,fmt);
ConsoleColorScope cs( Color_StrongOrange );
DoWriteLn( _addIndentation( FastFormatString_Unicode( fmt, args ) ) );
DoWriteLn( _addIndentation( pxsFmtV( fmt, args ) ) );
va_end(args);
return false;
@ -528,7 +528,7 @@ bool IConsoleWriter::WriteFromStdout( const char* fmt, ... ) const
va_list args;
va_start(args,fmt);
DoWrite( FastFormatString_Ascii(fmt, args) );
DoWrite( pxsFmtV(fmt, args) );
va_end(args);
return false;
@ -541,7 +541,7 @@ bool IConsoleWriter::WriteFromStdout( ConsoleColors color, const char* fmt, ...
va_list args;
va_start(args,fmt);
ConsoleColorScope cs( color );
DoWriteFromStdout( FastFormatString_Ascii(fmt, args) );
DoWriteFromStdout( pxsFmtV(fmt, args) );
va_end(args);
return false;

View File

@ -23,51 +23,120 @@ using namespace Threading;
// system deadlock.
static const int MaxFormattedStringLength = 0x80000;
// --------------------------------------------------------------------------------------
// FormatBuffer
// --------------------------------------------------------------------------------------
// This helper class provides a "safe" way for us to check if the global handles for the
// string format buffer have been initialized or destructed by the C++ global heap manager.
// (C++ has no way to enforce init/destruct order on complex globals, thus the convoluted
// use of booleans to check the status of the type initializers).
//
template< typename CharType >
class FormatBuffer : public Mutex
{
public:
bool& clearbit;
SafeArray<CharType> buffer;
wxMBConvUTF8 ConvUTF8;
#include "TlsVariable.inl"
FormatBuffer( bool& bit_to_clear_on_destruction )
: clearbit( bit_to_clear_on_destruction )
, buffer( 4096, wxsFormat( L"%s Format Buffer", (sizeof(CharType)==1) ? "Ascii" : "Unicode" ) )
// --------------------------------------------------------------------------------------
// FastFormatBuffers
// --------------------------------------------------------------------------------------
template< typename CharType >
class FastFormatBuffers
{
DeclareNoncopyableObject(FastFormatBuffers);
protected:
typedef SafeAlignedArray<CharType,16> BufferType;
static const uint BufferCount = 3;
BufferType m_buffers[BufferCount];
uint m_curslot;
public:
FastFormatBuffers()
{
bit_to_clear_on_destruction = false;
// This protects against potential recursive calls to our formatter, by forcing those
// calls to use a dynamic buffer for formatting.
m_curslot = BufferCount;
for (uint i=0; i<BufferCount; ++i)
{
//m_buffers[i].Name = wxsFormat(L"Ascii Formatting Buffer (slot%d)", i);
m_buffers[i].Name = wxsFormat(L"%s Formatting Buffer (slot%d)",
(sizeof(CharType)==1) ? L"Ascii" : L"Unicode", i);
m_buffers[i].MakeRoomFor(1024);
m_buffers[i].ChunkSize = 4096;
}
virtual ~FormatBuffer() throw()
m_curslot = 0;
}
virtual ~FastFormatBuffers() throw()
{
clearbit = true;
Wait(); // lock the mutex, just in case.
pxAssumeDev(m_curslot==0, "Dangling Ascii formatting buffer detected!");
}
bool HasFreeBuffer() const
{
return m_curslot < BufferCount;
}
BufferType& GrabBuffer()
{
++m_curslot;
pxAssume(m_curslot<BufferCount);
return m_buffers[m_curslot];
}
void ReleaseBuffer()
{
--m_curslot;
pxAssume(m_curslot<BufferCount);
}
BufferType& operator[](uint i)
{
pxAssume(i<BufferCount);
return m_buffers[i];
}
};
// Assume the buffer is 'deleted' -- technically it's never existed on startup,
// but "deleted" is well enough.
// --------------------------------------------------------------------------------------
// GlobalBufferManager
// --------------------------------------------------------------------------------------
// This local-scope class is needed in order to safely deal with C++ initializing and destroying
// global objects in arbitrary order. The initbit is updated by the object when constructed and
// destroyed; code using this class provides its own statically-initialized boolean (which MUST
// default to false!) and then sets the boolean to true to indicate the object is ready for use.
//
template< typename T >
class GlobalBufferManager
{
public:
bool& initbit;
T instance;
static bool ascii_buffer_is_deleted = true;
static bool unicode_buffer_is_deleted = true;
GlobalBufferManager( bool& globalBoolean )
: initbit( globalBoolean )
{
initbit = true;
}
static FormatBuffer<char> ascii_buffer( ascii_buffer_is_deleted );
static FormatBuffer<wxChar> unicode_buffer( unicode_buffer_is_deleted );
~GlobalBufferManager() throw()
{
initbit = false;
instance.Dispose();
}
static void format_that_ascii_mess( SafeArray<char>& buffer, const char* fmt, va_list argptr )
T& Get()
{
return instance;
}
operator T&()
{
return instance;
}
};
static bool buffer_is_avail = false;
static GlobalBufferManager< BaseTlsVariable< FastFormatBuffers< char > > > m_buffer_tls(buffer_is_avail);
static __releaseinline void format_that_ascii_mess( SafeArray<char>& buffer, uint writepos, const char* fmt, va_list argptr )
{
while( true )
{
int size = buffer.GetLength();
int len = vsnprintf(buffer.GetPtr(), size, fmt, argptr);
int len = vsnprintf(buffer.GetPtr(writepos), size-writepos, fmt, argptr);
// some implementations of vsnprintf() don't NUL terminate
// the string if there is not enough space for it so
@ -80,28 +149,29 @@ static void format_that_ascii_mess( SafeArray<char>& buffer, const char* fmt, va
// total number of characters which would have been written if the
// buffer were large enough (newer standards such as Unix98)
if ( len < 0 )
if (len < 0)
len = size + (size/4);
if ( len < size ) break;
buffer.ExactAlloc( len + 1 );
len += writepos;
if (len < size) break;
buffer.ExactAlloc( len + 31 );
};
// performing an assertion or log of a truncated string is unsafe, so let's not; even
// though it'd be kinda nice if we did.
}
static void format_that_unicode_mess( SafeArray<wxChar>& buffer, const wxChar* fmt, va_list argptr)
static __releaseinline void format_that_unicode_mess( SafeArray<char>& buffer, uint writepos, const wxChar* fmt, va_list argptr)
{
while( true )
{
int size = buffer.GetLength();
int len = wxVsnprintf(buffer.GetPtr(), size, fmt, argptr);
int size = buffer.GetLength()/2;
int len = wxVsnprintf((wxChar*)buffer.GetPtr(writepos), size-writepos, fmt, argptr);
// some implementations of vsnprintf() don't NUL terminate
// the string if there is not enough space for it so
// always do it manually
buffer[size-1] = L'\0';
((wxChar*)buffer.GetPtr())[size-1] = L'\0';
if( size >= MaxFormattedStringLength ) break;
@ -112,80 +182,134 @@ static void format_that_unicode_mess( SafeArray<wxChar>& buffer, const wxChar* f
if ( len < 0 )
len = size + (size/4);
len += writepos;
if ( len < size ) break;
buffer.ExactAlloc( len + 1 );
buffer.ExactAlloc( (len + 31) * 2 );
};
// performing an assertion or log of a truncated string is unsafe, so let's not; even
// though it'd be kinda nice if we did.
}
// returns the length of the string (not including the 0)
int FastFormatString_AsciiRaw(wxCharBuffer& dest, const char* fmt, va_list argptr)
SafeArray<char>* GetFormatBuffer( bool& deleteDest )
{
if( ascii_buffer_is_deleted )
deleteDest = false;
if (buffer_is_avail)
{
// This means that the program is shutting down and the C++ destructors are
// running, randomly deallocating static variables from existence. We handle it
// as gracefully as possible by allocating local vars to do our bidding (slow, but
// ultimately necessary!)
SafeArray<char> localbuf( 4096, L"Temporary Ascii Formatting Buffer" );
format_that_ascii_mess( localbuf, fmt, argptr );
dest = localbuf.GetPtr();
return strlen(dest);
}
else
{
// This is normal operation. The static buffers are available for use, and we use
// them for sake of efficiency (fewer heap allocs, for sure!)
ScopedLock locker( ascii_buffer );
format_that_ascii_mess( ascii_buffer.buffer, fmt, argptr );
dest = ascii_buffer.buffer.GetPtr();
return strlen(dest);
if (m_buffer_tls.Get()->HasFreeBuffer())
return &m_buffer_tls.Get()->GrabBuffer();
}
deleteDest = true;
return new SafeArray<char>(2048, L"Temporary string formatting buffer");
}
wxString FastFormatString_Ascii(const char* fmt, va_list argptr)
// --------------------------------------------------------------------------------------
// FastFormatUnicode (implementations)
// --------------------------------------------------------------------------------------
FastFormatUnicode::FastFormatUnicode()
{
if( ascii_buffer_is_deleted )
{
// This means that the program is shutting down and the C++ destructors are
// running, randomly deallocating static variables from existence. We handle it
// as gracefully as possible by allocating local vars to do our bidding (slow, but
// ultimately necessary!)
SafeArray<char> localbuf( 4096, L"Temporary Ascii Formatting Buffer" );
format_that_ascii_mess( localbuf, fmt, argptr );
return fromUTF8( localbuf.GetPtr() );
}
else
{
// This is normal operation. The static buffers are available for use, and we use
// them for sake of efficiency (fewer heap allocs, for sure!)
ScopedLock locker( ascii_buffer );
format_that_ascii_mess( ascii_buffer.buffer, fmt, argptr );
return fromUTF8( ascii_buffer.buffer.GetPtr() );
}
m_dest = GetFormatBuffer(m_deleteDest);
((wxChar*)m_dest->GetPtr())[0] = 0;
}
wxString FastFormatString_Unicode(const wxChar* fmt, va_list argptr)
FastFormatUnicode::~FastFormatUnicode() throw()
{
// See above for the explanation on the _is_deleted flags.
if( unicode_buffer_is_deleted )
{
SafeArray<wxChar> localbuf( 4096, L"Temporary Unicode Formatting Buffer" );
format_that_unicode_mess( localbuf, fmt, argptr );
return localbuf.GetPtr();
}
if (m_deleteDest)
delete m_dest;
else
{
ScopedLock locker( unicode_buffer );
format_that_unicode_mess( unicode_buffer.buffer, fmt, argptr );
return unicode_buffer.buffer.GetPtr();
}
m_buffer_tls.Get()->ReleaseBuffer();
}
FastFormatUnicode& FastFormatUnicode::WriteV( const char* fmt, va_list argptr )
{
wxString converted( fromUTF8(FastFormatAscii().WriteV( fmt, argptr )) );
uint inspos = wxStrlen((wxChar*)m_dest->GetPtr());
m_dest->MakeRoomFor((inspos + converted.Length() + 31)*2);
wxStrcpy( &((wxChar*)m_dest->GetPtr())[inspos], converted );
return *this;
}
FastFormatUnicode& FastFormatUnicode::WriteV( const wxChar* fmt, va_list argptr )
{
format_that_unicode_mess( *m_dest, wxStrlen((wxChar*)m_dest->GetPtr()), fmt, argptr );
return *this;
}
FastFormatUnicode& FastFormatUnicode::Write( const char* fmt, ... )
{
va_list list;
va_start(list, fmt);
WriteV(fmt,list);
va_end(list);
return *this;
}
FastFormatUnicode& FastFormatUnicode::Write( const wxChar* fmt, ... )
{
va_list list;
va_start(list, fmt);
WriteV(fmt,list);
va_end(list);
return *this;
}
const wxChar* FastFormatUnicode::GetResult() const
{
return (wxChar*)m_dest->GetPtr();
}
// --------------------------------------------------------------------------------------
// FastFormatAscii (implementations)
// --------------------------------------------------------------------------------------
FastFormatAscii::FastFormatAscii()
{
m_dest = GetFormatBuffer(m_deleteDest);
m_dest->GetPtr()[0] = 0;
}
FastFormatAscii::~FastFormatAscii() throw()
{
if (m_deleteDest)
delete m_dest;
else
m_buffer_tls.Get()->ReleaseBuffer();
}
const wxString FastFormatAscii::GetString() const
{
return fromAscii(m_dest->GetPtr());
}
FastFormatAscii::operator wxString() const
{
return fromAscii(m_dest->GetPtr());
}
FastFormatAscii& FastFormatAscii::WriteV( const char* fmt, va_list argptr )
{
format_that_ascii_mess( *m_dest, strlen(m_dest->GetPtr()), fmt, argptr );
return *this;
}
FastFormatAscii& FastFormatAscii::Write( const char* fmt, ... )
{
va_list list;
va_start(list, fmt);
WriteV(fmt,list);
va_end(list);
return *this;
}
const char* FastFormatAscii::GetResult() const
{
return m_dest->GetPtr();
}
FastFormatAscii::operator const char*() const
{
return m_dest->GetPtr();
}

View File

@ -148,7 +148,7 @@ pxWindowTextWriter& pxWindowTextWriter::FormatLn( const wxChar* fmt, ... )
{
va_list args;
va_start(args,fmt);
_DoWrite( FastFormatString_Unicode(fmt, args) );
_DoWrite( pxsFmtV(fmt, args) );
va_end(args);
return *this;
}

View File

@ -20,8 +20,8 @@ void AsciiFile::Printf( const char* fmt, ... )
{
va_list list;
va_start( list, fmt );
//std::string writeme; vssprintf( writeme, fmt, list );
wxCharBuffer result; int reslen = FastFormatString_AsciiRaw(result, fmt, list);
FastFormatAscii ascii;
ascii.WriteV(fmt,list);
va_end( list );
Write( result.data(), reslen );
Write( ascii, strlen(ascii) );
}