/******************************************************************************/
/* */
/* X r d C l i e n t R e a d A h e a d . c c */
/* */
/* Author: Fabrizio Furano (CERN IT-DM, 2009) */
/* */
/* This file is part of the XRootD software suite. */
/* */
/* XRootD is free software: you can redistribute it and/or modify it under */
/* the terms of the GNU Lesser General Public License as published by the */
/* Free Software Foundation, either version 3 of the License, or (at your */
/* option) any later version. */
/* */
/* XRootD 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 Lesser General Public */
/* License for more details. */
/* */
/* You should have received a copy of the GNU Lesser General Public License */
/* along with XRootD in a file called COPYING.LESSER (LGPL license) and file */
/* COPYING (GPL license). If not, see . */
/* */
/* The copyright holder's institutional names and contributor's names may not */
/* be used to endorse or promote products derived from this software without */
/* specific prior written permission of the institution or contributor. */
/******************************************************************************/
/////////////////////////////////////////////////////////////////////////
// //
// Classes to implement a selectable read ahead decision maker //
// //
//////////////////////////////////////////////////////////////////////////
#include "XrdClientReadAhead.hh"
#include "XrdClientConst.hh"
#include "XrdClientVector.hh"
bool XrdClientReadAheadMgr::TrimReadRequest(long long &offs, long &len, long rasize, long blksz) {
if (!blksz) return true;
long long newoffs;
long newlen;
long long lastbyte;
newoffs = (long long)(offs / blksz);
newoffs *= blksz;
lastbyte = offs+len+blksz-1;
lastbyte = (long long)(lastbyte / blksz);
lastbyte *= blksz;
newlen = lastbyte-newoffs;
// std::cerr << "Trim: " << offs << "," << len << " --> " << newoffs << "," << newlen << std::endl;
offs = newoffs;
len = newlen;
return true;
}
// -----------------------------------------------------------------------
// A basic implementation. Purely sequential read ahead
class XrdClientReadAhead_pureseq : public XrdClientReadAheadMgr {
protected:
long long RALast;
public:
XrdClientReadAhead_pureseq() {
RALast = 0;
}
virtual int GetReadAheadHint(long long offset, long len, long long &raoffset, long &ralen, long blksz);
virtual int Reset() {
RALast = 0;
return 0;
}
};
int XrdClientReadAhead_pureseq::GetReadAheadHint(long long offset, long len, long long &raoffset, long &ralen, long blksz) {
if (!blksz) blksz = 128*1024;
// We read ahead only if (offs+len) lies in an interval of RALast not bigger than the readahead size
if ( (RALast - (offset+len) < RASize) &&
(RALast - (offset+len) > -RASize) &&
(RASize > 0) ) {
// This is a HIT case. Async readahead will try to put some data
// in advance into the cache. The higher the araoffset will be,
// the best chances we have not to cause overhead
raoffset = xrdmax(RALast, offset + len);
ralen = xrdmin(RASize,
offset + len + RASize - raoffset);
if (ralen > 0) {
TrimReadRequest(raoffset, ralen, RASize, blksz);
RALast = raoffset + ralen;
return 0;
}
}
return 1;
};
// -----------------------------------------------------------------------
// Another read ahead schema. A window centered on the recent average slides through the file
// following the stream of the requests
class XrdClientReadAhead_slidingavg : public XrdClientReadAheadMgr {
protected:
long long RALast;
long long LastOffsSum, LastOffsSum2;
long long LastOffsSumsq, LastOffsSumsq2;
XrdClientVector LastOffs;
XrdClientVector LastAvgApprox, LastAvgApprox2;
public:
XrdClientReadAhead_slidingavg() {
RALast = 0;
LastOffsSum = LastOffsSum2 = 0;
LastOffsSumsq = LastOffsSumsq2 = 0;
}
virtual int GetReadAheadHint(long long offset, long len, long long &raoffset, long &ralen, long blksz);
virtual int Reset() {
RALast = 0;
LastOffsSum = LastOffsSum2 = 0;
LastOffsSumsq = LastOffsSumsq2 = 0;
return 0;
}
};
int XrdClientReadAhead_slidingavg::GetReadAheadHint(long long offset, long len, long long &raoffset, long &ralen, long blksz) {
if (!blksz) blksz = 128*1024;
// Keep the sums up to date, together with the max array size and the sumsqs
LastOffsSum += offset;
LastOffsSum2 += offset;
LastOffs.Push_back(offset);
int loSZ = LastOffs.GetSize(), loSZ50 = loSZ-50;
if (loSZ >= 50) {
LastOffsSum2 -= LastOffs[loSZ50];
}
if (LastOffs.GetSize() >= 1000) {
LastOffsSum -= LastOffs[0];
}
long long lastavg = LastOffsSum / LastOffs.GetSize();
long long lastavg2 = LastOffsSum2 / xrdmin(LastOffs.GetSize(), 50);
// Now the approximations of the std deviation, shifted right by some positions to avoid overflows
long long sqerr = (((offset >> 20) - (lastavg >> 20))*((offset >> 20) - (lastavg >> 20)));
LastOffsSumsq += sqerr;
long long sqerr2 = ( ((offset - lastavg2) >> 20) * ((offset - lastavg2) >> 20) );
LastOffsSumsq2 += sqerr2;
LastAvgApprox.Push_back(sqerr);
LastAvgApprox2.Push_back(sqerr2);
if (LastAvgApprox2.GetSize() >= 50) {
LastOffsSumsq2 -= LastAvgApprox2[0];
LastAvgApprox2.Erase(0);
}
if (LastAvgApprox.GetSize() >= 1000) {
LastOffsSumsq -= LastAvgApprox[0];
LastAvgApprox.Erase(0);
}
if (LastOffs.GetSize() >= 1000) {
LastOffs.Erase(0);
}
long long stddevi = LastOffsSumsq / LastOffs.GetSize();
long long stddevi2 = LastOffsSumsq2 / LastAvgApprox2.GetSize();
//std::cerr << "offs:" << offset << " avg:" << lastavg << " avg2:" << lastavg2 << " devi:" << stddevi << " devi2:" << stddevi2;
// To read ahead, we want at least a few samples
//if ( LastOffs.GetSize() < 10 ) return 1;
// If the more stable avg is usable, use it
if ((stddevi << 20) < 3*RASize) {
raoffset = xrdmax(RALast, lastavg - RASize/2);
ralen = xrdmin(RASize,
lastavg + RASize/2 - raoffset);
if (ralen > (1024*1024)) {
TrimReadRequest(raoffset, ralen, RASize, blksz);
RALast = raoffset + ralen;
//std::cerr << " raoffs:" << raoffset << " ralen:" << ralen << " Got avg" << std::endl;
return 0;
}
//std::cerr << std::endl;
} else
// If the less stable avg is usable, use it
if ((stddevi2 << 20) < 3*RASize) {
raoffset = xrdmax(RALast, lastavg2 - RASize/2);
ralen = xrdmin(RASize,
lastavg2 + RASize/2 - raoffset);
if (ralen > (1024*1024)) {
TrimReadRequest(raoffset, ralen, RASize, blksz);
RALast = raoffset + ralen;
//std::cerr << " raoffs:" << raoffset << " ralen:" << ralen << " Got avg2" << std::endl;
return 0;
}
//std::cerr << std::endl;
}
//std::cerr << std::endl;
return 1;
};
// ------------------------------------------------------
XrdClientReadAheadMgr *XrdClientReadAheadMgr::CreateReadAheadMgr(XrdClient_RAStrategy strategy) {
XrdClientReadAheadMgr *ramgr = 0;
switch (strategy) {
case RAStr_none:
break;
case RAStr_pureseq: {
ramgr = new XrdClientReadAhead_pureseq();
break;
}
case RAStr_SlidingAvg: {
ramgr = new XrdClientReadAhead_slidingavg();
break;
}
}
if (ramgr) ramgr->currstrategy = strategy;
return ramgr;
}