posts - 56,  comments - 12,  trackbacks - 0

          PiecePicker 用于實現(xiàn)“片斷選擇算法”,片斷選擇算法在《Incentives Build Robustness in BitTorrent》一文中有介紹,我把相關(guān)內(nèi)容列出來。

           

          BT的片斷選擇算法,綜合下面幾種策略。

           

          l         嚴(yán)格的優(yōu)先級

              片斷選擇的第一個策略是:一旦請求了某個片斷的子片斷,那么該片斷剩下的子片斷優(yōu)先被請求。這樣,可以盡可能快的獲得一個完整的片斷

           

          l         最少的優(yōu)先

              每 個peer都優(yōu)先選擇整個系統(tǒng)中最少的那些片斷去下載,而那些在系統(tǒng)中相對較多的片斷,放在后面下載,這樣,整個系統(tǒng)就趨向于一種更優(yōu)的狀態(tài)。如果不用這 種算法,大家都去下載最多的那些片斷,那么這些片斷就會在系統(tǒng)中分布的越來越多,而那些在系統(tǒng)中相對較少的片斷仍然很少,最后,某些 peer 就不再擁有其它 peer 感興趣的片斷了,那么系統(tǒng)的參與者越來越少,整個系統(tǒng)的性能就下降。

           

          l         隨機的第一個片斷

              “最 少優(yōu)先”的一個例外是在下載剛開始的時候。此時,下載者沒有任何片斷可供上傳,所以,需要盡快的獲取一個完整的片斷。而最少的片斷,通常只有某一個 peer擁有,所以,它可能比多個peers都擁有的那些片斷下載的要慢。因此,第一個片斷是隨機選擇的,直到第一個片斷下載完成,才切換到“最少優(yōu)先” 的策略。

           

          l         最后階段模式

              有 時候,從一個速率很慢的peer那里請求一個片斷。在下載的中間階段,這不是什么問題,但是卻可能潛在的延遲下載的完成。為了防止這種情況,在最后階段, peer向它的所有的peers們都發(fā)送某片斷的子片斷的請求,一旦某些子片斷到了,那么就會向其它peer發(fā)送cancel 消息,取消對這些子片斷的請求,以避免帶寬的浪費。實際上,用這種方法并沒有浪費多少帶寬,而文件的結(jié)束部分也一直下載的非常快。

           

          下面是我在分析之前思考的兩個問題:

           

          問題1:如何實現(xiàn)“嚴(yán)格優(yōu)先級”

          答案:記錄每個已經(jīng)開始下載的片斷。優(yōu)先選擇它們。

           

          問題2:如何實現(xiàn)“最少優(yōu)先”算法?也就是你如何去統(tǒng)計某個片斷在系統(tǒng)中最少?

          答案:通過 have 消息(have消息請參看BT對等協(xié)議)來計算。在下載過程中,會不停的收到其它 peer 發(fā)來的 have 消息,每個have消息都表明對方擁有了某個片斷。那么,為每個片斷維護一個計數(shù)器,每收到一個have消息,相應(yīng)的計數(shù)器加1。在選擇片斷的時候,計數(shù) 器最小的某個片斷被選中。

           

          在實際代碼中,可以看到,變量started和seedstarted 是用來實現(xiàn)“嚴(yán)格優(yōu)先級”的,它們記錄了那些已經(jīng)開始下載的片斷。而變量 numinterests用來實現(xiàn)“最少優(yōu)先”算法。

           

          PiecePicker類的核心函數(shù)是 next() ,它綜合多種策略,來計算出下一個應(yīng)該被選擇進行下載的片斷。

           

          PiecePicker 類的難點是三個變量 numinterests、interests、pos_in_interests的作用。因為沒有任何注釋,我思考了很久才明白它們的作用,特別是 pos_in_interests。所以,在分析代碼之前,我結(jié)合例子來講解這三個變量的作用。

           

          假設(shè)有三個片斷:

           

          numinterests:

          類型是list,每個片斷對應(yīng)一項,記錄了每個片斷收到的 have 消息的個數(shù)。初始化的時候,numinterests = [0, 0, 0]。

           

          interests:

          類型是 list,它的每一項又是一個 list。例如在這個例子中,初始化的時候,interests = [ [0, 1, 2] ],顯然,它只有一項。

          interests 的作用是什么了?嗯,有點難以表達。大概是這樣的吧:所有未完成下載的片斷的索引號都保存在 interests,進行片斷選擇的時候,要用到 interests。我們看兩個例子:

          1、interests = [ [0, 1], [2]]

          2、interests = [ [1], [0, 2]]

          在第一個例子中,片斷0、1位于 interests 的第0項,片斷2位于 interests的第1項。

          在第二個例子中,片斷1位于位于 interests 的第0項,片斷0、2位于 interests的第1項。

          無論哪種情況,都表明0、1、2三個片斷都還沒有下載完成。

          那么,某個片斷到底應(yīng)該處于 interests 中什么位置了?答案是根據(jù)該片斷收到的 have 消息個數(shù),也就是 numinterests 的值。 例如,第一個例子中,說明片斷0、1收到的 have 個數(shù)都是0,所以處于 interests的第0項,而片斷2收到的 have 個數(shù)是1,所以處于第1項。而初始化的時候,interests =[ [0, 1, 2]],說明片斷0、1、2收到的 have個數(shù)都是0。

          奇怪,為什么要這樣設(shè)計了?答案就是“最少優(yōu)先”的片斷選擇策略。我們看到,擁有越多 have 的片斷,在 interests 中,位置越靠后。在進行片斷選擇的時候,可能會從 interests中選一個片斷出來(為什么說可能了,一會可以看到,會優(yōu)先采用其它策略,如果其它策略不能選一個出來,才會試圖從 interests 中選)。這樣,按照索引從小到大的順序,擁有 have 越少的片斷,越可能被選到。我們考慮這樣一個例子:

          interests = [[2, 3], [5, 0, 1], [], [], [4]]

          片斷2、3擁有0個 have,不能被選擇。(至少要有一個 have 才被考慮)。

          片斷0、1、5都有1個have,所以會優(yōu)先從它們中選擇一個。

          片斷4擁有4個 have,所以最后被考慮。

           

          pos_in_interests:

          如上所述,擁有相同 have 個數(shù)的片斷,處于 interests 中的同一位置。例如上面這個例子,0、1、5都處于第1個位置。那么它們又根據(jù)什么原則進行先后排列了?答案是隨機排列。所以,既可能是0、1、5,也可 能是 1、5、0,或者其它。為了記錄某個片斷的確切位置,就需要用到 pos_in_interests了。它也是一個 list,每個片斷擁有一項,根據(jù)上面這個例子,應(yīng)該是:

          pos_in_interests = [1, 2, 0, 1, 0, 0]

          看出什么來沒?呵呵

          它的意思是,

          片斷0是 [5, 0, 1] 的第1個

          片斷1是 [5, 0, 1] 的第2個

          片斷2是 [2, 3] 的第0個

          片斷3是 [2, 3] 的第1個

          片斷4是 [4] 的第0個

          片斷5是 [5, 0, 1] 的第0個

           

          就是這樣嘍,不知道我有沒有說清楚。

           

           

          # 封裝“片斷選擇算法”

          class PiecePicker:

              def __init__(self, numpieces, rarest_first_cutoff = 1):

                  self.rarest_first_cutoff = rarest_first_cutoff

                  self.numpieces = numpieces  # 片斷的個數(shù)

                  self.interests = [range(numpieces)]

                  self.pos_in_interests = range(numpieces)

                  self.numinterests = [0] * numpieces

                  self.started = []

                  self.seedstarted = []

                  self.numgot = 0 # 獲得了幾個片斷?

                  self.scrambled = range(numpieces)

                  shuffle(self.scrambled)

           

                 收到一個 have 消息的處理

              def got_have(self, piece):

                  if self.numinterests[piece] is None:

          return

                        numint = self.numinterests[piece]

                  if numint == len(self.interests) - 1:

          self.interests.append([])

                        numinterests 對應(yīng)的值要加 1 。

                  self.numinterests[piece] += 1

                        調(diào)整 interests pos_in_interests

                  self._shift_over(piece, self.interests[numint], self.interests[numint + 1])

           

                 丟失一個 have 消息?????

              def lost_have(self, piece):

                  if self.numinterests[piece] is None:

                      return

                  numint = self.numinterests[piece]

                  self.numinterests[piece] -= 1

                  self._shift_over(piece, self.interests[numint], self.interests[numint - 1])

           

                 調(diào)整 interests pos_in_interests ,前面已經(jīng)分析過。

              def _shift_over(self, piece, l1, l2):

                  p = self.pos_in_interests[piece]

                  l1[p] = l1[-1]

                  self.pos_in_interests[l1[-1]] = p

                  del l1[-1]

                  newp = randrange(len(l2) + 1)

                  if newp == len(l2):

                      self.pos_in_interests[piece] = len(l2)

                      l2.append(piece)

                  else:

                      old = l2[newp]

                      self.pos_in_interests[old] = len(l2)

                      l2.append(old)

                      l2[newp] = piece

                      self.pos_in_interests[piece] = newp

           

                 為某個片斷發(fā)送過 requested 消息,用于“嚴(yán)格優(yōu)先級”策略

              def requested(self, piece, seed = False):

                  if piece not in self.started:

                               把片斷索引號添加到 started

                      self.started.append(piece)

                  if seed and piece not in self.seedstarted:

                      self.seedstarted.append(piece)

           

              # 如果某個片斷已經(jīng)得到,那么調(diào)用這個函數(shù)

              def complete(self, piece):

                  assert self.numinterests[piece] is not None

                  self.numgot += 1

           

                  l = self.interests[self.numinterests[piece]]

                  p = self.pos_in_interests[piece]

                  l[p] = l[-1]

                  self.pos_in_interests[l[-1]] = p

                  del l[-1]

                  self.numinterests[piece] = None

                  try:

                      self.started.remove(piece)

                      self.seedstarted.remove(piece)

                  except Error:

                      pass

           

                 計算下一個被選擇的片斷

              def next(self, havefunc, seed = False):

                  bests = None

                  bestnum = 2 ** 30

           

                        首先根據(jù)“嚴(yán)格優(yōu)先級”策略,從已經(jīng)開始下載的片斷中選擇。

                  if seed:

                      s = self.seedstarted

                  else:

          s = self.started

           

          “嚴(yán)格優(yōu)先級”策略是和“最少優(yōu)先”策略結(jié)合起來使用的。也就是說,在滿足“嚴(yán)格優(yōu)先”的片斷中,再去選擇一個滿足“最少優(yōu)先”的片斷。注意,“最少優(yōu)先”還有一個限制,就是如果一個片斷如果從來沒有收到過 have 消息(也就是計數(shù)是 0 ),也不能被選擇。這個判斷由下面的 havefunc(i) 完成。

                        for i in s:

                      if havefunc(i):

                          if self.numinterests[i] < bestnum:

                              bests = [i]

                              bestnum = self.numinterests[i]

                          elif self.numinterests[i] == bestnum:

          bests.append(i)

           

          經(jīng)過“嚴(yán)格優(yōu)先級”和“最少優(yōu)先”策略之后,可能返回多個候選片斷,從中隨機選擇一個,返回。

                  if bests:

                               bests 隨機返回一個值

          return choice(bests)

           

           

          如果以上步驟,沒有選擇出一個片斷。那么隨機選擇一個。這大概就是“隨機的第一個片斷”的策略吧。因為 rarest_first_cutoff 默認是設(shè)置為 1 的。也就是說,在一個片斷都沒有獲得的情況下,才會選擇這種策略。如果 rarest_first_cutoff 設(shè)置為 10 ,那么這個策略就可以叫做“隨機的前 10 個片斷”,呵呵。

           

                  if self.numgot < self.rarest_first_cutoff:

                      for i in self.scrambled:

                          if havefunc(i):

                              return i

          return None

           

          如果不能采用“隨機的第一個片斷”測率,那么, interests 終于派上用場了。到這里,終于明白 interests 為什么要用 numinterests 對應(yīng)的值來進行定位了。還是“最少優(yōu)先”的思想,因為那些收到 have 消息少的片斷,在 interests 中位置比較靠前,所以會優(yōu)先被選擇到。

                  for i in xrange(1, min(bestnum, len(self.interests))):

                      for j in self.interests[i]:

                          if havefunc(j):

          return j

           

                        如果還選不出來,只好返回 None 了。

                  return None

           

              def am_I_complete(self):

                  return self.numgot == self.numpieces

           

          誰來補充?

              def bump(self, piece):

                  l = self.interests[self.numinterests[piece]]

                  pos = self.pos_in_interests[piece]

                  del l[pos]

                  l.append(piece)

                  for i in range(pos,len(l)):

          self.pos_in_interests[l[i]] = i
          posted on 2007-01-19 00:22 苦笑枯 閱讀(259) 評論(0)  編輯  收藏 所屬分類: P2P
          收藏來自互聯(lián)網(wǎng),僅供學(xué)習(xí)。若有侵權(quán),請與我聯(lián)系!

          <2007年1月>
          31123456
          78910111213
          14151617181920
          21222324252627
          28293031123
          45678910

          常用鏈接

          留言簿(2)

          隨筆分類(56)

          隨筆檔案(56)

          搜索

          •  

          最新評論

          閱讀排行榜

          評論排行榜

          主站蜘蛛池模板: 旬邑县| 衡南县| 东城区| 苍南县| 黄龙县| 桂平市| 建德市| 蒙阴县| 黑龙江省| 北海市| 安顺市| 阿鲁科尔沁旗| 杭州市| 河源市| 洛川县| 甘孜| 固始县| 平阴县| 奈曼旗| 庆阳市| 罗源县| 宁阳县| 惠安县| 鲜城| 迭部县| 昆山市| 罗源县| 太和县| 内丘县| 潞西市| 巴楚县| 礼泉县| 兴业县| 格尔木市| 青田县| 北川| 仁化县| 永登县| 嘉祥县| 宁蒗| 辉南县|