From 43374a25b92c74d6c566d1b53d47503a28680996 Mon Sep 17 00:00:00 2001
From: Giovanni Bussi <giovanni.bussi@gmail.com>
Date: Wed, 28 Aug 2013 19:28:16 +0200
Subject: [PATCH] Optimized OFile

Removed many scans of the output buffer by keeping trace of buffer length.
Time needed to write a long line with a sequence of file.printf() is
now linear with line length. Before it was quatradic, causing problems
especially when logging huge atom lists at startup.
---
 src/tools/OFile.cpp | 25 ++++++++++++++-----------
 src/tools/OFile.h   |  2 ++
 2 files changed, 16 insertions(+), 11 deletions(-)

diff --git a/src/tools/OFile.cpp b/src/tools/OFile.cpp
index 7820ee202..5ff9a73a1 100644
--- a/src/tools/OFile.cpp
+++ b/src/tools/OFile.cpp
@@ -53,6 +53,7 @@ OFile::OFile():
 {
   fmtField();
   buflen=1;
+  actual_buffer_length=0;
   buffer=new char[buflen];
 // these are set to zero to avoid valgrind errors
   for(unsigned i=0;i<buflen;++i) buffer[i]=0;
@@ -78,14 +79,13 @@ OFile& OFile::setLinePrefix(const std::string&l){
 }
 
 int OFile::printf(const char*fmt,...){
-  size_t pointer=strlen(buffer);
   va_list arg;
   va_start(arg, fmt);
-  int r=std::vsnprintf(&buffer[pointer],buflen-pointer,fmt,arg);
+  int r=std::vsnprintf(&buffer[actual_buffer_length],buflen-actual_buffer_length,fmt,arg);
   va_end(arg);
-  if(r>=buflen-pointer){
+  if(r>=buflen-actual_buffer_length){
     int newlen=buflen;
-    while(newlen<=r+pointer) newlen*=2;
+    while(newlen<=r+actual_buffer_length) newlen*=2;
     char* newbuf=new char [newlen];
     memmove(newbuf,buffer,buflen);
     for(int k=buflen;k<newlen;k++) newbuf[k]=0;
@@ -94,22 +94,25 @@ int OFile::printf(const char*fmt,...){
     buflen=newlen;
     va_list arg;
     va_start(arg, fmt);
-    r=std::vsnprintf(&buffer[pointer],buflen-pointer,fmt,arg);
+    r=std::vsnprintf(&buffer[actual_buffer_length],buflen-actual_buffer_length,fmt,arg);
     va_end(arg);
   }
-  plumed_massert(r>-1 && r<buflen-pointer,"error using fmt string " + std::string(fmt));
+  plumed_massert(r>-1 && r<buflen-actual_buffer_length,"error using fmt string " + std::string(fmt));
 
 // Line is buffered until newline, then written with a PLUMED: prefix
   char*p1=buffer;
   char*p2;
-  while((p2=strchr(p1,'\n'))){
-    *p2='\0';
+// newline is only searched in the just added portion:
+  char*psearch=p1+actual_buffer_length;
+  actual_buffer_length+=r;
+  while((p2=strchr(psearch,'\n'))){
     if(linePrefix.length()>0) llwrite(linePrefix.c_str(),linePrefix.length());
-    llwrite(p1,std::strlen(p1));
-    llwrite("\n",1);
+    llwrite(p1,p2-p1+1);
+    actual_buffer_length-=(p2-p1)+1;
     p1=p2+1;
+    psearch=p1;
   };
-  memmove(buffer,p1,strlen(p1)+1);
+  if(buffer!=p1) memmove(buffer,p1,actual_buffer_length);
   return r;
 }
 
diff --git a/src/tools/OFile.h b/src/tools/OFile.h
index 74732c167..fe08c9dce 100644
--- a/src/tools/OFile.h
+++ b/src/tools/OFile.h
@@ -92,6 +92,8 @@ public virtual FileBase{
   char* buffer;
 /// Internal buffer length
   int buflen;
+/// This variables stores the actual buffer length
+  unsigned actual_buffer_length;
 /// Class identifying a single field for fielded output
   class Field:
   public FieldBase{
-- 
GitLab