aboutsummaryrefslogtreecommitdiffstats
path: root/searchcore/src/vespa/searchcore/proton/matching/matching_stats.h
blob: f53c073e3c819e050ed36b0b43c80d1baa45b30e (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
// Copyright Vespa.ai. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.

#pragma once

#include <vector>
#include <cstddef>
#include <vespa/vespalib/util/time.h>
#include <vespa/vespalib/datastore/atomic_value_wrapper.h>

namespace proton::matching {

/**
 * Statistics for the matching pipeline. Used for internal aggregation
 * before inserting numbers into the metrics framework. The values
 * produced by a single search are set on a single object. Values are
 * aggregated by adding objects together.
 **/
struct MatchingStats
{
private:
    class Avg {
        double _value;
        size_t _count;
        double _min;
        double _max;
    public:
        Avg() noexcept : _value(0.0), _count(0), _min(0.0), _max(0.0) {}
        Avg & set(double value) noexcept {
            _value = value;
            _count = 1;
            _min = value;
            _max = value;
            return *this;
        }
        double avg() const noexcept {
            return (_count > 0) ? (_value / _count) : 0;
        }
        size_t count() const noexcept { return _count; }
        double min() const noexcept { return _min; }
        double max() const noexcept { return _max; }
        void add(const Avg &other) noexcept {
            if (_count == 0) {
                _min = other._min;
                _max = other._max;
            } else if (other._count > 0) {
                _min = std::min(_min, other._min);
                _max = std::max(_max, other._max);
            }
            _value += other._value;
            _count += other._count;
        }
    };

public:

    /**
     * Matching statistics that are tracked separately for each match
     * thread.
     **/
    class Partition {
        size_t _docsCovered;
        size_t _docsMatched;
        size_t _docsRanked;
        size_t _docsReRanked;
        size_t _softDoomed;
        Avg    _doomOvertime;
        Avg    _active_time;
        Avg    _wait_time;
        friend MatchingStats;
    public:
        Partition() noexcept
            : _docsCovered(0),
              _docsMatched(0),
              _docsRanked(0),
              _docsReRanked(0),
              _softDoomed(0),
              _doomOvertime(),
              _active_time(),
              _wait_time() { }

        Partition &docsCovered(size_t value) noexcept { _docsCovered = value; return *this; }
        size_t docsCovered() const noexcept { return _docsCovered; }
        Partition &docsMatched(size_t value) noexcept { _docsMatched = value; return *this; }
        size_t docsMatched() const noexcept { return _docsMatched; }
        Partition &docsRanked(size_t value) noexcept { _docsRanked = value; return *this; }
        size_t docsRanked() const noexcept { return _docsRanked; }
        Partition &docsReRanked(size_t value) noexcept { _docsReRanked = value; return *this; }
        size_t docsReRanked() const noexcept { return _docsReRanked; }
        Partition &softDoomed(bool v) noexcept { _softDoomed += v ? 1 : 0; return *this; }
        size_t softDoomed() const noexcept { return _softDoomed; }
        Partition & doomOvertime(vespalib::duration overtime) noexcept { _doomOvertime.set(vespalib::to_s(overtime)); return *this; }
        vespalib::duration doomOvertime() const noexcept { return vespalib::from_s(_doomOvertime.max()); }

        Partition &active_time(double time_s) noexcept { _active_time.set(time_s); return *this; }
        double active_time_avg() const noexcept { return _active_time.avg(); }
        size_t active_time_count() const noexcept { return _active_time.count(); }
        double active_time_min() const noexcept { return _active_time.min(); }
        double active_time_max() const noexcept { return _active_time.max(); }
        Partition &wait_time(double time_s) noexcept { _wait_time.set(time_s); return *this; }
        double wait_time_avg() const noexcept { return _wait_time.avg(); }
        size_t wait_time_count() const noexcept { return _wait_time.count(); }
        double wait_time_min() const noexcept { return _wait_time.min(); }
        double wait_time_max() const noexcept { return _wait_time.max(); }

        Partition &add(const Partition &rhs) noexcept {
            _docsCovered += rhs.docsCovered();
            _docsMatched += rhs._docsMatched;
            _docsRanked += rhs._docsRanked;
            _docsReRanked += rhs._docsReRanked;
            _softDoomed += rhs._softDoomed;
            _doomOvertime.add(rhs._doomOvertime);

            _active_time.add(rhs._active_time);
            _wait_time.add(rhs._wait_time);
            return *this;
        }
    };

private:
    size_t                 _queries;
    size_t                 _limited_queries;
    size_t                 _docidSpaceCovered;
    size_t                 _docsMatched;
    size_t                 _docsRanked;
    size_t                 _docsReRanked;
    size_t                 _softDoomed;
    Avg                    _doomOvertime;
    using SoftDoomFactor = vespalib::datastore::AtomicValueWrapper<double>;
    SoftDoomFactor         _softDoomFactor;
    Avg                    _querySetupTime;
    Avg                    _queryLatency;
    Avg                    _matchTime;
    Avg                    _groupingTime;
    Avg                    _rerankTime;
    std::vector<Partition> _partitions;

public:
    static constexpr double INITIAL_SOFT_DOOM_FACTOR = 0.5;
    MatchingStats(const MatchingStats &) = delete;
    MatchingStats & operator = (const MatchingStats &) = delete;
    MatchingStats(MatchingStats &&) noexcept = default;
    MatchingStats & operator =  (MatchingStats &&) noexcept = default;
    MatchingStats() noexcept : MatchingStats(INITIAL_SOFT_DOOM_FACTOR) {}
    MatchingStats(double prev_soft_doom_factor) noexcept;
    ~MatchingStats();

    MatchingStats &queries(size_t value) { _queries = value; return *this; }
    size_t queries() const { return _queries; }

    MatchingStats &limited_queries(size_t value) { _limited_queries = value; return *this; }
    size_t limited_queries() const { return _limited_queries; }

    MatchingStats &docidSpaceCovered(size_t value) { _docidSpaceCovered = value; return *this; }
    size_t docidSpaceCovered() const { return _docidSpaceCovered; }

    MatchingStats &docsMatched(size_t value) { _docsMatched = value; return *this; }
    size_t docsMatched() const { return _docsMatched; }

    MatchingStats &docsRanked(size_t value) { _docsRanked = value; return *this; }
    size_t docsRanked() const { return _docsRanked; }

    MatchingStats &docsReRanked(size_t value) { _docsReRanked = value; return *this; }
    size_t docsReRanked() const { return _docsReRanked; }

    MatchingStats &softDoomed(size_t value) { _softDoomed = value; return *this; }
    size_t softDoomed() const { return _softDoomed; }

    vespalib::duration doomOvertime() const { return vespalib::from_s(_doomOvertime.max()); }

    MatchingStats &softDoomFactor(double value) { _softDoomFactor.store_relaxed(value); return *this; }
    double softDoomFactor() const { return _softDoomFactor.load_relaxed(); }
    MatchingStats &updatesoftDoomFactor(vespalib::duration hardLimit, vespalib::duration softLimit, vespalib::duration duration);

    MatchingStats &querySetupTime(double time_s) { _querySetupTime.set(time_s); return *this; }
    double querySetupTimeAvg() const { return _querySetupTime.avg(); }
    size_t querySetupTimeCount() const { return _querySetupTime.count(); }
    double querySetupTimeMin() const { return _querySetupTime.min(); }
    double querySetupTimeMax() const { return _querySetupTime.max(); }

    MatchingStats &queryLatency(double time_s) { _queryLatency.set(time_s); return *this; }
    double queryLatencyAvg() const { return _queryLatency.avg(); }
    size_t queryLatencyCount() const { return _queryLatency.count(); }
    double queryLatencyMin() const { return _queryLatency.min(); }
    double queryLatencyMax() const { return _queryLatency.max(); }

    MatchingStats &matchTime(double time_s) { _matchTime.set(time_s); return *this; }
    double matchTimeAvg() const { return _matchTime.avg(); }
    size_t matchTimeCount() const { return _matchTime.count(); }
    double matchTimeMin() const { return _matchTime.min(); }
    double matchTimeMax() const { return _matchTime.max(); }

    MatchingStats &groupingTime(double time_s) { _groupingTime.set(time_s); return *this; }
    double groupingTimeAvg() const { return _groupingTime.avg(); }
    size_t groupingTimeCount() const { return _groupingTime.count(); }
    double groupingTimeMin() const { return _groupingTime.min(); }
    double groupingTimeMax() const { return _groupingTime.max(); }

    MatchingStats &rerankTime(double time_s) { _rerankTime.set(time_s); return *this; }
    double rerankTimeAvg() const { return _rerankTime.avg(); }
    size_t rerankTimeCount() const { return _rerankTime.count(); }
    double rerankTimeMin() const { return _rerankTime.min(); }
    double rerankTimeMax() const { return _rerankTime.max(); }

    // used to merge in stats from each match thread
    MatchingStats &merge_partition(const Partition &partition, size_t id);
    size_t getNumPartitions() const { return _partitions.size(); }
    const Partition &getPartition(size_t index) const { return _partitions[index]; }

    // used to aggregate accross searches (and configurations)
    MatchingStats &add(const MatchingStats &rhs) noexcept;
};

}