9

svnmerge.pyというスクリプトがあり、これを微調整して最適化しようとしています。私はPythonはまったく初めてなので、簡単ではありません。

RevisionSet現在の問題は、スクリプトで呼び出されたクラスに関連しているようです。本質的には、整数キーのブール値の大きなハッシュテーブル (?) を作成することです。最悪の場合、現在 75,000 近くある SVN リポジトリの各リビジョンに 1 つです。

その後、そのような巨大な配列に対して集合演算 (加算、減算、交差など) を実行します。実装は最も単純な O(n) 実装であり、当然のことながら、このような大規模なセットではかなり遅くなります。連続値のスパンが長いため、データ構造全体を最適化できます。たとえば、1 から 74,000 までのすべてのキーにtrue. また、スクリプトは Python 2.2 用に書かれています。これはかなり古いバージョンであり、とにかく 2.6 を使用しているため、そこにも何かが得られる可能性があります。

これを自分でまとめようとすることもできますが、それは難しく、多くの時間がかかります。学習経験は欲しいが、今は結果の方が重要だ。私に何を提案しますか?

4

5 に答える 5

7

プレーンな python の代わりに numpy で試してみることができます。このような操作では非常に高速であることがわかりました。

例えば:

# Create 1000000 numbers between 0 and 1000, takes 21ms
x = numpy.random.randint(0, 1000, 1000000)

# Get all items that are larger than 500, takes 2.58ms
y = x > 500

# Add 10 to those items, takes 26.1ms
x[y] += 10

それはもっと多くの行があるので、75000も問題にならないと思います:)

于 2010-10-19T10:59:26.647 に答える
1

これは、セットにする RevisionSet の簡単な置き換えです。それははるかに速いはずです。完全にテストしたわけではありませんが、行ったすべてのテストで機能しました。__sub__スピードアップする方法は間違いなく他にもありますが、元のコードがやのような関数で行っていた Python のループを実行するのではなく、セットの高速な実装を実際に利用するので、これは本当に役立つと思います__and__。唯一の問題は、イテレータがソートされていないことです。これを考慮して、コードを少し変更する必要がある場合があります。これを改善する方法は他にもあると思いますが、うまくいけば、良いスタートを切ることができます。

class RevisionSet(set):
    """
    A set of revisions, held in dictionary form for easy manipulation. If we
    were to rewrite this script for Python 2.3+, we would subclass this from
    set (or UserSet).  As this class does not include branch
    information, it's assumed that one instance will be used per
    branch.
    """
    def __init__(self, parm):
        """Constructs a RevisionSet from a string in property form, or from
        a dictionary whose keys are the revisions. Raises ValueError if the
        input string is invalid."""


        revision_range_split_re = re.compile('[-:]')

        if isinstance(parm, set):
            print "1"
            self.update(parm.copy())
        elif isinstance(parm, list):
            self.update(set(parm))
        else:
            parm = parm.strip()
            if parm:
                for R in parm.split(","):
                    rev_or_revs = re.split(revision_range_split_re, R)
                    if len(rev_or_revs) == 1:
                        self.add(int(rev_or_revs[0]))
                    elif len(rev_or_revs) == 2:
                        self.update(set(range(int(rev_or_revs[0]),
                                         int(rev_or_revs[1])+1)))
                    else:
                        raise ValueError, 'Ill formatted revision range: ' + R

    def sorted(self):
        return sorted(self)

    def normalized(self):
        """Returns a normalized version of the revision set, which is an
        ordered list of couples (start,end), with the minimum number of
        intervals."""
        revnums = sorted(self)
        revnums.reverse()
        ret = []
        while revnums:
            s = e = revnums.pop()
            while revnums and revnums[-1] in (e, e+1):
                e = revnums.pop()
            ret.append((s, e))
        return ret

    def __str__(self):
        """Convert the revision set to a string, using its normalized form."""
        L = []
        for s,e in self.normalized():
            if s == e:
                L.append(str(s))
            else:
                L.append(str(s) + "-" + str(e))
        return ",".join(L)

追加: ところで、元の RevisionSet と上記の私の RevisionSet の結合、交差、減算を比較したところ、75000 要素を持つ 2 つの RevisionSet を操作する場合、上記のコードはこれらの操作で 3 倍から 7 倍高速です。他の人がnumpyが進むべき道だと言っていることは知っていますが、コメントが示すように、Pythonの経験があまりない場合は、より多くの変更が必要になるため、そのルートに行きたくないかもしれません. 私のコードを試して、それが機能するかどうかを確認し、機能する場合は、十分に高速かどうかを確認することをお勧めします. そうでない場合は、プロファイリングを行って、改善が必要なものを確認します。そうして初めて、numpy の使用を検討します (これは、私が頻繁に使用する優れたパッケージです)。

于 2010-10-19T19:12:40.950 に答える
0

たとえば、1〜74,000のすべてのキーにtrueが含まれています

サブセットで作業してみませんか?最後までちょうど74001。

データの74/75のプルーニングは、O(n)よりも賢いアルゴリズムを作成しようとするよりもはるかに簡単です。

于 2010-10-19T11:35:38.823 に答える
0

ちょっとした考え。私は、バイナリ画像操作でランコーディングを使用してこの種のことを行っていました。つまり、各セットを一連の数値として格納します。ビット数オフ、ビット数オン、ビット数オフなどです。

次に、単純なマージアルゴリズムの装飾として、それらに対してあらゆる種類のブール演算を実行できます。

于 2010-10-19T16:00:28.867 に答える
0

リビジョンのセットを持つように RevisionSet を書き直す必要があります。リビジョンの内部表現は整数で、必要に応じてリビジョン範囲を作成する必要があると思います。

Python 2.3 以前をサポートするコードを使用しなければならない理由はありません。

于 2010-10-19T15:07:36.177 に答える