Growable NetworkStats object instead of builder.

NetworkStats now grows in place with arraycopy() instead of callers
needing to know record count a priori.  Better growth calculation for
both NetworkStats and NetworkStatsHistory; 50% each time.  Better
estimates of buckets needed in calling services.

Change-Id: I3adbffa0b7407612cc6349d9135a8b4eb63cd440
This commit is contained in:
Jeff Sharkey
2011-06-11 22:16:55 -07:00
parent 6fc33809a7
commit 755000588f
3 changed files with 66 additions and 61 deletions

View File

@@ -23,6 +23,7 @@ import android.util.SparseBooleanArray;
import java.io.CharArrayWriter; import java.io.CharArrayWriter;
import java.io.PrintWriter; import java.io.PrintWriter;
import java.util.Arrays;
import java.util.HashSet; import java.util.HashSet;
/** /**
@@ -48,74 +49,60 @@ public class NetworkStats implements Parcelable {
* generated. * generated.
*/ */
public final long elapsedRealtime; public final long elapsedRealtime;
public final String[] iface; public int size;
public final int[] uid; public String[] iface;
public final long[] rx; public int[] uid;
public final long[] tx; public long[] rx;
public long[] tx;
// TODO: add fg/bg stats once reported by kernel // TODO: add fg/bg stats once reported by kernel
private NetworkStats(long elapsedRealtime, String[] iface, int[] uid, long[] rx, long[] tx) { public NetworkStats(long elapsedRealtime, int initialSize) {
this.elapsedRealtime = elapsedRealtime; this.elapsedRealtime = elapsedRealtime;
this.iface = iface; this.size = 0;
this.uid = uid; this.iface = new String[initialSize];
this.rx = rx; this.uid = new int[initialSize];
this.tx = tx; this.rx = new long[initialSize];
this.tx = new long[initialSize];
} }
public NetworkStats(Parcel parcel) { public NetworkStats(Parcel parcel) {
elapsedRealtime = parcel.readLong(); elapsedRealtime = parcel.readLong();
size = parcel.readInt();
iface = parcel.createStringArray(); iface = parcel.createStringArray();
uid = parcel.createIntArray(); uid = parcel.createIntArray();
rx = parcel.createLongArray(); rx = parcel.createLongArray();
tx = parcel.createLongArray(); tx = parcel.createLongArray();
} }
public static class Builder { public NetworkStats addEntry(String iface, int uid, long rx, long tx) {
private long mElapsedRealtime; if (size >= this.iface.length) {
private final String[] mIface; final int newLength = Math.max(this.iface.length, 10) * 3 / 2;
private final int[] mUid; this.iface = Arrays.copyOf(this.iface, newLength);
private final long[] mRx; this.uid = Arrays.copyOf(this.uid, newLength);
private final long[] mTx; this.rx = Arrays.copyOf(this.rx, newLength);
this.tx = Arrays.copyOf(this.tx, newLength);
private int mIndex = 0;
public Builder(long elapsedRealtime, int size) {
mElapsedRealtime = elapsedRealtime;
mIface = new String[size];
mUid = new int[size];
mRx = new long[size];
mTx = new long[size];
} }
public Builder addEntry(String iface, int uid, long rx, long tx) { this.iface[size] = iface;
mIface[mIndex] = iface; this.uid[size] = uid;
mUid[mIndex] = uid; this.rx[size] = rx;
mRx[mIndex] = rx; this.tx[size] = tx;
mTx[mIndex] = tx; size++;
mIndex++;
return this;
}
public NetworkStats build() { return this;
if (mIndex != mIface.length) {
throw new IllegalArgumentException("unexpected number of entries");
}
return new NetworkStats(mElapsedRealtime, mIface, mUid, mRx, mTx);
}
} }
@Deprecated
public int length() { public int length() {
// length is identical for all fields return size;
return iface.length;
} }
/** /**
* Find first stats index that matches the requested parameters. * Find first stats index that matches the requested parameters.
*/ */
public int findIndex(String iface, int uid) { public int findIndex(String iface, int uid) {
final int length = length(); for (int i = 0; i < size; i++) {
for (int i = 0; i < length; i++) {
if (equal(iface, this.iface[i]) && uid == this.uid[i]) { if (equal(iface, this.iface[i]) && uid == this.uid[i]) {
return i; return i;
} }
@@ -195,9 +182,8 @@ public class NetworkStats implements Parcelable {
} }
// result will have our rows, and elapsed time between snapshots // result will have our rows, and elapsed time between snapshots
final int length = length(); final NetworkStats result = new NetworkStats(deltaRealtime, size);
final NetworkStats.Builder result = new NetworkStats.Builder(deltaRealtime, length); for (int i = 0; i < size; i++) {
for (int i = 0; i < length; i++) {
final String iface = this.iface[i]; final String iface = this.iface[i];
final int uid = this.uid[i]; final int uid = this.uid[i];
@@ -221,7 +207,7 @@ public class NetworkStats implements Parcelable {
} }
} }
return result.build(); return result;
} }
private static boolean equal(Object a, Object b) { private static boolean equal(Object a, Object b) {
@@ -255,6 +241,7 @@ public class NetworkStats implements Parcelable {
/** {@inheritDoc} */ /** {@inheritDoc} */
public void writeToParcel(Parcel dest, int flags) { public void writeToParcel(Parcel dest, int flags) {
dest.writeLong(elapsedRealtime); dest.writeLong(elapsedRealtime);
dest.writeInt(size);
dest.writeStringArray(iface); dest.writeStringArray(iface);
dest.writeIntArray(uid); dest.writeIntArray(uid);
dest.writeLongArray(rx); dest.writeLongArray(rx);

View File

@@ -53,11 +53,15 @@ public class NetworkStatsHistory implements Parcelable {
public long[] tx; public long[] tx;
public NetworkStatsHistory(long bucketDuration) { public NetworkStatsHistory(long bucketDuration) {
this(bucketDuration, 10);
}
public NetworkStatsHistory(long bucketDuration, int initialSize) {
this.bucketDuration = bucketDuration; this.bucketDuration = bucketDuration;
bucketStart = new long[0]; bucketStart = new long[initialSize];
rx = new long[0]; rx = new long[initialSize];
tx = new long[0]; tx = new long[initialSize];
bucketCount = bucketStart.length; bucketCount = 0;
} }
public NetworkStatsHistory(Parcel in) { public NetworkStatsHistory(Parcel in) {
@@ -168,8 +172,8 @@ public class NetworkStatsHistory implements Parcelable {
*/ */
private void insertBucket(int index, long start) { private void insertBucket(int index, long start) {
// create more buckets when needed // create more buckets when needed
if (bucketCount + 1 > bucketStart.length) { if (bucketCount >= bucketStart.length) {
final int newLength = bucketStart.length + 10; final int newLength = Math.max(bucketStart.length, 10) * 3 / 2;
bucketStart = Arrays.copyOf(bucketStart, newLength); bucketStart = Arrays.copyOf(bucketStart, newLength);
rx = Arrays.copyOf(rx, newLength); rx = Arrays.copyOf(rx, newLength);
tx = Arrays.copyOf(tx, newLength); tx = Arrays.copyOf(tx, newLength);

View File

@@ -255,7 +255,7 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
// combine all interfaces that match template // combine all interfaces that match template
final String subscriberId = getActiveSubscriberId(); final String subscriberId = getActiveSubscriberId();
final NetworkStatsHistory combined = new NetworkStatsHistory( final NetworkStatsHistory combined = new NetworkStatsHistory(
mSettings.getNetworkBucketDuration()); mSettings.getNetworkBucketDuration(), estimateNetworkBuckets());
for (InterfaceIdentity ident : mNetworkStats.keySet()) { for (InterfaceIdentity ident : mNetworkStats.keySet()) {
final NetworkStatsHistory history = mNetworkStats.get(ident); final NetworkStatsHistory history = mNetworkStats.get(ident);
if (ident.matchesTemplate(networkTemplate, subscriberId)) { if (ident.matchesTemplate(networkTemplate, subscriberId)) {
@@ -297,9 +297,9 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
} }
} }
final NetworkStats.Builder stats = new NetworkStats.Builder(end - start, 1); final NetworkStats stats = new NetworkStats(end - start, 1);
stats.addEntry(IFACE_ALL, UID_ALL, tx, tx); stats.addEntry(IFACE_ALL, UID_ALL, tx, tx);
return stats.build(); return stats;
} }
} }
@@ -313,7 +313,7 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
ensureUidStatsLoadedLocked(); ensureUidStatsLoadedLocked();
final int size = mUidStats.size(); final int size = mUidStats.size();
final NetworkStats.Builder stats = new NetworkStats.Builder(end - start, size); final NetworkStats stats = new NetworkStats(end - start, size);
long[] total = new long[2]; long[] total = new long[2];
for (int i = 0; i < size; i++) { for (int i = 0; i < size; i++) {
@@ -322,7 +322,7 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
total = history.getTotalData(start, end, total); total = history.getTotalData(start, end, total);
stats.addEntry(IFACE_ALL, uid, total[0], total[1]); stats.addEntry(IFACE_ALL, uid, total[0], total[1]);
} }
return stats.build(); return stats;
} }
} }
@@ -510,9 +510,10 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
// update when no existing, or when bucket duration changed // update when no existing, or when bucket duration changed
NetworkStatsHistory updated = null; NetworkStatsHistory updated = null;
if (existing == null) { if (existing == null) {
updated = new NetworkStatsHistory(bucketDuration); updated = new NetworkStatsHistory(bucketDuration, 10);
} else if (existing.bucketDuration != bucketDuration) { } else if (existing.bucketDuration != bucketDuration) {
updated = new NetworkStatsHistory(bucketDuration); updated = new NetworkStatsHistory(
bucketDuration, estimateResizeBuckets(existing, bucketDuration));
updated.recordEntireHistory(existing); updated.recordEntireHistory(existing);
} }
@@ -531,9 +532,10 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
// update when no existing, or when bucket duration changed // update when no existing, or when bucket duration changed
NetworkStatsHistory updated = null; NetworkStatsHistory updated = null;
if (existing == null) { if (existing == null) {
updated = new NetworkStatsHistory(bucketDuration); updated = new NetworkStatsHistory(bucketDuration, 10);
} else if (existing.bucketDuration != bucketDuration) { } else if (existing.bucketDuration != bucketDuration) {
updated = new NetworkStatsHistory(bucketDuration); updated = new NetworkStatsHistory(
bucketDuration, estimateResizeBuckets(existing, bucketDuration));
updated.recordEntireHistory(existing); updated.recordEntireHistory(existing);
} }
@@ -809,6 +811,18 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
return telephony.getSubscriberId(); return telephony.getSubscriberId();
} }
private int estimateNetworkBuckets() {
return (int) (mSettings.getNetworkMaxHistory() / mSettings.getNetworkBucketDuration());
}
private int estimateUidBuckets() {
return (int) (mSettings.getUidMaxHistory() / mSettings.getUidBucketDuration());
}
private static int estimateResizeBuckets(NetworkStatsHistory existing, long newBucketDuration) {
return (int) (existing.bucketCount * existing.bucketDuration / newBucketDuration);
}
/** /**
* Default external settings that read from {@link Settings.Secure}. * Default external settings that read from {@link Settings.Secure}.
*/ */