forked from I2P_Developers/i2p.i2p
* Router:
- Add new RandomIterator, use in UDP, peer selector, profile organizer - Add a stat to monitor peer selector run time
This commit is contained in:
@@ -11,6 +11,7 @@ package net.i2p.router.networkdb.kademlia;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.TreeSet;
|
||||
@@ -19,6 +20,7 @@ import net.i2p.data.Hash;
|
||||
import net.i2p.data.RouterInfo;
|
||||
import net.i2p.router.RouterContext;
|
||||
import net.i2p.router.peermanager.PeerProfile;
|
||||
import net.i2p.router.util.RandomIterator;
|
||||
import net.i2p.stat.Rate;
|
||||
import net.i2p.util.Log;
|
||||
|
||||
@@ -74,6 +76,7 @@ class FloodfillPeerSelector extends PeerSelector {
|
||||
if (peersToIgnore == null)
|
||||
peersToIgnore = new HashSet(1);
|
||||
peersToIgnore.add(_context.routerHash());
|
||||
// TODO this is very slow
|
||||
FloodfillSelectionCollector matches = new FloodfillSelectionCollector(key, peersToIgnore, maxNumRouters);
|
||||
if (kbuckets == null) return new ArrayList();
|
||||
kbuckets.getAll(matches);
|
||||
@@ -104,6 +107,8 @@ class FloodfillPeerSelector extends PeerSelector {
|
||||
*/
|
||||
private List<Hash> selectFloodfillParticipants(Set<Hash> toIgnore, KBucketSet kbuckets) {
|
||||
if (kbuckets == null) return Collections.EMPTY_LIST;
|
||||
// TODO this is very slow - use profile getPeersByCapability('f') instead
|
||||
_context.statManager().addRateData("netDb.newFSC", 0, 0);
|
||||
FloodfillSelectionCollector matches = new FloodfillSelectionCollector(null, toIgnore, 0);
|
||||
kbuckets.getAll(matches);
|
||||
return matches.getFloodfillParticipants();
|
||||
@@ -320,7 +325,6 @@ class FloodfillPeerSelector extends PeerSelector {
|
||||
* Group 4: Non-floodfills, sorted by closest-to-the-key
|
||||
*/
|
||||
public List<Hash> get(int howMany, boolean preferConnected) {
|
||||
Collections.shuffle(_floodfillMatches, _context.random());
|
||||
List<Hash> rv = new ArrayList(howMany);
|
||||
List<Hash> badff = new ArrayList(howMany);
|
||||
List<Hash> unconnectedff = new ArrayList(howMany);
|
||||
@@ -329,8 +333,8 @@ class FloodfillPeerSelector extends PeerSelector {
|
||||
// Only add in "good" floodfills here...
|
||||
// Let's say published in last 3h and no failed sends in last 30m
|
||||
// (Forever shitlisted ones are excluded in add() above)
|
||||
for (int i = 0; found < howMany && i < _floodfillMatches.size(); i++) {
|
||||
Hash entry = (Hash) _floodfillMatches.get(i);
|
||||
for (Iterator<Hash> iter = new RandomIterator(_floodfillMatches); (found < howMany) && iter.hasNext(); ) {
|
||||
Hash entry = iter.next();
|
||||
RouterInfo info = _context.netDb().lookupRouterInfoLocally(entry);
|
||||
if (info != null && now - info.getPublished() > 3*60*60*1000) {
|
||||
badff.add(entry);
|
||||
@@ -391,6 +395,7 @@ class FloodfillPeerSelector extends PeerSelector {
|
||||
if (peersToIgnore != null && peersToIgnore.contains(Hash.FAKE_HASH)) {
|
||||
// return non-ff
|
||||
peersToIgnore.addAll(selectFloodfillParticipants(peersToIgnore, kbuckets));
|
||||
// TODO this is very slow
|
||||
FloodfillSelectionCollector matches = new FloodfillSelectionCollector(rkey, peersToIgnore, maxNumRouters);
|
||||
kbuckets.getAll(matches);
|
||||
return matches.get(maxNumRouters);
|
||||
|
@@ -42,6 +42,7 @@ class KBucketSet {
|
||||
_context = context;
|
||||
_log = context.logManager().getLog(KBucketSet.class);
|
||||
createBuckets();
|
||||
context.statManager().createRateStat("netDb.KBSGetAllTime", "Time to add all Hashes to the Collector", "NetworkDatabase", new long[] { 60*60*1000 });
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -99,8 +100,10 @@ class KBucketSet {
|
||||
}
|
||||
|
||||
public void getAll(SelectionCollector collector) {
|
||||
long start = _context.clock().now();
|
||||
for (int i = 0; i < _buckets.length; i++)
|
||||
_buckets[i].getEntries(collector);
|
||||
_context.statManager().addRateData("netDb.KBSGetAllTime", _context.clock().now() - start, 0);
|
||||
}
|
||||
|
||||
public int pickBucket(Hash key) {
|
||||
|
@@ -28,6 +28,7 @@ import net.i2p.data.RouterInfo;
|
||||
import net.i2p.router.NetworkDatabaseFacade;
|
||||
import net.i2p.router.RouterContext;
|
||||
import net.i2p.router.tunnel.pool.TunnelPeerSelector;
|
||||
import net.i2p.router.util.RandomIterator;
|
||||
import net.i2p.stat.Rate;
|
||||
import net.i2p.stat.RateStat;
|
||||
import net.i2p.util.Log;
|
||||
@@ -1043,17 +1044,19 @@ public class ProfileOrganizer {
|
||||
private void locked_selectPeers(Map<Hash, PeerProfile> peers, int howMany, Set<Hash> toExclude, Set<Hash> matches) {
|
||||
locked_selectPeers(peers, howMany, toExclude, matches, 0);
|
||||
}
|
||||
|
||||
private void locked_selectPeers(Map<Hash, PeerProfile> peers, int howMany, Set<Hash> toExclude, Set<Hash> matches, int mask) {
|
||||
List all = new ArrayList(peers.keySet());
|
||||
if (toExclude != null)
|
||||
all.removeAll(toExclude);
|
||||
|
||||
all.removeAll(matches);
|
||||
all.remove(_us);
|
||||
Collections.shuffle(all, _random);
|
||||
List<Hash> all = new ArrayList(peers.keySet());
|
||||
Set<Integer> IPSet = new HashSet(8);
|
||||
for (int i = 0; (matches.size() < howMany) && (i < all.size()); i++) {
|
||||
Hash peer = (Hash)all.get(i);
|
||||
// use RandomIterator to avoid shuffling the whole thing
|
||||
for (Iterator<Hash> iter = new RandomIterator(all); (matches.size() < howMany) && iter.hasNext(); ) {
|
||||
Hash peer = iter.next();
|
||||
if (toExclude != null && toExclude.contains(peer))
|
||||
continue;
|
||||
if (matches.contains(peer))
|
||||
continue;
|
||||
if (_us.equals(peer))
|
||||
continue;
|
||||
boolean ok = isSelectable(peer);
|
||||
if (ok) {
|
||||
ok = mask <= 0 || notRestricted(peer, IPSet, mask);
|
||||
|
@@ -34,6 +34,7 @@ import net.i2p.router.RouterContext;
|
||||
import net.i2p.router.transport.Transport;
|
||||
import net.i2p.router.transport.TransportBid;
|
||||
import net.i2p.router.transport.TransportImpl;
|
||||
import net.i2p.router.util.RandomIterator;
|
||||
import net.i2p.util.ConcurrentHashSet;
|
||||
import net.i2p.util.Log;
|
||||
import net.i2p.util.SimpleScheduler;
|
||||
@@ -2326,9 +2327,8 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority
|
||||
|
||||
PeerState pickTestPeer(RemoteHostId dontInclude) {
|
||||
List<PeerState> peers = new ArrayList(_peersByIdent.values());
|
||||
Collections.shuffle(peers, _context.random());
|
||||
for (int i = 0; i < peers.size(); i++) {
|
||||
PeerState peer = peers.get(i);
|
||||
for (Iterator<PeerState> iter = new RandomIterator(peers); iter.hasNext(); ) {
|
||||
PeerState peer = iter.next();
|
||||
if ( (dontInclude != null) && (dontInclude.equals(peer.getRemoteHostId())) )
|
||||
continue;
|
||||
RouterInfo peerInfo = _context.netDb().lookupRouterInfoLocally(peer.getRemotePeer());
|
||||
|
178
router/java/src/net/i2p/router/util/RandomIterator.java
Normal file
178
router/java/src/net/i2p/router/util/RandomIterator.java
Normal file
@@ -0,0 +1,178 @@
|
||||
package net.i2p.router.util;
|
||||
|
||||
/*
|
||||
* Modified from:
|
||||
* http://www.lockergnome.com/awarberg/2007/04/22/random-iterator-in-java/
|
||||
* No license, free to use
|
||||
*/
|
||||
|
||||
//import java.util.ArrayList;
|
||||
import java.util.BitSet;
|
||||
import java.util.List;
|
||||
import java.util.Iterator;
|
||||
import java.util.NoSuchElementException;
|
||||
import java.util.Random;
|
||||
|
||||
import net.i2p.util.RandomSource;
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* This is some Java code I wrote for a school project to save some time when iterating in
|
||||
* random order over a part of list (until some condition becomes true):
|
||||
*
|
||||
* Here is a sample on how to use the code:
|
||||
*
|
||||
<pre>
|
||||
for(Iterator<Object> iter = new RandomIterator<Object>(myObjList); iter.hasNext();){
|
||||
Object o = iter.next();
|
||||
if(someCondition(o) )
|
||||
return o; // iteration stopped early
|
||||
}
|
||||
</pre>
|
||||
*
|
||||
* I wrote it to replace a Collection.shuffle call and this code gave us an overall increase in program execution speed of about 25%.
|
||||
* As the javadoc description says, you are better off calling Collection.shuffle if you need to iterate over the entire list. But if you may stop early this class can save you some time, as it did in our case.
|
||||
*
|
||||
* Provides a random iteration over the given list.
|
||||
*
|
||||
* This effect can be achieved by using Collections.shuffle,
|
||||
* which shuffles the entire collection in linear time.
|
||||
*
|
||||
* If the iteration process may end before all items
|
||||
* are processed, this class may give a speed increase
|
||||
* because the shuffling process is performed as items are requested
|
||||
* rather than in the beginning.
|
||||
*
|
||||
* I2P changes:
|
||||
*<pre>
|
||||
* - Use BitSet instead of boolean[]
|
||||
* - Use I2P RandomSource
|
||||
* - Done check in next(), throw NSEE
|
||||
* - Ensure lower and upper bounds are always clear
|
||||
* - Replace unbounded loop in next(). It is now O(N) time, but now
|
||||
* the iterator will tend to "clump" results and thus is not truly random.
|
||||
* *** This class is not recommended for small Lists,
|
||||
* *** or for iterating through a large portion of a List.
|
||||
* *** Use Collections.shuffle() instead.
|
||||
* - Add test code
|
||||
*</pre>
|
||||
*
|
||||
*/
|
||||
public class RandomIterator<E> implements Iterator<E> {
|
||||
/**
|
||||
* Mapping indicating which items were served (by index).
|
||||
* if served[i] then the item with index i in the list
|
||||
* has already been served.
|
||||
*
|
||||
* Note it is possible to save memory here by using
|
||||
* BitSet rather than a boolean array, however it will
|
||||
* increase the running time slightly.
|
||||
*/
|
||||
private final BitSet served;
|
||||
|
||||
/** The amount of items served so far */
|
||||
private int servedCount = 0;
|
||||
private final List<E> list;
|
||||
private final int LIST_SIZE;
|
||||
|
||||
/**
|
||||
* The random number generator has a great influence
|
||||
* on the running time of this iterator.
|
||||
*
|
||||
* See, for instance,
|
||||
* <a href="http://www.qbrundage.com/michaelb/pubs/essays/random_number_generation" title="http://www.qbrundage.com/michaelb/pubs/essays/random_number_generation" target="_blank">http://www.qbrundage.com/michaelb/pubs/e…</a>
|
||||
* for some implementations, which are faster than java.util.Random.
|
||||
*/
|
||||
private static final Random rand = RandomSource.getInstance();
|
||||
|
||||
/** Used to narrow the range to take random indexes from */
|
||||
private int lower, upper;
|
||||
|
||||
public RandomIterator(List<E> list){
|
||||
this.list = list;
|
||||
LIST_SIZE = list.size();
|
||||
served = new BitSet(LIST_SIZE);
|
||||
upper = LIST_SIZE - 1;
|
||||
}
|
||||
|
||||
public boolean hasNext() {
|
||||
return servedCount < LIST_SIZE;
|
||||
}
|
||||
|
||||
public E next() {
|
||||
if (!hasNext())
|
||||
throw new NoSuchElementException();
|
||||
int range = upper - lower + 1;
|
||||
|
||||
// This has unbounded behavior, even with lower/upper
|
||||
//int index;
|
||||
//do {
|
||||
// index = lower + rand.nextInt(range);
|
||||
//} while (served.get(index));
|
||||
|
||||
// This tends to "clump" results, escpecially toward the end of the iteration.
|
||||
// It also tends to leave the first and last few elements until the end.
|
||||
int start = lower + rand.nextInt(range);
|
||||
int index;
|
||||
if ((start % 2) == 0) // coin flip
|
||||
index = served.nextClearBit(start);
|
||||
else
|
||||
index = previousClearBit(start);
|
||||
if (index < 0)
|
||||
throw new NoSuchElementException("shouldn't happen");
|
||||
servedCount++;
|
||||
served.set(index);
|
||||
|
||||
// check if the range from which random values
|
||||
// are taken can be reduced
|
||||
// I2P - ensure lower and upper are always clear
|
||||
if (hasNext()) {
|
||||
if (index == lower)
|
||||
lower = served.nextClearBit(lower);
|
||||
else if (index == upper)
|
||||
upper = previousClearBit(upper - 1);
|
||||
}
|
||||
return list.get(index);
|
||||
}
|
||||
|
||||
/** just like nextClearBit() */
|
||||
private int previousClearBit(int n) {
|
||||
for (int i = n; i >= lower; i--) {
|
||||
if (!served.get(i)) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws UnsupportedOperationException always
|
||||
*/
|
||||
public void remove() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
/*****
|
||||
public static void main(String[] args) {
|
||||
System.out.println("\n testing with 0");
|
||||
test(0);
|
||||
System.out.println("\n testing with 1");
|
||||
test(1);
|
||||
System.out.println("\n testing with 2");
|
||||
test(2);
|
||||
System.out.println("\n testing with 1000");
|
||||
test(1000);
|
||||
}
|
||||
|
||||
public static void test(int n) {
|
||||
List<Integer> l = new ArrayList(n);
|
||||
for (int i = 0; i < n; i++) {
|
||||
l.add(Integer.valueOf(i));
|
||||
}
|
||||
for (Iterator<Integer> iter = new RandomIterator(l); iter.hasNext(); ) {
|
||||
System.out.println(iter.next().toString());
|
||||
}
|
||||
}
|
||||
*****/
|
||||
}
|
6
router/java/src/net/i2p/router/util/package.html
Normal file
6
router/java/src/net/i2p/router/util/package.html
Normal file
@@ -0,0 +1,6 @@
|
||||
<html><body>
|
||||
<p>
|
||||
These classes define the several useful utilities used
|
||||
throughout the router.
|
||||
</p>
|
||||
</body></html>
|
Reference in New Issue
Block a user