Increased gorgonsub's ASS write speed with large files 3.5x by getting rid of wxString conversion and concatenation routines and writing my own.

Originally committed to SVN as r2059.
This commit is contained in:
Rodrigo Braz Monteiro 2008-03-15 00:29:17 +00:00
parent 2cbf0e587d
commit 98d5794f20
8 changed files with 188 additions and 48 deletions

View file

@ -80,4 +80,16 @@ namespace Gorgonsub {
String IntegerToString(int value); String IntegerToString(int value);
String PrettySize(int bytes); String PrettySize(int bytes);
// Fast string write
inline void WriteText(wxChar *&dst,const wxChar *src,size_t len,size_t &pos) {
memcpy(dst,src,len*sizeof(wxChar));
dst += len;
pos += len;
}
inline void WriteChar(wxChar *&dst,const wxChar &src,size_t &pos) {
*dst = src;
dst++;
pos++;
}
void WriteNumber(wxChar *&dst,wxChar *temp,int number,int pad,size_t &pos);
}; };

View file

@ -140,8 +140,10 @@ bool DialogueASS::Parse(wxString rawData, int version)
// Serialize // Serialize
String DialogueASS::ToText(int version) const String DialogueASS::ToText(int version) const
{ {
// Old, slow code
if (false) {
// Prepare // Prepare
wxString final = _T(""); wxString final;
// Write comment or dialogue // Write comment or dialogue
if (isComment) final = _T("Comment: "); if (isComment) final = _T("Comment: ");
@ -161,10 +163,69 @@ String DialogueASS::ToText(int version) const
// Write effect and text // Write effect and text
final += effect + _T(",") + text; final += effect + _T(",") + text;
// Make sure that final has no line breaks
final.Replace(_T("\n"),_T(""));
final.Replace(_T("\r"),_T(""));
// Return final // Return final
return final; return final;
} }
// New, faster code
else {
// Calculate size
size_t size = 9+9+20+12+12; // 9 for "comment: " (+1 for dialogue below), 9 for commas,
// 20 for times, 12 for margins, 12 just to be sure that layer fits
if (!isComment) size++; // Comment->Dialogue
if (version == 0) size += 8; // "Marked=0"
else if (version == 2) size += 5; // Fourth margin
size += style.Length() + actor.Length() + effect.Length() + text.Length();
// Allocate string
wxString final;
wxChar *buffer = final.GetWriteBuf(size);
wxChar temp[16];
// Write comment/dialogue
size_t pos = 0;
if (isComment) WriteText(buffer,_T("Comment: "),9,pos);
else WriteText(buffer,_T("Dialogue: "),10,pos);
// Write layer or marked
if (version >= 1) {
WriteNumber(buffer,temp,layer,0,pos);
WriteChar(buffer,_T(','),pos);
}
else WriteText(buffer,_T("Marked=0,"),9,pos);
// Write times
wxString tempStr = start.GetString(2,1);
WriteText(buffer,&tempStr[0],10,pos);
WriteChar(buffer,_T(','),pos);
tempStr = end.GetString(2,1);
WriteText(buffer,&tempStr[0],10,pos);
WriteChar(buffer,_T(','),pos);
// Write style and actor
WriteText(buffer,&style[0],style.Length(),pos);
WriteChar(buffer,_T(','),pos);
WriteText(buffer,&actor[0],actor.Length(),pos);
WriteChar(buffer,_T(','),pos);
// Write margins
size_t marCount = 3;
if (version == 2) marCount++;
for (size_t i=0;i<marCount;i++) {
WriteNumber(buffer,temp,margin[i],4,pos);
WriteChar(buffer,_T(','),pos);
}
// Write effect and text
WriteText(buffer,&effect[0],effect.Length(),pos);
WriteChar(buffer,_T(','),pos);
WriteText(buffer,&text[0],text.Length(),pos);
// Write terminator
WriteText(buffer,_T("\0"),1,pos);
// Restore string's state
final.UngetWriteBuf(pos-1);
return final;
}
}

View file

