[peerdist] Assume that most recently discovered peer can be reused

The peer discovery time has a significant impact on the overall
PeerDist download speed, since each block requires an individual
discovery attempt.  In most cases, a peer that responds for block N
will turn out to also respond for block N+1.

Assume that the most recently discovered peer (for any block) probably
has a copy of the next block to be discovered, thereby allowing the
peer download attempt to begin immediately.

In the case that this assumption is incorrect, the existing error
recovery path will allow for fallback to newly discovered peers (or to
the origin server).

Suggested-by: Andreas Hammarskjöld <junior@2PintSoftware.com>
Signed-off-by: Michael Brown <mcb30@ipxe.org>
diff --git a/src/net/peerdisc.c b/src/net/peerdisc.c
index 55e3f7f..d7e0d29 100644
--- a/src/net/peerdisc.c
+++ b/src/net/peerdisc.c
@@ -73,6 +73,9 @@
  */
 unsigned int peerdisc_timeout_secs = PEERDISC_DEFAULT_TIMEOUT_SECS;
 
+/** Most recently discovered peer (for any block) */
+static char *peerdisc_recent;
+
 /** Hosted cache server */
 static char *peerhost;
 
@@ -383,6 +386,7 @@
 	struct peerdisc_peer *peer;
 	struct peerdisc_client *peerdisc;
 	struct peerdisc_client *tmp;
+	char *recent;
 
 	/* Ignore duplicate peers */
 	list_for_each_entry ( peer, &segment->peers, list ) {
@@ -403,6 +407,15 @@
 	/* Add to end of list of peers */
 	list_add_tail ( &peer->list, &segment->peers );
 
+	/* Record as most recently discovered peer */
+	if ( location != peerdisc_recent ) {
+		recent = strdup ( location );
+		if ( recent ) {
+			free ( peerdisc_recent );
+			peerdisc_recent = recent;
+		}
+	}
+
 	/* Notify all clients */
 	list_for_each_entry_safe ( peerdisc, tmp, &segment->clients, list )
 		peerdisc->op->discovered ( peerdisc );
@@ -484,6 +497,16 @@
 
 	} else {
 
+		/* Add most recently discovered peer to list of peers
+		 *
+		 * This is a performance optimisation: we assume that
+		 * the most recently discovered peer for any block has
+		 * a high probability of also having a copy of the
+		 * next block that we attempt to discover.
+		 */
+		if ( peerdisc_recent )
+			peerdisc_discovered ( segment, peerdisc_recent );
+
 		/* Start discovery timer */
 		start_timer_nodelay ( &segment->timer );
 		DBGC2 ( segment, "PEERDISC %p discovering %s\n",