xaizek / etabench (License: GPLv3) (since 2022-09-08)
Benchmark for algorithms that compute I/O ETA
<root> / src / algs.hpp (8b10a1354af1ebb71ee3efeaaa6cd7e7c17e15f2) (6,921B) (mode 100644) [raw]
// etabench
// Copyright (C) 2022 xaizek <xaizek@posteo.net>
//
// This file is part of etabench.
//
// etabench is free software: you can redistribute it and/or modify
// it under the terms of version 3 of the GNU General Public License
// as published by the Free Software Foundation.
//
// etabench is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with etabench.  If not, see <https://www.gnu.org/licenses/>.

#ifndef ETABENCH__ALGS_HPP__
#define ETABENCH__ALGS_HPP__

#include <functional>
#include <map>
#include <memory>
#include <vector>

#include "core.hpp"

class Window
{
    using weight_f = std::function<float(int idx, float lastWeight)>;

public:
    // Pass in negative size for unlimited window size.
    Window(int size, weight_f weight) : size(size), weight(weight)
    { }

public:
    void clear()
    { data.clear(); }

    void extend(float datum)
    {
        // Emulating circular buffer.
        data.push_back(datum);
        if ((int)data.size() == size) {
            data.erase(data.cbegin());
        }
    }

    float getValue() const
    {
        float v = 0.0f;
        float totalWeight = 0.0f;

        float w = 0;
        for (int i = 0; float datum : data) {
            w = weight(i++, w);
            v += w*datum;
            totalWeight += w;
        }

        v /= totalWeight;
        return v;
    }

    float update(float datum)
    {
        extend(datum);
        return getValue();
    }

private:
    int size;
    weight_f weight;
    std::vector<float> data;
};

class AverageAlg : public EtaAlg
{
public:
    virtual std::string getName() const override
    { return "Average"; }

    virtual void reset() override
    { }

    virtual int estimate(int current, int total, int time) override;
};

class ImmediateAlg : public EtaAlg
{
public:
    virtual std::string getName() const override
    { return "Immediate"; }

    virtual void reset() override;

    virtual int estimate(int current, int total, int time) override;

private:
    int lastProgress;
    int lastTime;
};

class CombinedAlg : public EtaAlg
{
public:
    explicit CombinedAlg(std::vector<std::unique_ptr<EtaAlg>> algs);

public:
    virtual std::string getName() const override;

    virtual void reset() override;

    virtual int estimate(int current, int total, int time) override;

private:
    std::vector<std::unique_ptr<EtaAlg>> algs;
};

// Immediage speed whose next value is clamped by its previous value times +-2%.
class ImmChangeLimitAlg : public EtaAlg
{
public:
    virtual std::string getName() const override
    { return "ImmChangeLimit"; }

    virtual void reset() override;

    virtual int estimate(int current, int total, int time) override;

private:
    int lastProgress;
    int lastTime;
    float lastSpeed;
};

// Average speed whose next value is clamped by its previous value times +-2%.
class AveChangeLimitAlg : public EtaAlg
{
public:
    virtual std::string getName() const override
    { return "AveChangeLimit"; }

    virtual void reset() override;

    virtual int estimate(int current, int total, int time) override;

private:
    float lastSpeed;
};

// Limited average that looks back `delay` seconds.
class LookBackAlg : public EtaAlg
{
    struct Point {
        int time;
        int current;
    };

public:
    explicit LookBackAlg(int delay);

public:
    virtual std::string getName() const override
    { return "LookBack " + std::to_string(delay); }

    virtual void reset() override;

    virtual int estimate(int current, int total, int time) override;

private:
    int delay;
    std::vector<Point> points;
};

class AccelerationAlg : public EtaAlg
{
public:
    AccelerationAlg();

public:
    virtual std::string getName() const override
    { return "Acceleration"; }

    virtual void reset() override;

    virtual int estimate(int current, int total, int time) override;

private:
    int lastProgress;
    int lastTime;
    int lastEta;
    float lastV;
    Window speeds;
    Window etas;
};

class SwitchAlg : public EtaAlg
{
public:
    virtual std::string getName() const override
    { return "Switch"; }

    virtual void reset() override;

    virtual int estimate(int current, int total, int time) override;

private:
    std::map<int, int> history;
    int lastProgress;
};

class GravityAlg : public EtaAlg
{
public:
    GravityAlg();

public:
    virtual std::string getName() const override
    { return "Gravity"; }

    virtual void reset() override;

    virtual int estimate(int current, int total, int time) override;

private:
    int lastProgress;
    int lastTime;
    float avgProgressRate;
    float avgSeekRate;
    Window speeds;
};

class FirefoxAlg : public EtaAlg
{
public:
    virtual std::string getName() const override
    { return "Firefox"; }

    virtual void reset() override;

    virtual int estimate(int current, int total, int time) override;

private:
    int lastTime;
    int lastProgress;
    float lastEta;
    float speed;
};

class SmoothingAlg : public EtaAlg
{
public:
    explicit SmoothingAlg(int alpha) : alpha(alpha)
    { }

public:
    virtual std::string getName() const override
    { return "Smoothing 0." + std::to_string(alpha); }

    virtual void reset() override;

    virtual int estimate(int current, int total, int time) override;

private:
    int lastTime;
    int lastProgress;
    int lastEta;
    int alpha;
    float speed;
};

class SlownessAlg : public EtaAlg
{
public:
    explicit SlownessAlg(int decay) : decay(decay)
    { }

public:
    virtual std::string getName() const override
    { return "Slowness " + std::to_string(decay); }

    virtual void reset() override;

    virtual int estimate(int current, int total, int time) override;

private:
    int lastProgress;
    int lastTime;
    int lastEta;
    float totalEta;
    int decay;
};

class WindowAlg : public EtaAlg
{
public:
    WindowAlg(int windowSize, float alpha);

public:
    virtual std::string getName() const override;

    virtual void reset() override;

    virtual int estimate(int current, int total, int time) override;

private:
    int windowSize;
    float alpha;
    int lastTime;
    int lastProgress;
    int lastEta;
    Window speeds;
};

class ExponentialAlg : public EtaAlg
{
public:
    explicit ExponentialAlg(int decay) : decay(decay)
    { }

public:
    virtual std::string getName() const override
    { return "Exponential " + std::to_string(decay); }

    virtual void reset() override;

    virtual int estimate(int current, int total, int time) override;

private:
    int lastProgress;
    int lastTime;
    int lastEta;
    float rateEst;
    int decay;
};

#endif // ETABENCH__ALGS_HPP__
Hints

Before first commit, do not forget to setup your git environment:
git config --global user.name "your_name_here"
git config --global user.email "your@email_here"

Clone this repository using HTTP(S):
git clone https://code.reversed.top/user/xaizek/etabench

Clone this repository using ssh (do not forget to upload a key first):
git clone ssh://rocketgit@code.reversed.top/user/xaizek/etabench

You are allowed to anonymously push to this repository.
This means that your pushed commits will automatically be transformed into a pull request:
... clone the repository ...
... make some changes and some commits ...
git push origin master