source: trunk/libtransmission/bandwidth.c @ 7160

Last change on this file since 7160 was 7160, checked in by charles, 12 years ago

(libT) make the the speed measurements a little more accurate

File size: 9.1 KB
Line 
1/*
2 * This file Copyright (C) 2008 Charles Kerr <charles@rebelbase.com>
3 *
4 * This file is licensed by the GPL version 2.  Works owned by the
5 * Transmission project are granted a special exemption to clause 2(b)
6 * so that the bulk of its code can remain under the MIT license.
7 * This exemption does not extend to derived works not owned by
8 * the Transmission project.
9 *
10 * $Id:$
11 */
12
13#include <assert.h>
14#include <limits.h>
15
16#include "event.h"
17
18#include "transmission.h"
19#include "bandwidth.h"
20#include "iobuf.h"
21#include "ptrarray.h"
22#include "utils.h"
23
24/***
25****
26***/
27
28enum
29{
30    HISTORY_MSEC = 2000,
31    INTERVAL_MSEC = HISTORY_MSEC,
32    GRANULARITY_MSEC = 40,
33    HISTORY_SIZE = ( INTERVAL_MSEC / GRANULARITY_MSEC ),
34    MAGIC_NUMBER = 43143
35};
36
37struct bratecontrol
38{
39    int newest;
40    struct { uint64_t date, size; } transfers[HISTORY_SIZE];
41};
42
43static float
44getSpeed( const struct bratecontrol * r, int interval_msec )
45{
46    uint64_t       bytes = 0;
47    const uint64_t cutoff = tr_date ( ) - interval_msec;
48    int            i = r->newest;
49
50    for( ;; )
51    {
52        if( r->transfers[i].date <= cutoff )
53            break;
54
55        bytes += r->transfers[i].size;
56
57        if( --i == -1 ) i = HISTORY_SIZE - 1; /* circular history */
58        if( i == r->newest ) break; /* we've come all the way around */
59    }
60
61    return ( bytes / 1024.0 ) * ( 1000.0 / interval_msec );
62}
63
64static void
65bytesUsed( struct bratecontrol * r, size_t size )
66{
67    const uint64_t now = tr_date ( );
68
69    if( r->transfers[r->newest].date + GRANULARITY_MSEC >= now )
70        r->transfers[r->newest].size += size;
71    else
72    {
73        if( ++r->newest == HISTORY_SIZE ) r->newest = 0;
74        r->transfers[r->newest].date = now;
75        r->transfers[r->newest].size = size;
76    }
77}
78
79/******
80*******
81*******
82******/
83
84struct tr_band
85{
86    unsigned int isLimited : 1;
87    unsigned int honorParentLimits : 1;
88    size_t bytesLeft;
89    double desiredSpeed;
90    struct bratecontrol raw;
91    struct bratecontrol piece;
92};
93
94struct tr_bandwidth
95{
96    struct tr_band band[2];
97    struct tr_bandwidth * parent;
98    int magicNumber;
99    tr_session * session;
100    tr_ptrArray * children; /* struct tr_bandwidth */
101    tr_ptrArray * iobufs; /* struct tr_iobuf */
102};
103
104/***
105****
106***/
107
108static int
109comparePointers( const void * a, const void * b )
110{
111    return a - b;
112}
113
114static int
115isBandwidth( const tr_bandwidth * b )
116{
117    return ( b != NULL ) && ( b->magicNumber == MAGIC_NUMBER );
118}
119
120static int
121isDirection( const tr_direction dir )
122{
123    return ( dir == TR_UP ) || ( dir == TR_DOWN );
124}
125
126/***
127****
128***/
129
130tr_bandwidth*
131tr_bandwidthNew( tr_session * session, tr_bandwidth * parent )
132{
133    tr_bandwidth * b = tr_new0( tr_bandwidth, 1 );
134    b->session = session;
135    b->children = tr_ptrArrayNew( );
136    b->iobufs = tr_ptrArrayNew( );
137    b->magicNumber = MAGIC_NUMBER;
138    b->band[TR_UP].honorParentLimits = 1;
139    b->band[TR_DOWN].honorParentLimits = 1;
140    tr_bandwidthSetParent( b, parent );
141    return b;
142}
143
144void
145tr_bandwidthFree( tr_bandwidth * b )
146{
147    assert( isBandwidth( b ) );
148
149    tr_bandwidthSetParent( b, NULL );
150    tr_ptrArrayFree( b->iobufs, NULL );
151    tr_ptrArrayFree( b->children, NULL );
152    b->magicNumber = 0xDEAD;
153    tr_free( b );
154}
155
156/***
157****
158***/
159
160void
161tr_bandwidthSetParent( tr_bandwidth  * b,
162                       tr_bandwidth  * parent )
163{
164    assert( isBandwidth( b ) );
165    assert( b != parent );
166
167    if( b->parent )
168    {
169        assert( isBandwidth( b->parent ) );
170
171        tr_ptrArrayRemoveSorted( b->parent->children, b, comparePointers );
172        b->parent= NULL;
173    }
174
175    if( parent )
176    {
177        assert( isBandwidth( parent ) );
178        assert( parent->parent != b );
179
180        tr_ptrArrayInsertSorted( parent->children, b, comparePointers );
181        b->parent = parent;
182    }
183}
184
185void
186tr_bandwidthHonorParentLimits( tr_bandwidth  * b,
187                               tr_direction    dir,
188                               int             honorParentLimits )
189{
190    assert( isBandwidth( b ) );
191    assert( isDirection( dir ) );
192
193    b->band[dir].honorParentLimits = honorParentLimits != 0;
194}
195
196/***
197****
198***/
199
200void
201tr_bandwidthSetDesiredSpeed( tr_bandwidth  * b,
202                             tr_direction    dir,
203                             double          desiredSpeed )
204{
205    assert( isBandwidth( b ) );
206    assert( isDirection( dir ) );
207
208    b->band[dir].desiredSpeed = desiredSpeed; 
209}
210
211double
212tr_bandwidthGetDesiredSpeed( const tr_bandwidth  * b,
213                             tr_direction          dir )
214{
215    assert( isBandwidth( b ) );
216    assert( isDirection( dir ) );
217
218    return b->band[dir].desiredSpeed;
219}
220
221void
222tr_bandwidthSetLimited( tr_bandwidth  * b,
223                        tr_direction    dir,
224                        int             isLimited )
225{
226    assert( isBandwidth( b ) );
227    assert( isDirection( dir ) );
228
229    b->band[dir].isLimited = isLimited != 0;
230}
231
232int
233tr_bandwidthIsLimited( const tr_bandwidth  * b,
234                       tr_direction          dir )
235{
236    assert( isBandwidth( b ) );
237    assert( isDirection( dir ) );
238
239    return b->band[dir].isLimited != 0;
240}
241
242#if 1
243#define DEBUG_DIRECTION TR_UP
244#endif
245
246void
247tr_bandwidthAllocate( tr_bandwidth  * b,
248                      tr_direction    dir,
249                      int             period_msec )
250{
251    double clamped = 0;
252
253    assert( isBandwidth( b ) );
254    assert( isDirection( dir ) );
255
256    if( b->band[dir].isLimited )
257    {
258        const double currentSpeed = tr_bandwidthGetPieceSpeed( b, dir ); /* KiB/s */
259        const double desiredSpeed = b->band[dir].desiredSpeed;           /* KiB/s */
260        const double pulseCount = HISTORY_MSEC / (double)period_msec;
261        const double nextPulseSpeed = desiredSpeed * ( pulseCount + 1 ) - ( currentSpeed * pulseCount );
262        b->band[dir].bytesLeft = nextPulseSpeed * 1024.0 * period_msec / 1000.0;
263
264#ifdef DEBUG_DIRECTION
265        if( dir == DEBUG_DIRECTION )
266                fprintf( stderr, "bandwidth %p currentPieceSpeed(%5.2f of %5.2f) desiredSpeed(%5.2f), allocating %5.2f\n",
267                         b, currentSpeed, tr_bandwidthGetRawSpeed( b, dir ), desiredSpeed,
268                         b->band[dir].bytesLeft/1024.0 );
269#endif
270    }
271
272    /* notify the io buffers that there's more bandwidth available */
273    if( !b->band[dir].isLimited || ( b->band[dir].bytesLeft > 0 ) )
274    {
275        int i, n=0;
276        short what = dir==TR_UP ? EV_WRITE : EV_READ;
277        struct tr_iobuf ** iobufs = (struct tr_iobuf**) tr_ptrArrayPeek( b->iobufs, &n );
278#ifdef DEBUG_DIRECTION
279if( ( dir == DEBUG_DIRECTION ) && ( n > 1 ) )
280fprintf( stderr, "bandwidth %p has %d iobufs\n", b, n );
281#endif
282        for( i=0; i<n; ++i )
283            tr_iobuf_enable( iobufs[i], what );
284    }
285
286    /* all children should reallocate too */
287    if( 1 ) {
288        int i, n=0;
289        struct tr_bandwidth ** children = (struct tr_bandwidth**) tr_ptrArrayPeek( b->children, &n );
290        for( i=0; i<n; ++i )
291            tr_bandwidthAllocate( children[i], dir, period_msec );
292    }
293}
294
295/***
296****
297***/
298
299void
300tr_bandwidthAddBuffer( tr_bandwidth        * b,
301                       struct tr_iobuf     * iobuf )
302{
303    assert( isBandwidth( b ) );
304    assert( iobuf );
305
306    tr_ptrArrayInsertSorted( b->iobufs, iobuf, comparePointers );
307}
308
309void
310tr_bandwidthRemoveBuffer( tr_bandwidth        * b,
311                          struct tr_iobuf     * iobuf )
312{
313    assert( isBandwidth( b ) );
314    assert( iobuf );
315
316    tr_ptrArrayRemoveSorted( b->iobufs, iobuf, comparePointers );
317}
318
319/***
320****
321***/
322
323size_t
324tr_bandwidthClamp( const tr_bandwidth  * b,
325                   tr_direction          dir,
326                   size_t                byteCount )
327{
328    assert( isBandwidth( b ) );
329    assert( isDirection( dir ) );
330
331    if( b )
332    {
333        if( b->band[dir].isLimited )
334            byteCount = MIN( byteCount, b->band[dir].bytesLeft );
335
336        if( b->parent && b->band[dir].honorParentLimits )
337            byteCount = tr_bandwidthClamp( b->parent, dir, byteCount );
338    }
339
340    return byteCount;
341}
342
343double
344tr_bandwidthGetRawSpeed( const tr_bandwidth * b, tr_direction dir )
345{
346    assert( isBandwidth( b ) );
347    assert( isDirection( dir ) );
348
349    return getSpeed( &b->band[dir].raw, HISTORY_MSEC );
350}
351
352double
353tr_bandwidthGetPieceSpeed( const tr_bandwidth * b, tr_direction dir )
354{
355    assert( isBandwidth( b ) );
356    assert( isDirection( dir ) );
357
358    return getSpeed( &b->band[dir].piece, HISTORY_MSEC );
359}
360
361void
362tr_bandwidthUsed( tr_bandwidth  * b,
363                  tr_direction    dir,
364                  size_t          byteCount,
365                  int             isPieceData )
366{
367    struct tr_band * band;
368    size_t oldBytesLeft;
369
370    assert( isBandwidth( b ) );
371    assert( isDirection( dir ) );
372
373    band = &b->band[dir];
374
375    oldBytesLeft = band->bytesLeft;
376
377    if( band->isLimited && isPieceData )
378        band->bytesLeft -= MIN( band->bytesLeft, byteCount );
379
380#ifdef DEBUG_DIRECTION
381if( ( dir == DEBUG_DIRECTION ) && ( band->isLimited ) )
382fprintf( stderr, "%p consumed %5zu bytes of %5s data... was %6zu, now %6zu left\n",
383         b, byteCount, (isPieceData?"piece":"raw"), oldBytesLeft, band->bytesLeft );
384#endif
385
386    bytesUsed( &band->raw, byteCount );
387
388    if( isPieceData )
389        bytesUsed( &band->piece, byteCount );
390
391    if( b->parent != NULL )
392        tr_bandwidthUsed( b->parent, dir, byteCount, isPieceData );
393}
Note: See TracBrowser for help on using the repository browser.