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 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
String DialogueASS::ToText(int version) const
{
// Prepare
wxString final = _T("");
// Old, slow code
if (false) {
// Prepare
wxString final;
// Write comment or dialogue
if (isComment) final = _T("Comment: ");
else final = _T("Dialogue: ");
// Write comment or dialogue
if (isComment) final = _T("Comment: ");
else final = _T("Dialogue: ");
// Write layer or marked
if (version >= 1) final += wxString::Format(_T("%01i,"),layer);
else final += _T("Marked=0,");
// Write layer or marked
if (version >= 1) final += wxString::Format(_T("%01i,"),layer);
else final += _T("Marked=0,");
// Write times, style and actor
final += start.GetString(2,1) + _T(",") + end.GetString(2,1) + _T(",") + style + _T(",") + actor + _T(",");
// Write times, style and actor
final += start.GetString(2,1) + _T(",") + end.GetString(2,1) + _T(",") + style + _T(",") + actor + _T(",");
// Write margins
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]);
// Write margins
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]);
// Write effect and text
final += effect + _T(",") + text;
// Write effect and 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
trim = _trim;
threaded = prefetch;
threaded = prefetch && false;
thread = NULL;
// Set encoding
@ -68,6 +68,8 @@ TextFileReader::TextFileReader(wxInputStream &stream,Gorgonsub::String enc,bool
// Destructor
TextFileReader::~TextFileReader()
{
wxCriticalSectionLocker locker(mutex);
if (thread) thread->Delete();
}
@ -182,6 +184,7 @@ Gorgonsub::String TextFileReader::ActuallyReadLine()
// Checks if there's more to read
bool TextFileReader::HasMoreLines()
{
if (cache.size()) return true;
wxCriticalSectionLocker locker(mutex);
return (!file.Eof());
}
@ -217,12 +220,13 @@ String TextFileReader::ReadLineFromFile()
// Load into cache if needed
String final;
if (cache.size() == 0) {
{
wxCriticalSectionLocker locker(mutex);
cache.push_back(ActuallyReadLine());
if (cache.size() == 0) {
cache.push_back(ActuallyReadLine());
}
}
bool startThread = false;
{
// Retrieve from cache
wxCriticalSectionLocker locker(mutex);
@ -234,16 +238,11 @@ String TextFileReader::ReadLineFromFile()
// Start the thread to prefetch more
if (cache.size() < 3 && thread == NULL) {
thread = new PrefetchThread(this);
startThread = true;
thread->Create();
thread->Run();
}
}
// Starts the thread
if (startThread) {
thread->Create();
thread->Run();
}
return final;
}
@ -253,21 +252,27 @@ String TextFileReader::ReadLineFromFile()
wxThread::ExitCode PrefetchThread::Entry()
{
// Lock
wxCriticalSectionLocker locker(parent->mutex);
while (parent->cache.size() < 3 && !parent->file.Eof()) {
// So I need to do this for whatever reason
bool run = true;
while (run) {
if (TestDestroy()) {
parent->thread = NULL;
return 0;
}
// Get line
parent->cache.push_back(parent->ActuallyReadLine());
{
wxCriticalSectionLocker locker(parent->mutex);
if (parent->cache.size() < 6) {
if (!parent->file.Eof()) {
// Get line
parent->cache.push_back(parent->ActuallyReadLine());
}
else run = false;
}
}
Sleep(50);
}
// Die
//wxCriticalSectionLocker locker(parent->mutex);
wxCriticalSectionLocker locker(parent->mutex);
parent->thread = NULL;
Delete();
return 0;
}

View file

@ -64,7 +64,7 @@ namespace Gorgonsub {
String ActuallyReadLine();
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();
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 == 0) _ms = 0;
// Generate mask string
wxString mask = wxString::Format(_T("%%0%ii:%%0%ii:%%0%ii.%%0%ii"),h_precision,2,2,ms_precision);
// Old code
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
wxString final = wxString::Format(mask,h,min,s,_ms);
// Generate final string
wxString final = wxString::Format(mask,h,min,s,_ms);
// Done
return final;
// Done
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) {
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_writer.h"
int main () {
int main() {
using namespace std;
using namespace Gorgonsub;
@ -111,4 +112,6 @@ int main () {
catch (std::exception &e) {
cout << "\n\nException: " << e.what() << endl << endl;
}
return true;
}

View file

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