1
// This file is part of BOINC.
2
// http://boinc.berkeley.edu
3
// Copyright (C) 2008 University of California
5
// BOINC is free software; you can redistribute it and/or modify it
6
// under the terms of the GNU Lesser General Public License
7
// as published by the Free Software Foundation,
8
// either version 3 of the License, or (at your option) any later version.
10
// BOINC is distributed in the hope that it will be useful,
11
// but WITHOUT ANY WARRANTY; without even the implied warranty of
12
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
13
// See the GNU Lesser General Public License for more details.
15
// You should have received a copy of the GNU Lesser General Public License
16
// along with BOINC. If not, see <http://www.gnu.org/licenses/>.
17
// Modify claimed credit based on the historical granted credit if
18
// the project is configured to do this
21
// functions related to the computation and granting of credit
22
// Note: this is credit.cpp rather than sched_credit.cpp
23
// because you might grant credit e.g. from a trickle handler
28
#include "error_numbers.h"
30
#include "sched_config.h"
31
#include "sched_msgs.h"
32
#include "sched_util.h"
33
#include "sched_shmem.h"
34
#include "sched_types.h"
39
double fpops_to_credit(double fpops, double intops) {
40
// TODO: use fp_weight if specified in config file
41
double fpc = (fpops/1e9)*COBBLESTONE_FACTOR/SECONDS_PER_DAY;
42
double intc = (intops/1e9)*COBBLESTONE_FACTOR/SECONDS_PER_DAY;
43
return std::max(fpc, intc);
46
// Grant the host (and associated user and team)
47
// the given amount of credit for work that started at the given time.
48
// Update the user and team records,
49
// but not the host record (caller must update)
52
DB_HOST& host, double start_time, double cpu_time, double credit
60
// first, process the host
64
start_time, credit, CREDIT_HALF_LIFE,
65
host.expavg_credit, host.expavg_time
67
host.total_credit += credit;
71
retval = user.lookup_id(host.userid);
73
log_messages.printf(MSG_CRITICAL,
74
"lookup of user %d failed %d\n",
82
start_time, credit, CREDIT_HALF_LIFE,
83
user.expavg_credit, user.expavg_time
86
buf, "total_credit=total_credit+%.15e, expavg_credit=%.15e, expavg_time=%.15e",
87
credit, user.expavg_credit, user.expavg_time
89
retval = user.update_field(buf);
91
log_messages.printf(MSG_CRITICAL,
92
"update of user %d failed %d\n",
97
// and finally the team
100
retval = team.lookup_id(user.teamid);
102
log_messages.printf(MSG_CRITICAL,
103
"lookup of team %d failed %d\n",
110
start_time, credit, CREDIT_HALF_LIFE,
111
team.expavg_credit, team.expavg_time
114
"total_credit=total_credit+%.15e, expavg_credit=%.15e, expavg_time=%.15e",
115
credit, team.expavg_credit, team.expavg_time
117
retval = team.update_field(buf);
119
log_messages.printf(MSG_CRITICAL,
120
"update of team %d failed %d\n",
128
///////////////////// V2 CREDIT STUFF STARTS HERE ///////////////////
130
// levels of confidence in a credit value
132
#define PFC_MODE_NORMAL 0
133
// PFC was computed in the "normal" way, i.e.
135
// - app version scaling (i.e. not anonymous platform)
137
#define PFC_MODE_APPROX 1
138
// PFC was approximated, but still (in the absence of cheating)
139
// reflects the size of the particular job
140
#define PFC_MODE_WU_EST 2
141
// PFC was set to the WU estimate.
142
// If this doesn't reflect the WU size, neither does the PFC estimate
143
// This is a last resort, and can be way off.
145
// used in the computation of AV scale factors
150
int nvers_thresh; // # app versions w/ lots of samples
159
void update(APP_VERSION& av) {
161
if (av.pfc.n > MIN_VERSION_SAMPLES) {
163
pfc_sum += av.pfc.get_avg() * av.pfc.n;
168
return pfc_sum/pfc_n;
172
// "avg" is the average PFC for this app
173
// over CPU versions or GPU versions, whichever is lowest.
174
// Update the pfc_scale of this app's versions in the DB,
175
// and update app.min_avg_pfc
177
int scale_versions(APP& app, double avg, SCHED_SHMEM* ssp) {
181
for (int j=0; j<ssp->napp_versions; j++) {
182
APP_VERSION& av = ssp->app_versions[j];
183
if (av.appid != app.id) continue;
184
if (av.pfc.n < MIN_VERSION_SAMPLES) continue;
185
av.pfc_scale= avg/av.pfc.get_avg();
189
sprintf(buf, "pfc_scale=%.15e", av.pfc_scale);
190
retval = dav.update_field(buf);
191
if (retval) return retval;
192
if (config.debug_credit) {
193
PLATFORM* p = ssp->lookup_platform_id(av.platformid);
194
log_messages.printf(MSG_NORMAL,
195
" updating scale factor for %d (%s %s)\n",
196
av.id, p->name, av.plan_class
198
log_messages.printf(MSG_NORMAL,
199
" n: %g avg PFC: %g new scale: %g\n",
200
av.pfc.n, av.pfc.get_avg(), av.pfc_scale
204
app.min_avg_pfc = avg;
207
sprintf(buf, "min_avg_pfc=%.15e", avg);
208
retval = da.update_field(buf);
209
if (retval) return retval;
213
// Update app version scale factors,
214
// and find the min average PFC for each app.
215
// Called periodically from the master feeder.
217
int update_av_scales(SCHED_SHMEM* ssp) {
219
if (config.debug_credit) {
220
log_messages.printf(MSG_NORMAL, "-- updating app version scales --\n");
222
for (i=0; i<ssp->napps; i++) {
223
APP& app = ssp->apps[i];
224
if (config.debug_credit) {
225
log_messages.printf(MSG_NORMAL, "app %s (%d)\n", app.name, app.id);
227
RSC_INFO cpu_info, gpu_info;
229
// find the average PFC of CPU and GPU versions
231
for (j=0; j<ssp->napp_versions; j++) {
232
APP_VERSION& avr = ssp->app_versions[j];
233
if (avr.appid != app.id) continue;
235
retval = av.lookup_id(avr.id);
236
if (retval) return retval;
237
avr = av; // update shared mem array
238
if (strstr(av.plan_class, "cuda") || strstr(av.plan_class, "ati")) {
239
if (config.debug_credit) {
240
log_messages.printf(MSG_NORMAL,
241
"add to gpu totals: (%d %s) %g %g\n",
242
av.id, av.plan_class, av.pfc.n, av.pfc.get_avg()
247
if (config.debug_credit) {
248
log_messages.printf(MSG_NORMAL,
249
"add to cpu totals: (%d %s) %g %g\n",
250
av.id, av.plan_class, av.pfc.n, av.pfc.get_avg()
257
// If there are only CPU or only GPU versions,
258
// and at least 2 are above threshold, normalize to the average
260
// If there are both, and at least 1 of each is above threshold,
261
// normalize to the min of the averages
263
if (cpu_info.nvers_total) {
264
if (gpu_info.nvers_total) {
265
if (cpu_info.nvers_thresh && gpu_info.nvers_thresh) {
266
if (config.debug_credit) {
267
log_messages.printf(MSG_NORMAL,
268
"CPU avg: %g; GPU avg: %g\n",
269
cpu_info.avg(), gpu_info.avg()
273
cpu_info.avg()<gpu_info.avg()?cpu_info.avg():gpu_info.avg(),
278
if (cpu_info.nvers_thresh > 1) {
279
log_messages.printf(MSG_NORMAL,
280
"CPU avg: %g\n", cpu_info.avg()
282
scale_versions(app, cpu_info.avg(), ssp);
286
if (gpu_info.nvers_thresh > 1) {
287
log_messages.printf(MSG_NORMAL,
288
"GPU avg: %g\n", gpu_info.avg()
290
scale_versions(app, gpu_info.avg(), ssp);
296
if (config.debug_credit) {
297
log_messages.printf(MSG_NORMAL, "-------------\n");
302
// look up HOST_APP_VERSION record; called from validator and transitioner.
303
// Normally the record will exist; if not create it (transitional case)
305
int hav_lookup(DB_HOST_APP_VERSION& hav, int hostid, int avid) {
308
sprintf(buf, "where host_id=%d and app_version_id=%d", hostid, avid);
309
retval = hav.lookup(buf);
310
if (retval == ERR_DB_NOT_FOUND) {
312
hav.host_id = hostid;
313
hav.app_version_id = avid;
314
retval = hav.insert();
319
DB_APP_VERSION* av_lookup(int id, vector<DB_APP_VERSION>& app_versions) {
320
for (unsigned int i=0; i<app_versions.size(); i++) {
321
if (app_versions[i].id == id) {
322
return &app_versions[i];
326
int retval = av.lookup_id(id);
327
if (retval) return NULL;
328
app_versions.push_back(av);
329
return &(app_versions[app_versions.size()-1]);
332
// the estimated PFC for a given WU, in the absence of any other info
334
inline double wu_estimated_pfc(WORKUNIT& wu, DB_APP& app) {
335
return wu.rsc_fpops_est*app.min_avg_pfc;
337
inline double wu_estimated_credit(WORKUNIT& wu, DB_APP& app) {
338
return wu_estimated_pfc(wu, app)*COBBLESTONE_SCALE;
341
// Compute or estimate "claimed peak FLOP count".
342
// Possibly update host_app_version records and write to DB.
343
// Possibly update app_version records in memory and let caller write to DB,
344
// to merge DB writes
347
RESULT& r, WORKUNIT& wu, DB_APP& app, // in
348
vector<DB_APP_VERSION>&app_versions, // in/out
349
DB_HOST_APP_VERSION& hav, // in/out
350
double& pfc, int& mode // out
352
DB_APP_VERSION* avp=0;
355
mode = PFC_MODE_APPROX;
357
// is result from old scheduler that didn't set r.app_version_id correctly?
358
// if so, use WU estimate (this is a transient condition)
360
if (r.app_version_id == 0 || r.app_version_id == 1) {
361
if (config.debug_credit) {
362
log_messages.printf(MSG_NORMAL,
363
"[credit] [RESULT#%d] missing app_version_id (%d): returning WU default %.2f\n",
364
r.id, r.app_version_id, wu_estimated_credit(wu, app)
367
mode = PFC_MODE_WU_EST;
368
pfc = wu_estimated_pfc(wu, app);
372
// temporary kludge for SETI@home:
373
// if GPU initialization fails the app falls back to CPU.
375
if (strstr(r.stderr_out, "Device Emulation (CPU)")) {
376
if (config.debug_credit) {
377
log_messages.printf(MSG_NORMAL,
378
"[credit] [RESULT#%d][AV#%d] CUDA app fell back to CPU; returning WU default %.2f\n",
379
r.id, r.app_version_id, wu.rsc_fpops_est*COBBLESTONE_SCALE
382
mode = PFC_MODE_WU_EST;
383
pfc = wu_estimated_pfc(wu, app);
387
int gavid = generalized_app_version_id(r.app_version_id, r.appid);
392
mode = PFC_MODE_WU_EST;
393
pfc = wu_estimated_pfc(wu, app);
397
// old clients report CPU time but not elapsed time.
398
// Use HOST_APP_VERSION.et to track statistics of CPU time.
400
if (!r.elapsed_time) {
401
if (config.debug_credit) {
402
log_messages.printf(MSG_NORMAL,
403
"[credit] [RESULT#%d] old client (elapsed time not reported)\n",
408
r.cpu_time/wu.rsc_fpops_est,
409
HAV_AVG_THRESH, HAV_AVG_WEIGHT, HAV_AVG_LIMIT
411
pfc = wu_estimated_pfc(wu, app);
412
if (config.debug_credit) {
413
log_messages.printf(MSG_NORMAL,
414
"[credit] [RESULT#%d] old client: raw credit %.2f\n",
415
r.id, pfc*COBBLESTONE_SCALE
418
bool do_scale = true;
419
if (hav.et.n < MIN_HOST_SAMPLES || (hav.et.get_avg() <= 0)) {
421
if (config.debug_credit) {
422
log_messages.printf(MSG_NORMAL,
423
"[credit] [RESULT#%d] old client: no host scaling - zero or too few samples %f\n",
429
&& app.host_scale_check
430
&& hav.consecutive_valid < CONS_VALID_HOST_SCALE
433
if (config.debug_credit) {
434
log_messages.printf(MSG_NORMAL,
435
"[credit] [RESULT#%d] old client: no host scaling - cons valid %d\n",
436
r.id, hav.consecutive_valid
441
double s = r.cpu_time / (hav.et.get_avg()*wu.rsc_fpops_est);
443
if (config.debug_credit) {
444
log_messages.printf(MSG_NORMAL,
445
"[credit] [RESULT#%d] old client: scaling (based on CPU time) by %g, return %.2f\n",
446
r.id, s, pfc*COBBLESTONE_SCALE
450
if (config.debug_credit) {
451
log_messages.printf(MSG_NORMAL,
452
"[credit] [RESULT#%d] old client: returning PFC %.2f\n",
453
r.id, pfc*COBBLESTONE_SCALE
459
// r.flops_estimate shouldn't be zero,
460
// but (because of scheduler bug) it can be.
461
// At this point we don't have much to go on, so use 1e10.
463
if (!r.flops_estimate) {
464
r.flops_estimate = 1e10;
467
double raw_pfc = (r.elapsed_time * r.flops_estimate);
468
if (config.debug_credit) {
469
log_messages.printf(MSG_NORMAL,
470
"[credit] [RESULT#%d] raw credit: %.2f (%.2f sec, %.2f est GFLOPS)\n",
471
r.id, raw_pfc*COBBLESTONE_SCALE, r.elapsed_time,
478
if (raw_pfc > wu.rsc_fpops_bound) {
479
char query[256], clause[256];
480
pfc = wu_estimated_pfc(wu, app);
481
if (config.debug_credit) {
482
log_messages.printf(MSG_NORMAL,
483
"[credit] [RESULT#%d] sanity check failed: %.2f>%.2f, return %.2f\n",
484
r.id, raw_pfc*COBBLESTONE_SCALE,
485
wu.rsc_fpops_bound*COBBLESTONE_SCALE, pfc*COBBLESTONE_SCALE
488
sprintf(query, "consecutive_valid=0");
489
sprintf(clause, "host_id=%d and app_version_id=%d", r.hostid, gavid);
490
retval = hav.update_fields_noid(query, clause);
494
if (r.app_version_id < 0) {
497
bool do_scale = true;
498
if (hav.pfc.n < MIN_HOST_SAMPLES || hav.pfc.get_avg()<=0) {
500
if (config.debug_credit) {
501
log_messages.printf(MSG_NORMAL,
502
"[credit] [RESULT#%d] anon platform, not scaling, PFC avg zero or too few samples %.0f\n",
508
&& app.host_scale_check
509
&& hav.consecutive_valid < CONS_VALID_HOST_SCALE
512
if (config.debug_credit) {
513
log_messages.printf(MSG_NORMAL,
514
"[credit] [RESULT#%d] anon platform, not scaling, cons valid %d\n",
515
r.id, hav.consecutive_valid
520
double scale = app.min_avg_pfc / hav.pfc.get_avg();
521
pfc = raw_pfc * scale;
522
if (config.debug_credit) {
523
log_messages.printf(MSG_NORMAL,
524
"[credit] [RESULT#%d] anon platform, scaling by %g (%.2f/%.2f)\n",
525
r.id, scale, app.min_avg_pfc, hav.pfc.get_avg()
529
pfc = wu_estimated_pfc(wu, app);
530
if (config.debug_credit) {
531
log_messages.printf(MSG_NORMAL,
532
"[credit] [RESULT#%d] not scaling, using app avg %.2f\n",
533
r.id, pfc*COBBLESTONE_SCALE
537
if (config.debug_credit) {
538
log_messages.printf(MSG_NORMAL,
539
"[credit] [RESULT#%d] anon platform, returning %.2f\n",
540
r.id, pfc*COBBLESTONE_SCALE
544
avp = av_lookup(r.app_version_id, app_versions);
546
log_messages.printf(MSG_CRITICAL,
547
"get_pfc() [RESULT#%d]: No AVP %d!!\n", r.id, r.app_version_id
549
return ERR_NOT_FOUND;
551
if (config.debug_credit) {
552
log_messages.printf(MSG_NORMAL,
553
"[credit] [RESULT#%d] [AV#%d] normal case. %.0f sec, %.1f GFLOPS. raw credit: %.2f\n",
554
r.id, avp->id, r.elapsed_time, r.flops_estimate/1e9,
555
raw_pfc*COBBLESTONE_SCALE
559
bool do_scale = true;
560
double host_scale = 0;
561
if (app.host_scale_check
562
&& hav.consecutive_valid < CONS_VALID_HOST_SCALE
565
if (config.debug_credit) {
566
log_messages.printf(MSG_NORMAL,
567
"[credit] [RESULT#%d] not host scaling - cons valid %d\n",
568
r.id, hav.consecutive_valid
572
if (do_scale && (hav.pfc.n < MIN_HOST_SAMPLES || hav.pfc.get_avg()==0)) {
574
if (config.debug_credit) {
575
log_messages.printf(MSG_NORMAL,
576
"[credit] [RESULT#%d] not host scaling - HAV PFC zero or too few samples %.0f\n",
581
if (do_scale && avp->pfc.n < MIN_VERSION_SAMPLES) {
583
if (config.debug_credit) {
584
log_messages.printf(MSG_NORMAL,
585
"[credit] [RESULT#%d] not host scaling - app_version PFC too few samples%.0f\n",
590
if (do_scale && hav.pfc.get_avg() <= 0) {
592
if (config.debug_credit) {
593
log_messages.printf(MSG_NORMAL,
594
"[credit] [RESULT#%d] not host scaling - HAV PFC is zero\n",
600
host_scale = avp->pfc.get_avg() / hav.pfc.get_avg();
601
if (host_scale > 10) host_scale = 10;
602
if (config.debug_credit) {
603
log_messages.printf(MSG_NORMAL,
604
"[credit] [RESULT#%d] host scale: %.2f (%f/%f)\n",
605
r.id, host_scale, avp->pfc.get_avg(), hav.pfc.get_avg()
611
if (avp->pfc_scale) {
612
pfc *= avp->pfc_scale;
615
mode = PFC_MODE_NORMAL;
617
if (config.debug_credit) {
618
log_messages.printf(MSG_NORMAL,
619
"[credit] [RESULT#%d] applying app version scale %.3f\n",
627
if (config.debug_credit) {
628
log_messages.printf(MSG_NORMAL,
629
"[credit] [RESULT#%d] no app version scale\n",
634
if (config.debug_credit) {
635
log_messages.printf(MSG_NORMAL,
636
"[credit] [RESULT#%d] [AV#%d] PFC avgs with %g (%g/%g)\n",
638
raw_pfc/wu.rsc_fpops_est,
639
raw_pfc, wu.rsc_fpops_est
642
avp->pfc_samples.push_back(raw_pfc/wu.rsc_fpops_est);
645
if (config.debug_credit) {
646
log_messages.printf(MSG_NORMAL,
647
"[credit] [RESULT#%d] updating HAV PFC %.2f et %g turnaround %d\n",
648
r.id, raw_pfc / wu.rsc_fpops_est,
649
r.elapsed_time / wu.rsc_fpops_est,
650
(r.received_time - r.sent_time)
655
raw_pfc / wu.rsc_fpops_est,
656
HAV_AVG_THRESH, HAV_AVG_WEIGHT, HAV_AVG_LIMIT
659
r.elapsed_time / wu.rsc_fpops_est,
660
HAV_AVG_THRESH, HAV_AVG_WEIGHT, HAV_AVG_LIMIT
662
hav.turnaround.update_var(
663
(r.received_time - r.sent_time),
664
HAV_AVG_THRESH, HAV_AVG_WEIGHT, HAV_AVG_LIMIT
667
// keep track of credit per app version
670
avp->credit_samples.push_back(pfc*COBBLESTONE_SCALE);
671
avp->credit_times.push_back(r.sent_time);
677
// compute the average of some numbers,
678
// where each value is weighted by the sum of the other values.
679
// (reduces the weight of large outliers)
681
double low_average(vector<double>& v) {
688
for (i=0; i<n; i++) {
692
for (i=0; i<n; i++) {
693
total += v[i]*(sum-v[i]);
695
return total/((n-1)*sum);
698
double vec_min(vector<double>& v) {
700
for (unsigned int i=1; i<v.size(); i++) {
701
if (v[i] < x) x = v[i];
706
// Called by validator when canonical result has been selected.
707
// Compute credit for valid instances, store in result.granted_credit
708
// and return as credit
710
int assign_credit_set(
711
WORKUNIT& wu, vector<RESULT>& results,
713
vector<DB_APP_VERSION>& app_versions,
714
vector<DB_HOST_APP_VERSION>& host_app_versions,
715
double max_granted_credit, double& credit
720
vector<double> normal;
721
vector<double> approx;
723
for (i=0; i<results.size(); i++) {
724
RESULT& r = results[i];
725
if (r.validate_state != VALIDATE_STATE_VALID) continue;
726
DB_HOST_APP_VERSION& hav = host_app_versions[i];
727
retval = get_pfc(r, wu, app, app_versions, hav, pfc, mode);
729
log_messages.printf(MSG_CRITICAL, "get_pfc() error: %d\n", retval);
732
if (config.debug_credit) {
733
log_messages.printf(MSG_NORMAL,
734
"[credit] [RESULT#%d] get_pfc() returns credit %g mode %s\n",
735
r.id, pfc*COBBLESTONE_SCALE, (mode==PFC_MODE_NORMAL)?"normal":"approx"
739
if (max_granted_credit && pfc*COBBLESTONE_SCALE > max_granted_credit) {
740
log_messages.printf(MSG_NORMAL,
741
"[credit] Credit too high: %f\n", pfc*COBBLESTONE_SCALE
743
pfc = wu_estimated_pfc(wu, app);
745
if (pfc > wu.rsc_fpops_bound) {
746
log_messages.printf(MSG_NORMAL,
747
"[credit] PFC too high: %f\n", pfc*COBBLESTONE_SCALE
749
pfc = wu_estimated_pfc(wu, app);
751
if (mode == PFC_MODE_NORMAL) {
752
normal.push_back(pfc);
754
approx.push_back(pfc);
758
// averaging policy: if there is least one normal result,
759
// use the "low average" of normal results.
760
// Otherwise use the min of all results
764
x = low_average(normal);
768
x *= COBBLESTONE_SCALE;
769
if (config.debug_credit) {
770
log_messages.printf(MSG_NORMAL,
771
"[credit] [WU#%d] assign_credit_set: credit %g\n",
775
for (i=0; i<results.size(); i++) {
776
RESULT& r = results[i];
777
if (r.validate_state != VALIDATE_STATE_VALID) continue;
778
r.granted_credit = x;
785
// - errored out (scheduler)
786
// - timed out (transitioner)
787
// - failed validation (validator).
788
// Put (host/app_version) on "host scale probation",
789
// so that we won't use host scaling for a while.
791
void got_error(DB_HOST_APP_VERSION& hav) {
792
if (config.debug_credit) {
793
log_messages.printf(MSG_NORMAL,
794
"[credit] [HAV#%d] got error, setting error rate to %f\n",
795
hav.app_version_id, ERROR_RATE_INIT
800
// carefully write any app_version records that have changed;
801
// done at the end of every validator scan.
803
int write_modified_app_versions(vector<DB_APP_VERSION>& app_versions) {
806
double now = dtime();
808
if (config.debug_credit && app_versions.size()) {
809
log_messages.printf(MSG_NORMAL,
810
"[credit] start write_modified_app_versions()\n"
813
for (i=0; i<app_versions.size(); i++) {
814
DB_APP_VERSION& av = app_versions[i];
815
if (av.pfc_samples.empty() && av.credit_samples.empty()) {
816
if (config.debug_credit) {
817
log_messages.printf(MSG_NORMAL,
818
"[credit] skipping app version %d - no change\n", av.id
824
double pfc_n_orig = av.pfc.n;
825
double expavg_credit_orig = av.expavg_credit;
827
for (j=0; j<av.pfc_samples.size(); j++) {
830
AV_AVG_THRESH, AV_AVG_WEIGHT, AV_AVG_LIMIT
833
for (j=0; j<av.credit_samples.size(); j++) {
836
av.credit_times[j], av.credit_samples[j], CREDIT_HALF_LIFE,
837
av.expavg_credit, av.expavg_time
840
char query[512], clause[512];
842
"pfc_n=%.15e, pfc_avg=%.15e, expavg_credit=%.15e, expavg_time=%f",
848
if (config.debug_credit) {
849
log_messages.printf(MSG_NORMAL,
850
"[credit] updating app version %d:\n", av.id
852
log_messages.printf(MSG_NORMAL,
853
"[credit] pfc.n = %f, pfc.avg = %f, expavg_credit = %f, expavg_time=%f\n",
860
// if pfc_scale has changed (from feeder) reread it
863
"pfc_n=%.15e and abs(expavg_credit-%.15e)<1e-4 and abs(pfc_scale-%.15e)<1e-6",
864
pfc_n_orig, expavg_credit_orig, av.pfc_scale
866
retval = av.update_field(query, clause);
868
if (boinc_db.affected_rows() == 1) break;
869
retval = av.lookup_id(av.id);
872
av.pfc_samples.clear();
873
av.credit_samples.clear();
874
av.credit_times.clear();
875
if (retval) return retval;