@ -54,7 +54,7 @@ TextFileReader::TextFileReader(wxInputStream &stream,Gorgonsub::String enc,bool
{ {
// Setup // Setup
trim = _trim; trim = _trim;
threaded = prefetch; threaded = prefetch && false;
thread = NULL; thread = NULL;
// Set encoding // Set encoding
@ -68,6 +68,8 @@ TextFileReader::TextFileReader(wxInputStream &stream,Gorgonsub::String enc,bool
// Destructor // Destructor
TextFileReader::~TextFileReader() TextFileReader::~TextFileReader()
{ {
wxCriticalSectionLocker locker(mutex);
if (thread) thread->Delete();
} }
@ -182,6 +184,7 @@ Gorgonsub::String TextFileReader::ActuallyReadLine()
// Checks if there's more to read // Checks if there's more to read
bool TextFileReader::HasMoreLines() bool TextFileReader::HasMoreLines()
{ {
if (cache.size()) return true;
wxCriticalSectionLocker locker(mutex); wxCriticalSectionLocker locker(mutex);
return (!file.Eof()); return (!file.Eof());
} }
@ -217,12 +220,13 @@ String TextFileReader::ReadLineFromFile()
// Load into cache if needed // Load into cache if needed
String final; String final;
if (cache.size() == 0) { {
wxCriticalSectionLocker locker(mutex); wxCriticalSectionLocker locker(mutex);
if (cache.size() == 0) {
cache.push_back(ActuallyReadLine()); cache.push_back(ActuallyReadLine());
} }
}
bool startThread = false;
{ {
// Retrieve from cache // Retrieve from cache
wxCriticalSectionLocker locker(mutex); wxCriticalSectionLocker locker(mutex);
@ -234,15 +238,10 @@ String TextFileReader::ReadLineFromFile()
// Start the thread to prefetch more // Start the thread to prefetch more
if (cache.size() < 3 && thread == NULL) { if (cache.size() < 3 && thread == NULL) {
thread = new PrefetchThread(this); thread = new PrefetchThread(this);
startThread = true;
}
}
// Starts the thread
if (startThread) {
thread->Create(); thread->Create();
thread->Run(); thread->Run();
} }
}
return final; return final;
} }
@ -253,21 +252,27 @@ String TextFileReader::ReadLineFromFile()
wxThread::ExitCode PrefetchThread::Entry() wxThread::ExitCode PrefetchThread::Entry()
{ {
// Lock // Lock
wxCriticalSectionLocker locker(parent->mutex); bool run = true;
while (parent->cache.size() < 3 && !parent->file.Eof()) { while (run) {
// So I need to do this for whatever reason
if (TestDestroy()) { if (TestDestroy()) {
parent->thread = NULL; parent->thread = NULL;
return 0; return 0;
} }
{
wxCriticalSectionLocker locker(parent->mutex);
if (parent->cache.size() < 6) {
if (!parent->file.Eof()) {
// Get line // Get line
parent->cache.push_back(parent->ActuallyReadLine()); parent->cache.push_back(parent->ActuallyReadLine());
} }
else run = false;
}
}
Sleep(50);
}
// Die // Die
//wxCriticalSectionLocker locker(parent->mutex); wxCriticalSectionLocker locker(parent->mutex);
parent->thread = NULL; parent->thread = NULL;
Delete();
return 0; return 0;
} }

View file

@ -64,7 +64,7 @@ namespace Gorgonsub {
String ActuallyReadLine(); String ActuallyReadLine();
public: public:
TextFileReader(wxInputStream &stream,String encoding=_T(""),bool trim=true,bool prefetch=false); TextFileReader(wxInputStream &stream,String encoding=_T(""),bool trim=true,bool prefetch=true);
~TextFileReader(); ~TextFileReader();
String ReadLineFromFile(); String ReadLineFromFile();

View file

@ -67,6 +67,8 @@ String Time::GetString(int ms_precision,int h_precision) const
else if (ms_precision == 1) _ms /= 100; else if (ms_precision == 1) _ms /= 100;
else if (ms_precision == 0) _ms = 0; else if (ms_precision == 0) _ms = 0;
// Old code
if (false) {
// Generate mask string // Generate mask string
wxString mask = wxString::Format(_T("%%0%ii:%%0%ii:%%0%ii.%%0%ii"),h_precision,2,2,ms_precision); wxString mask = wxString::Format(_T("%%0%ii:%%0%ii:%%0%ii.%%0%ii"),h_precision,2,2,ms_precision);
@ -77,6 +79,33 @@ String Time::GetString(int ms_precision,int h_precision) const
return final; return final;
} }
// New code
else {
// Get write buffer
wxString final;
size_t size = 7+h_precision+ms_precision;
size_t pos = 0;
wxChar *buffer = final.GetWriteBuf(size);
wxChar temp[16];
// Write time
WriteNumber(buffer,temp,h,h_precision,pos);
WriteChar(buffer,_T(':'),pos);
WriteNumber(buffer,temp,min,2,pos);
WriteChar(buffer,_T(':'),pos);
WriteNumber(buffer,temp,s,2,pos);
WriteChar(buffer,_T('.'),pos);
WriteNumber(buffer,temp,_ms,ms_precision,pos);
// Write terminator
WriteText(buffer,_T("\0"),1,pos);
// Restore string's state and return
final.UngetWriteBuf(pos-1);
return final;
}
}
/////////////////// ///////////////////
// Parses a string // Parses a string

View file

@ -82,3 +82,33 @@ String Gorgonsub::FloatToString(double value) {
String Gorgonsub::IntegerToString(int value) { String Gorgonsub::IntegerToString(int value) {
return wxString::Format(_T("%i"),value); return wxString::Format(_T("%i"),value);
} }
////////////////////////////
// Fast writing to a string
void Gorgonsub::WriteNumber(wxChar *&dst,wxChar *temp,int number,int pad,size_t &pos) {
// Write number backwards first
int div, value;
size_t len;
for (len=0;true;len++) {
div = number / 10;
value = number - (div*10);
temp[len] = (wxChar) (value + '0');
if (!div) break;
number = div;
}
len++;
// Pad with zeroes
pad -= (int)len;
for (int i=0;i<pad;i++) {
*dst++ = (wxChar) '0';
pos++;
}
// Write number
for (size_t i=0;i<len;i++) {
*dst++ = temp[len-i-1];
pos++;
}
}

View file

@ -40,6 +40,7 @@
#include "text_file_reader.h" #include "text_file_reader.h"
#include "text_file_writer.h" #include "text_file_writer.h"
int main() { int main() {
using namespace std; using namespace std;
using namespace Gorgonsub; using namespace Gorgonsub;
@ -111,4 +112,6 @@ int main () {
catch (std::exception &e) { catch (std::exception &e) {
cout << "\n\nException: " << e.what() << endl << endl; cout << "\n\nException: " << e.what() << endl << endl;
} }
return true;
} }

View file

@ -118,7 +118,7 @@
<Tool <Tool
Name="VCCLCompilerTool" Name="VCCLCompilerTool"
AdditionalIncludeDirectories="../include" AdditionalIncludeDirectories="../include"
PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE" PreprocessorDefinitions="WIN32;NDEBUG;_WINDOWS"
RuntimeLibrary="2" RuntimeLibrary="2"
UsePrecompiledHeader="0" UsePrecompiledHeader="0"
WarningLevel="4" WarningLevel="4"