From 8e2849b7e5ed242d380f12c5b8ab985b15dcf590 Mon Sep 17 00:00:00 2001 From: zzz Date: Thu, 8 Mar 2007 18:55:17 +0000 Subject: [PATCH] 2007-03-08 zzz * i2psnark changes to improve upload performance: * Implement total uploader limit (10) * Don't timeout non-piece messages out * Change chunk size to 32K (was 64K) * Change request limit to 64K (was 256K) * i2psnark: Disconnect from seeds when complete --- .../java/src/org/klomp/snark/Peer.java | 15 +++++++ .../src/org/klomp/snark/PeerCheckerTask.java | 27 ++++++++----- .../org/klomp/snark/PeerConnectionOut.java | 4 +- .../src/org/klomp/snark/PeerCoordinator.java | 39 ++++++++++++++++--- .../java/src/org/klomp/snark/PeerState.java | 5 ++- .../java/src/org/klomp/snark/Snark.java | 23 ++++++++++- history.txt | 13 ++++++- .../src/net/i2p/router/RouterVersion.java | 4 +- 8 files changed, 109 insertions(+), 21 deletions(-) diff --git a/apps/i2psnark/java/src/org/klomp/snark/Peer.java b/apps/i2psnark/java/src/org/klomp/snark/Peer.java index 221c5366cb..6ad6971ae4 100644 --- a/apps/i2psnark/java/src/org/klomp/snark/Peer.java +++ b/apps/i2psnark/java/src/org/klomp/snark/Peer.java @@ -507,6 +507,21 @@ public class Peer implements Comparable return count; } + /** + * Return if a peer is a seeder + * Quite inefficient - a byte lookup table or counter in Bitfield would be much better + */ + public boolean isCompleted() + { + PeerState s = state; + if (s == null || s.bitfield == null) + return false; + for (int i = 0; i < s.bitfield.size(); i++) + if (!s.bitfield.get(i)) + return false; + return true; + } + /** * Push the total uploaded/downloaded onto a RATE_DEPTH deep stack */ diff --git a/apps/i2psnark/java/src/org/klomp/snark/PeerCheckerTask.java b/apps/i2psnark/java/src/org/klomp/snark/PeerCheckerTask.java index d944434540..a53304e933 100644 --- a/apps/i2psnark/java/src/org/klomp/snark/PeerCheckerTask.java +++ b/apps/i2psnark/java/src/org/klomp/snark/PeerCheckerTask.java @@ -41,6 +41,17 @@ class PeerCheckerTask extends TimerTask { synchronized(coordinator.peers) { + Iterator it = coordinator.peers.iterator(); + if ((!it.hasNext()) || coordinator.halted()) { + coordinator.peerCount = 0; + coordinator.interestedAndChoking = 0; + coordinator.setRateHistory(0, 0); + coordinator.uploaders = 0; + if (coordinator.halted()) + cancel(); + return; + } + // Calculate total uploading and worst downloader. long worstdownload = Long.MAX_VALUE; Peer worstDownloader = null; @@ -56,8 +67,7 @@ class PeerCheckerTask extends TimerTask // Keep track of peers we remove now, // we will add them back to the end of the list. List removed = new ArrayList(); - - Iterator it = coordinator.peers.iterator(); + int uploadLimit = coordinator.allowedUploaders(); while (it.hasNext()) { Peer peer = (Peer)it.next(); @@ -100,8 +110,9 @@ class PeerCheckerTask extends TimerTask // If we are at our max uploaders and we have lots of other // interested peers try to make some room. // (Note use of coordinator.uploaders) - if (coordinator.uploaders >= PeerCoordinator.MAX_UPLOADERS - && coordinator.interestedAndChoking > 0 + if (((coordinator.uploaders == uploadLimit + && coordinator.interestedAndChoking > 0) + || coordinator.uploaders > uploadLimit) && !peer.isChoking()) { // Check if it still wants pieces from us. @@ -186,8 +197,9 @@ class PeerCheckerTask extends TimerTask coordinator.uploaders = uploaders; // Remove the worst downloader if needed. (uploader if seeding) - if (uploaders >= PeerCoordinator.MAX_UPLOADERS - && coordinator.interestedAndChoking > 0 + if (((uploaders == uploadLimit + && coordinator.interestedAndChoking > 0) + || uploaders > uploadLimit) && worstDownloader != null) { if (Snark.debug >= Snark.DEBUG) @@ -216,8 +228,5 @@ class PeerCheckerTask extends TimerTask coordinator.setRateHistory(uploaded, downloaded); } - if (coordinator.halted()) { - cancel(); - } } } diff --git a/apps/i2psnark/java/src/org/klomp/snark/PeerConnectionOut.java b/apps/i2psnark/java/src/org/klomp/snark/PeerConnectionOut.java index 12c0f7c681..14527bd323 100644 --- a/apps/i2psnark/java/src/org/klomp/snark/PeerConnectionOut.java +++ b/apps/i2psnark/java/src/org/klomp/snark/PeerConnectionOut.java @@ -208,10 +208,12 @@ class PeerConnectionOut implements Runnable /** * Adds a message to the sendQueue and notifies the method waiting * on the sendQueue to change. + * If a PIECE message only, add a timeout. */ private void addMessage(Message m) { - SimpleTimer.getInstance().addEvent(new RemoveTooSlow(m), SEND_TIMEOUT); + if (m.type == Message.PIECE) + SimpleTimer.getInstance().addEvent(new RemoveTooSlow(m), SEND_TIMEOUT); synchronized(sendQueue) { sendQueue.add(m); diff --git a/apps/i2psnark/java/src/org/klomp/snark/PeerCoordinator.java b/apps/i2psnark/java/src/org/klomp/snark/PeerCoordinator.java index 42029c7a61..fccef5278d 100644 --- a/apps/i2psnark/java/src/org/klomp/snark/PeerCoordinator.java +++ b/apps/i2psnark/java/src/org/klomp/snark/PeerCoordinator.java @@ -170,7 +170,7 @@ public class PeerCoordinator implements PeerListener setRate(down, downloaded_old); } - private void setRate(long val, long array[]) + private static void setRate(long val, long array[]) { synchronized(array) { for (int i = RATE_DEPTH-1; i > 0; i--) @@ -352,6 +352,7 @@ public class PeerCoordinator implements PeerListener synchronized (peers) { int count = 0; int unchokedCount = 0; + int maxUploaders = allowedUploaders(); Iterator it = peers.iterator(); while (it.hasNext()) { @@ -360,7 +361,7 @@ public class PeerCoordinator implements PeerListener if (peer.isChoking() && peer.isInterested()) { count++; - if (uploaders < MAX_UPLOADERS) + if (uploaders < maxUploaders) { if (!peer.isChoked()) interested.add(unchokedCount++, peer); @@ -370,7 +371,7 @@ public class PeerCoordinator implements PeerListener } } - while (uploaders < MAX_UPLOADERS && interested.size() > 0) + while (uploaders < maxUploaders && interested.size() > 0) { Peer peer = (Peer)interested.remove(0); if (_log.shouldLog(Log.DEBUG)) @@ -586,14 +587,27 @@ public class PeerCoordinator implements PeerListener } // Announce to the world we have it! + // Disconnect from other seeders when we get the last piece synchronized(peers) { + List toDisconnect = new ArrayList(); Iterator it = peers.iterator(); while (it.hasNext()) { Peer p = (Peer)it.next(); if (p.isConnected()) - p.have(piece); + { + if (completed() && p.isCompleted()) + toDisconnect.add(p); + else + p.have(piece); + } + } + it = toDisconnect.iterator(); + while (it.hasNext()) + { + Peer p = (Peer)it.next(); + p.disconnect(true); } } @@ -615,7 +629,7 @@ public class PeerCoordinator implements PeerListener { synchronized(peers) { - if (uploaders < MAX_UPLOADERS) + if (uploaders < allowedUploaders()) { if(peer.isChoking()) { @@ -783,5 +797,20 @@ public class PeerCoordinator implements PeerListener for (int i = 0; arr[i] >= 0; i++) markUnrequestedIfOnlyOne(peer, arr[i]); } + + /** Return number of allowed uploaders for this torrent. + ** Check with Snark to see if we are over the total upload limit. + */ + public int allowedUploaders() + { + if (Snark.overUploadLimit(uploaders)) { + if (_log.shouldLog(Log.DEBUG)) + _log.debug("Over limit, uploaders was: " + uploaders); + return uploaders - 1; + } else if (uploaders < MAX_UPLOADERS) + return uploaders + 1; + else + return MAX_UPLOADERS; + } } diff --git a/apps/i2psnark/java/src/org/klomp/snark/PeerState.java b/apps/i2psnark/java/src/org/klomp/snark/PeerState.java index 7f8475a659..dd38ac357b 100644 --- a/apps/i2psnark/java/src/org/klomp/snark/PeerState.java +++ b/apps/i2psnark/java/src/org/klomp/snark/PeerState.java @@ -63,7 +63,8 @@ class PeerState private boolean resend = false; private final static int MAX_PIPELINE = 2; - private final static int PARTSIZE = 64*1024; // default was 16K, i2p-bt uses 64KB + private final static int PARTSIZE = 32*1024; // Snark was 16K, i2p-bt uses 64KB + private final static int MAX_PARTSIZE = 64*1024; // Don't let anybody request more than this PeerState(Peer peer, PeerListener listener, MetaInfo metainfo, PeerConnectionIn in, PeerConnectionOut out) @@ -173,7 +174,7 @@ class PeerState || begin < 0 || begin > metainfo.getPieceLength(piece) || length <= 0 - || length > 4*PARTSIZE) + || length > MAX_PARTSIZE) { // XXX - Protocol error -> disconnect? if (_log.shouldLog(Log.WARN)) diff --git a/apps/i2psnark/java/src/org/klomp/snark/Snark.java b/apps/i2psnark/java/src/org/klomp/snark/Snark.java index ca0c91e159..5e01d0264f 100644 --- a/apps/i2psnark/java/src/org/klomp/snark/Snark.java +++ b/apps/i2psnark/java/src/org/klomp/snark/Snark.java @@ -287,7 +287,7 @@ public class Snark /* * Don't start a tunnel if the torrent isn't going to be started. * If we are starting, - * startTorrent() will call trackerclient.start() which will force a connect. + * startTorrent() will force a connect. * boolean ok = I2PSnarkUtil.instance().connect(); if (!ok) fatal("Unable to connect to I2P"); @@ -368,6 +368,9 @@ public class Snark activity = "Checking storage"; storage = new Storage(meta, slistener); storage.check(rootDataDir); + // have to figure out when to reopen + // if (!start) + // storage.close(); } catch (IOException ioe) { @@ -751,4 +754,22 @@ public class Snark public interface CompleteListener { public void torrentComplete(Snark snark); } + + /** Maintain a total uploader cap + ** This should be configurable + */ + private final static int MAX_TOTAL_UPLOADERS = 10; + public static boolean overUploadLimit(int uploaders) { + PeerCoordinatorSet coordinators = PeerCoordinatorSet.instance(); + if (coordinators == null || uploaders <= 0) + return false; + int totalUploaders = 0; + for (Iterator iter = coordinators.iterator(); iter.hasNext(); ) { + PeerCoordinator c = (PeerCoordinator)iter.next(); + if (!c.halted()) + totalUploaders += c.uploaders; + } + Snark.debug("Total uploaders: " + totalUploaders, Snark.DEBUG); + return totalUploaders > MAX_TOTAL_UPLOADERS; + } } diff --git a/history.txt b/history.txt index e2826046b7..556478a577 100644 --- a/history.txt +++ b/history.txt @@ -1,4 +1,15 @@ -$Id: history.txt,v 1.555 2007-03-03 15:30:52 zzz Exp $ +$Id: history.txt,v 1.556 2007-03-07 00:11:46 zzz Exp $ + +2007-03-08 zzz + * i2psnark changes to improve upload performance: + * Implement total uploader limit (10) + * Don't timeout non-piece messages out + * Change chunk size to 32K (was 64K) + * Change request limit to 64K (was 256K) + * i2psnark: Disconnect from seeds when complete + +2007-03-07 zzz + * Remove dynamic router keys from config.jsp 2007-03-07 zzz * Streaming lib changes to improve upstream performance during congestion: diff --git a/router/java/src/net/i2p/router/RouterVersion.java b/router/java/src/net/i2p/router/RouterVersion.java index 8259a5a08b..008d80e64b 100644 --- a/router/java/src/net/i2p/router/RouterVersion.java +++ b/router/java/src/net/i2p/router/RouterVersion.java @@ -15,9 +15,9 @@ import net.i2p.CoreVersion; * */ public class RouterVersion { - public final static String ID = "$Revision: 1.491 $ $Date: 2007-03-03 15:30:52 $"; + public final static String ID = "$Revision: 1.492 $ $Date: 2007-03-07 00:11:45 $"; public final static String VERSION = "0.6.1.27"; - public final static long BUILD = 5; + public final static long BUILD = 6; public static void main(String args[]) { System.out.println("I2P Router version: " + VERSION + "-" + BUILD); System.out.println("Router ID: " + RouterVersion.ID);