summaryrefslogtreecommitdiff
path: root/library/statistics.cpp
diff options
context:
space:
mode:
authorDaniel Wilhelm <daniel@wili.li>2014-04-18 16:57:45 +0200
committerDaniel Wilhelm <daniel@wili.li>2014-04-18 16:57:45 +0200
commit2a3ebac62eb6dd88122c0f447ea90ce368373d3a (patch)
treefae5c18deaecfb6f39d4d66dd3de8ce730b2025b /library/statistics.cpp
parent1.17 (diff)
downloadFreeFileSync-2a3ebac62eb6dd88122c0f447ea90ce368373d3a.tar.gz
FreeFileSync-2a3ebac62eb6dd88122c0f447ea90ce368373d3a.tar.bz2
FreeFileSync-2a3ebac62eb6dd88122c0f447ea90ce368373d3a.zip
1.18
Diffstat (limited to 'library/statistics.cpp')
-rw-r--r--library/statistics.cpp320
1 files changed, 320 insertions, 0 deletions
diff --git a/library/statistics.cpp b/library/statistics.cpp
new file mode 100644
index 00000000..ec03c59e
--- /dev/null
+++ b/library/statistics.cpp
@@ -0,0 +1,320 @@
+#include "statistics.h"
+
+#include <wx/ffile.h>
+#include "globalFunctions.h"
+#include "statusHandler.h"
+#include "../algorithm.h"
+#include <limits>
+
+
+RetrieveStatistics::~RetrieveStatistics()
+{ //write statistics to a file
+ wxFFile outputFile(wxT("statistics.dat"), wxT("w"));
+
+ outputFile.Write(wxT("Time(ms);Objects;Data\n"));
+
+ for (std::vector<statEntry>::const_iterator i = data.begin(); i != data.end(); ++i)
+ {
+ outputFile.Write(globalFunctions::numberToWxString(int(i->time)));
+ outputFile.Write(wxT(";"));
+ outputFile.Write(globalFunctions::numberToWxString(i->objects));
+ outputFile.Write(wxT(";"));
+ outputFile.Write(globalFunctions::numberToWxString(float(i->value)));
+ outputFile.Write(wxT("\n"));
+ }
+}
+
+
+void RetrieveStatistics::writeEntry(const double value, const int objects)
+{
+ statEntry newEntry;
+ newEntry.value = value;
+ newEntry.objects = objects;
+ newEntry.time = timer.Time();
+ data.push_back(newEntry);
+}
+
+
+//########################################################################################
+
+inline
+bool isNull(const double number)
+{
+ return globalFunctions::abs(number) <= std::numeric_limits<double>::epsilon();
+}
+
+
+inline
+wxString Statistics::formatRemainingTime(const double timeInMs) const
+{
+ bool unitSec = true;
+ double remainingTime = timeInMs / 1000;
+ wxString unit = _(" sec");
+ if (remainingTime > 55)
+ {
+ unitSec = false;
+ remainingTime /= 60;
+ unit = _(" min");
+ if (remainingTime > 59)
+ {
+ remainingTime /= 60;
+ unit = _(" hour(s)");
+ if (remainingTime > 23)
+ {
+ remainingTime /= 24;
+ unit = _(" day(s)");
+ }
+ }
+ }
+
+
+ int formattedTime = globalFunctions::round(remainingTime);
+
+ //reduce precision to 5 seconds
+ if (unitSec && formattedTime % 5 != 0)
+ formattedTime += 5 - formattedTime % 5; //"ceiling"
+
+
+ //avoid "jumping back and forth" when fluctuating around .5
+ if (remainingTimeLast < formattedTime)
+ {
+ if (unitSec)
+ {
+ formattedTime = globalFunctions::round(remainingTime);
+ formattedTime -= formattedTime % 5; //"floor"
+ }
+ else
+ formattedTime = int(remainingTime); //"floor"
+ }
+ remainingTimeLast = formattedTime;
+
+ return globalFunctions::numberToWxString(formattedTime) + unit;
+ //+ wxT("(") + globalFunctions::numberToWxString(globalFunctions::round(timeInMs / 1000)) + wxT(")");
+}
+
+
+Statistics::Statistics(const int totalObjectCount,
+ const double totalDataAmount,
+ const unsigned windowSizeRemainingTime,
+ const unsigned windowSizeBytesPerSecond) :
+ objectsTotal(totalObjectCount),
+ dataTotal(totalDataAmount),
+ windowSizeRemTime(windowSizeRemainingTime),
+ windowSizeBPS(windowSizeBytesPerSecond),
+ windowMax(std::max(windowSizeRemainingTime, windowSizeBytesPerSecond)),
+ remainingTimeLast(256*256*256*100) //something "big"
+{}
+
+
+void Statistics::addMeasurement(const int objectsCurrent, const double dataCurrent)
+{
+ record newEntry;
+ newEntry.objects = objectsCurrent;
+ newEntry.data = dataCurrent;
+ newEntry.time = timer.Time();
+
+ //insert new record
+ measurements.push_back(newEntry);
+
+ //remove all records earlier than "currentTime - windowSize"
+ const long newBegin = newEntry.time - windowMax;
+ while (measurements.size() > 0 && measurements.front().time < newBegin)
+ measurements.pop_front();
+}
+
+
+wxString Statistics::getRemainingTime() const
+{
+ if (measurements.size() > 0)
+ {
+ //find start of records "window"
+ const record backElement = measurements.back();
+ const long frontTime = backElement.time - windowSizeRemTime;
+ std::list<record>::const_iterator frontElement = measurements.end();
+ do
+ {
+ --frontElement;
+ }
+ while (frontElement != measurements.begin() && frontElement->time > frontTime);
+
+ const double timeDelta = backElement.time - frontElement->time;
+ const double dataDelta = backElement.data - frontElement->data;
+
+ const double dataRemaining = dataTotal - backElement.data;
+
+ if (!isNull(dataDelta))
+ return formatRemainingTime(dataRemaining * timeDelta / dataDelta);
+ }
+
+ return wxT("-"); //fallback
+}
+
+
+wxString Statistics::getBytesPerSecond() const
+{
+ if (measurements.size() > 0)
+ {
+ //find start of records "window"
+ const long frontTime = measurements.back().time - windowSizeBPS;
+ std::list<record>::const_iterator frontElement = measurements.end();
+ do
+ {
+ --frontElement;
+ }
+ while (frontElement != measurements.begin() && frontElement->time > frontTime);
+
+ const double timeDelta = measurements.back().time - frontElement->time;
+ const double dataDelta = measurements.back().data - frontElement->data;
+
+ if (!isNull(timeDelta))
+ return FreeFileSync::formatFilesizeToShortString(dataDelta * 1000 / timeDelta) + _("/sec");
+ }
+
+ return wxT("-"); //fallback
+}
+
+
+void Statistics::pauseTimer()
+{
+ timer.Pause();
+}
+
+
+void Statistics::resumeTimer()
+{
+ timer.Resume();
+}
+
+/*
+class for calculation of remaining time:
+----------------------------------------
+"filesize |-> time" is an affine linear function f(x) = z_1 + z_2 x
+
+For given n measurements, sizes x_0, ..., x_n and times f_0, ..., f_n, the function f (as a polynom of degree 1) can be lineary approximated by
+
+z_1 = (r - s * q / p) / ((n + 1) - s * s / p)
+z_2 = (q - s * z_1) / p = (r - (n + 1) z_1) / s
+
+with
+p := x_0^2 + ... + x_n^2
+q := f_0 x_0 + ... + f_n x_n
+r := f_0 + ... + f_n
+s := x_0 + ... + x_n
+
+=> the time to process N files with amount of data D is: N * z_1 + D * z_2
+
+Problem:
+--------
+Times f_0, ..., f_n can be very small so that precision of the PC clock is poor.
+=> Times have to be accumulated to enhance precision:
+Copying of m files with sizes x_i and times f_i (i = 1, ..., m) takes sum_i f(x_i) := m * z_1 + z_2 * sum x_i = sum f_i
+With X defined as the accumulated sizes and F the accumulated times this gives: (in theory...)
+m * z_1 + z_2 * X = F <=>
+z_1 + z_2 * X / m = F / m
+
+=> we obtain a new (artificial) measurement with size X / m and time F / m to be used in the linear approximation above
+
+
+Statistics::Statistics(const int totalObjectCount, const double totalDataAmount, const unsigned recordCount) :
+ objectsTotal(totalObjectCount),
+ dataTotal(totalDataAmount),
+ recordsMax(recordCount),
+ objectsLast(0),
+ dataLast(0),
+ timeLast(wxGetLocalTimeMillis()),
+ z1_current(0),
+ z2_current(0),
+ dummyRecordPresent(false) {}
+
+
+wxString Statistics::getRemainingTime(const int objectsCurrent, const double dataCurrent)
+{
+ //add new measurement point
+ const int m = objectsCurrent - objectsLast;
+ if (m != 0)
+ {
+ objectsLast = objectsCurrent;
+
+ const double X = dataCurrent - dataLast;
+ dataLast = dataCurrent;
+
+ const wxLongLong timeCurrent = wxGetLocalTimeMillis();
+ const double F = (timeCurrent - timeLast).ToDouble();
+ timeLast = timeCurrent;
+
+ record newEntry;
+ newEntry.x_i = X / m;
+ newEntry.f_i = F / m;
+
+ //remove dummy record
+ if (dummyRecordPresent)
+ {
+ measurements.pop_back();
+ dummyRecordPresent = false;
+ }
+
+ //insert new record
+ measurements.push_back(newEntry);
+ if (measurements.size() > recordsMax)
+ measurements.pop_front();
+ }
+ else //dataCurrent increased without processing new objects:
+ { //modify last measurement until m != 0
+ const double X = dataCurrent - dataLast; //do not set dataLast, timeLast variables here, but write dummy record instead
+ if (!isNull(X))
+ {
+ const wxLongLong timeCurrent = wxGetLocalTimeMillis();
+ const double F = (timeCurrent - timeLast).ToDouble();
+
+ record modifyEntry;
+ modifyEntry.x_i = X;
+ modifyEntry.f_i = F;
+
+ //insert dummy record
+ if (!dummyRecordPresent)
+ {
+ measurements.push_back(modifyEntry);
+ if (measurements.size() > recordsMax)
+ measurements.pop_front();
+ dummyRecordPresent = true;
+ }
+ else //modify dummy record
+ measurements.back() = modifyEntry;
+ }
+ }
+
+ //calculate remaining time based on stored measurement points
+ double p = 0;
+ double q = 0;
+ double r = 0;
+ double s = 0;
+ for (std::list<record>::const_iterator i = measurements.begin(); i != measurements.end(); ++i)
+ {
+ const double x_i = i->x_i;
+ const double f_i = i->f_i;
+ p += x_i * x_i;
+ q += f_i * x_i;
+ r += f_i;
+ s += x_i;
+ }
+
+ if (!isNull(p))
+ {
+ const double n = measurements.size();
+ const double tmp = (n - s * s / p);
+
+ if (!isNull(tmp) && !isNull(s))
+ {
+ const double z1 = (r - s * q / p) / tmp;
+ const double z2 = (r - n * z1) / s; //not (n + 1) here, since n already is the number of measurements
+
+ //refresh current values for z1, z2
+ z1_current = z1;
+ z2_current = z2;
+ }
+ }
+
+ return formatRemainingTime((objectsTotal - objectsCurrent) * z1_current + (dataTotal - dataCurrent) * z2_current);
+}
+
+*/
bgstack15