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,31 +140,92 @@ bool DialogueASS::Parse(wxString rawData, int version)
// Serialize // Serialize
String DialogueASS::ToText(int version) const String DialogueASS::ToText(int version) const
{ {
// Prepare // Old, slow code
wxString final = _T(""); if (false) {
// Prepare
wxString final;
// Write comment or dialogue // Write comment or dialogue
if (isComment) final = _T("Comment: "); if (isComment) final = _T("Comment: ");
else final = _T("Dialogue: "); else final = _T("Dialogue: ");
// Write layer or marked // Write layer or marked
if (version >= 1) final += wxString::Format(_T("%01i,"),layer); if (version >= 1) final += wxString::Format(_T("%01i,"),layer);
else final += _T("Marked=0,"); else final += _T("Marked=0,");
// Write times, style and actor // Write times, style and actor
final += start.GetString(2,1) + _T(",") + end.GetString(2,1) + _T(",") + style + _T(",") + actor + _T(","); final += start.GetString(2,1) + _T(",") + end.GetString(2,1) + _T(",") + style + _T(",") + actor + _T(",");
// Write margins // Write margins
if (version <= 1) final += wxString::Format(_T("%04i,%04i,%04i,"),margin[0],margin[1],margin[2]); if (version <= 1) final += wxString::Format(_T("%04i,%04i,%04i,"),margin[0],margin[1],margin[2]);
else final += wxString::Format(_T("%04i,%04i,%04i,%04i,"),margin[0],margin[1],margin[2],margin[3]); else final += wxString::Format(_T("%04i,%04i,%04i,%04i,"),margin[0],margin[1],margin[2],margin[3]);
// Write effect and text // Write effect and text
final += effect + _T(",") + text; final += effect + _T(",") + text;
// Make sure that final has no line breaks // Return final
final.Replace(_T("\n"),_T("")); return final;
final.Replace(_T("\r"),_T("")); }
// Return final // New, faster code
return final; 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);
cache.push_back(ActuallyReadLine()); if (cache.size() == 0) {
cache.push_back(ActuallyReadLine());
}
} }
bool startThread = false;
{ {
// Retrieve from cache // Retrieve from cache
wxCriticalSectionLocker locker(mutex); wxCriticalSectionLocker locker(mutex);
@ -234,16 +238,11 @@ 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; thread->Create();
thread->Run();
} }
} }
// Starts the thread
if (startThread) {
thread->Create();
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;
} }
{
// Get line wxCriticalSectionLocker locker(parent->mutex);
parent->cache.push_back(parent->ActuallyReadLine()); if (parent->cache.size() < 6) {
if (!parent->file.Eof()) {
// Get line
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,14 +67,43 @@ 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;
// Generate mask string // Old code
wxString mask = wxString::Format(_T("%%0%ii:%%0%ii:%%0%ii.%%0%ii"),h_precision,2,2,ms_precision); if (false) {
// Generate mask string
wxString mask = wxString::Format(_T("%%0%ii:%%0%ii:%%0%ii.%%0%ii"),h_precision,2,2,ms_precision);
// Generate final string // Generate final string
wxString final = wxString::Format(mask,h,min,s,_ms); wxString final = wxString::Format(mask,h,min,s,_ms);
// Done // Done
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;
}
} }

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,7 +40,8 @@
#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"