/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar
1707.2.2 by Robert Collins
Start on bench_add, an add benchtest.
1
# Copyright (C) 2006 by Canonical Ltd
2
#
3
# This program is free software; you can redistribute it and/or modify
4
# it under the terms of the GNU General Public License as published by
5
# the Free Software Foundation; either version 2 of the License, or
6
# (at your option) any later version.
7
#
8
# This program is distributed in the hope that it will be useful,
9
# but WITHOUT ANY WARRANTY; without even the implied warranty of
10
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11
# GNU General Public License for more details.
12
#
13
# You should have received a copy of the GNU General Public License
14
# along with this program; if not, write to the Free Software
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
16
17
18
"""Benchmark test suite for bzr."""
19
1908.2.12 by John Arbash Meinel
Fix a small bug in _create_heavily_merged_tree when the target dir already exists
20
import errno
1908.2.2 by John Arbash Meinel
Allow caching basic kernel-like trees
21
import os
22
import shutil
23
24
from bzrlib import (
25
    add,
26
    bzrdir,
27
    osutils,
28
    plugin,
29
    workingtree,
30
    )
1773.4.1 by Martin Pool
Add pyflakes makefile target; fix many warnings
31
from bzrlib.tests.TestUtil import TestLoader
1714.1.4 by Robert Collins
Add new benchmarks for status and commit.
32
from bzrlib.tests.blackbox import ExternalBase
33
1841.1.1 by John Arbash Meinel
Allow plugins to provide benchmarks just like they do tests
34
1714.1.4 by Robert Collins
Add new benchmarks for status and commit.
35
class Benchmark(ExternalBase):
36
1908.2.2 by John Arbash Meinel
Allow caching basic kernel-like trees
37
    def make_kernel_like_tree(self, url=None, root='.',
1908.2.11 by John Arbash Meinel
Change caching logic. Don't cache at all without --cache-dir being supplied
38
                              link_working=False):
1725.2.5 by Robert Collins
Bugfix create_branch_convenience at the root of a file system to not loop
39
        """Setup a temporary tree roughly like a kernel tree.
40
        
41
        :param url: Creat the kernel like tree as a lightweight checkout
42
        of a new branch created at url.
1908.2.11 by John Arbash Meinel
Change caching logic. Don't cache at all without --cache-dir being supplied
43
        :param link_working: instead of creating a new copy of all files
1908.2.2 by John Arbash Meinel
Allow caching basic kernel-like trees
44
            just hardlink the working tree. Tests must request this, because
45
            they must break links if they want to change the files
1725.2.5 by Robert Collins
Bugfix create_branch_convenience at the root of a file system to not loop
46
        """
1908.2.14 by John Arbash Meinel
Hook the bench_bench.py tests up for the new classes
47
        creator = KernelLikeTreeCreator(self, link_working=link_working,
48
                                        url=url)
49
        return creator.create(root=root)
1908.2.5 by John Arbash Meinel
Updated bench_bench tests to test exactly what we really want to test
50
1908.2.2 by John Arbash Meinel
Allow caching basic kernel-like trees
51
    def make_kernel_like_added_tree(self, root='.',
1908.2.11 by John Arbash Meinel
Change caching logic. Don't cache at all without --cache-dir being supplied
52
                                    link_working=True,
1908.2.9 by John Arbash Meinel
Allow pre-warming the hash-cache
53
                                    hot_cache=True):
1908.2.2 by John Arbash Meinel
Allow caching basic kernel-like trees
54
        """Make a kernel like tree, with all files added
55
56
        :param root: Where to create the files
1908.2.11 by John Arbash Meinel
Change caching logic. Don't cache at all without --cache-dir being supplied
57
        :param link_working: Instead of copying all of the working tree
1908.2.2 by John Arbash Meinel
Allow caching basic kernel-like trees
58
            files, just hardlink them to the cached files. Tests can unlink
59
            files that they will change.
1908.2.9 by John Arbash Meinel
Allow pre-warming the hash-cache
60
        :param hot_cache: Run through the newly created tree and make sure
61
            the stat-cache is correct. The old way of creating a freshly
62
            added tree always had a hot cache.
1908.2.2 by John Arbash Meinel
Allow caching basic kernel-like trees
63
        """
1908.2.14 by John Arbash Meinel
Hook the bench_bench.py tests up for the new classes
64
        creator = KernelLikeAddedTreeCreator(self, link_working=link_working,
65
                                             hot_cache=hot_cache)
66
        return creator.create(root=root)
1908.2.5 by John Arbash Meinel
Updated bench_bench tests to test exactly what we really want to test
67
1908.2.3 by John Arbash Meinel
Support caching a committed kernel-like tree, and mark hardlinked trees as readonly.
68
    def make_kernel_like_committed_tree(self, root='.',
1908.2.11 by John Arbash Meinel
Change caching logic. Don't cache at all without --cache-dir being supplied
69
                                    link_working=True,
70
                                    link_bzr=False,
1908.2.9 by John Arbash Meinel
Allow pre-warming the hash-cache
71
                                    hot_cache=True):
1908.2.3 by John Arbash Meinel
Support caching a committed kernel-like tree, and mark hardlinked trees as readonly.
72
        """Make a kernel like tree, with all files added and committed
73
74
        :param root: Where to create the files
1908.2.11 by John Arbash Meinel
Change caching logic. Don't cache at all without --cache-dir being supplied
75
        :param link_working: Instead of copying all of the working tree
1908.2.3 by John Arbash Meinel
Support caching a committed kernel-like tree, and mark hardlinked trees as readonly.
76
            files, just hardlink them to the cached files. Tests can unlink
77
            files that they will change.
1908.2.11 by John Arbash Meinel
Change caching logic. Don't cache at all without --cache-dir being supplied
78
        :param link_bzr: Hardlink the .bzr directory. For readonly 
1908.2.3 by John Arbash Meinel
Support caching a committed kernel-like tree, and mark hardlinked trees as readonly.
79
            operations this is safe, and shaves off a lot of setup time
80
        """
1908.2.14 by John Arbash Meinel
Hook the bench_bench.py tests up for the new classes
81
        creator = KernelLikeCommittedTreeCreator(self,
82
                                                 link_working=link_working,
83
                                                 link_bzr=link_bzr,
84
                                                 hot_cache=hot_cache)
85
        return creator.create(root=root)
1707.2.2 by Robert Collins
Start on bench_add, an add benchtest.
86
1908.2.6 by John Arbash Meinel
Allow the many_merged and many_commit trees to be cached
87
    def make_many_commit_tree(self, directory_name='.',
88
                              hardlink=False):
89
        """Create a tree with many commits.
90
        
91
        No file changes are included. Not hardlinking the working tree, 
92
        because there are no working tree files.
1756.2.19 by Aaron Bentley
Add benchmarks for merged trees
93
        """
1908.2.15 by John Arbash Meinel
Switching many_merge and heavy_merge to new tree creator classes
94
        creator = SimpleManyCommitTreeCreator(self, link_bzr=hardlink)
95
        return creator.create(root=directory_name)
1908.2.6 by John Arbash Meinel
Allow the many_merged and many_commit trees to be cached
96
97
    def make_heavily_merged_tree(self, directory_name='.',
98
                                 hardlink=False):
99
        """Create a tree in which almost every commit is a merge.
100
       
101
        No file changes are included.  This produces two trees, 
102
        one of which is returned.  Except for the first commit, every
103
        commit in its revision-history is a merge another commit in the other
104
        tree.  Not hardlinking the working tree, because there are no working 
105
        tree files.
106
        """
1908.2.15 by John Arbash Meinel
Switching many_merge and heavy_merge to new tree creator classes
107
        creator = HeavilyMergedTreeCreator(self, link_bzr=hardlink)
108
        return creator.create(root=directory_name)
1756.2.19 by Aaron Bentley
Add benchmarks for merged trees
109
1707.2.2 by Robert Collins
Start on bench_add, an add benchtest.
110
1908.2.13 by John Arbash Meinel
Factor out all helper functions into a tree creator class
111
class TreeCreator(object):
112
    """Just a basic class which is used to create various test trees"""
113
114
    CACHE_ROOT = None
115
1908.2.14 by John Arbash Meinel
Hook the bench_bench.py tests up for the new classes
116
    def __init__(self, test, tree_name,
117
                 link_bzr=False,
118
                 link_working=False,
1908.2.13 by John Arbash Meinel
Factor out all helper functions into a tree creator class
119
                 hot_cache=True):
120
        """Instantiate a new creator object, supply the id of the tree"""
121
1908.2.14 by John Arbash Meinel
Hook the bench_bench.py tests up for the new classes
122
        self._cache_root = TreeCreator.CACHE_ROOT
123
        self._test = test
1908.2.13 by John Arbash Meinel
Factor out all helper functions into a tree creator class
124
        self._tree_name = tree_name
125
        self._link_bzr = link_bzr
126
        self._link_working = link_working
127
        self._hot_cache = hot_cache
1908.2.14 by John Arbash Meinel
Hook the bench_bench.py tests up for the new classes
128
        if not osutils.hardlinks_good():
129
            self._link_working = self._link_bzr = False
1908.2.13 by John Arbash Meinel
Factor out all helper functions into a tree creator class
130
1908.2.14 by John Arbash Meinel
Hook the bench_bench.py tests up for the new classes
131
    def is_caching_enabled(self):
1908.2.13 by John Arbash Meinel
Factor out all helper functions into a tree creator class
132
        """Will we try to cache the tree we create?"""
1908.2.14 by John Arbash Meinel
Hook the bench_bench.py tests up for the new classes
133
        return self._cache_root is not None
1908.2.13 by John Arbash Meinel
Factor out all helper functions into a tree creator class
134
135
    def is_cached(self):
136
        """Is this tree already cached?"""
137
        cache_dir = self._get_cache_dir()
138
        if cache_dir is None:
139
            return False
1908.2.14 by John Arbash Meinel
Hook the bench_bench.py tests up for the new classes
140
        return os.path.exists(cache_dir)
1908.2.13 by John Arbash Meinel
Factor out all helper functions into a tree creator class
141
        
1908.2.14 by John Arbash Meinel
Hook the bench_bench.py tests up for the new classes
142
    def disable_cache(self):
143
        """Do not use the cache"""
144
        self._cache_root = None
145
146
    def ensure_cached(self):
147
        """If caching, make sure the cached copy exists"""
148
        cache_dir = self._get_cache_dir()
149
        if cache_dir is None:
150
            return
151
152
        if not self.is_cached():
153
            self._create_tree(root=cache_dir, in_cache=True)
154
1908.2.13 by John Arbash Meinel
Factor out all helper functions into a tree creator class
155
    def create(self, root):
156
        """Create a new tree at 'root'.
157
158
        :return: A WorkingTree object.
159
        """
160
        cache_dir = self._get_cache_dir()
161
        if cache_dir is None:
162
            # Not caching
163
            return self._create_tree(root, in_cache=False)
164
1908.2.14 by John Arbash Meinel
Hook the bench_bench.py tests up for the new classes
165
        self.ensure_cached()
1908.2.13 by John Arbash Meinel
Factor out all helper functions into a tree creator class
166
167
        return self._clone_cached_tree(root)
168
169
    def _get_cache_dir(self):
170
        """Get the directory to use for caching this tree
171
172
        :return: The path to use for caching. If None, caching is disabled
173
        """
1908.2.14 by John Arbash Meinel
Hook the bench_bench.py tests up for the new classes
174
        if self._cache_root is None:
1908.2.13 by John Arbash Meinel
Factor out all helper functions into a tree creator class
175
            return None
1908.2.14 by John Arbash Meinel
Hook the bench_bench.py tests up for the new classes
176
        return osutils.pathjoin(self._cache_root, self._tree_name)
1908.2.13 by John Arbash Meinel
Factor out all helper functions into a tree creator class
177
178
    def _create_tree(self, root, in_cache=False):
179
        """Create the desired tree in the given location.
180
181
        Children should override this function to provide the actual creation
182
        of the desired tree. This will be called by 'create()'. If it is
183
        building a tree in the cache, before copying it to the real target,
184
        it will pass in_cache=True
185
        """
186
        raise NotImplemented(self._create_tree)
187
188
    def _clone_cached_tree(self, dest):
189
        """Copy the contents of the cached dir into the destination
190
        Optionally hardlink certain pieces of the tree.
191
192
        This is just meant as a helper function for child classes
193
194
        :param dest: The destination to copy things to
195
        """
196
        # We use shutil.copyfile so that we don't copy permissions
197
        # because most of our source trees are marked readonly to
198
        # prevent modifying in the case of hardlinks
199
        handlers = {'file':shutil.copyfile}
200
        if osutils.hardlinks_good():
201
            if self._link_working:
202
                if self._link_bzr:
203
                    handlers = {'file':os.link}
204
                else:
205
                    # Don't hardlink files inside bzr
206
                    def file_handler(source, dest):
207
                        if '.bzr/' in source:
208
                            shutil.copyfile(source, dest)
209
                        else:
210
                            os.link(source, dest)
211
                    handlers = {'file':file_handler}
212
            elif self._link_bzr:
213
                # Only link files inside .bzr/
214
                def file_handler(source, dest):
215
                    if '.bzr/' in source:
216
                        os.link(source, dest)
217
                    else:
218
                        shutil.copyfile(source, dest)
219
                handlers = {'file':file_handler}
220
221
        source = self._get_cache_dir()
222
        osutils.copy_tree(source, dest, handlers=handlers)
223
        tree = workingtree.WorkingTree.open(dest)
224
        if self._hot_cache:
225
            tree.lock_write()
226
            try:
227
                # tree._hashcache.scan() just checks and removes
228
                # entries that are out of date
229
                # we need to actually store new ones
230
                for path, ie in tree.inventory.iter_entries_by_dir():
231
                    tree.get_file_sha1(ie.file_id, path)
232
            finally:
233
                tree.unlock()
234
        # If we didn't iterate the tree, the hash cache is technically
235
        # invalid, and it would be better to remove it, but there is
236
        # no public api for that.
237
        return tree
238
239
    def _protect_files(self, root):
240
        """Chmod all files underneath 'root' to prevent writing
241
242
        This is a helper function for child classes.
243
244
        :param root: The base directory to modify
245
        """
246
        for dirinfo, entries in osutils.walkdirs(root):
247
            for relpath, name, kind, st, abspath in entries:
248
                if kind == 'file':
249
                    os.chmod(abspath, 0440)
250
251
1908.2.14 by John Arbash Meinel
Hook the bench_bench.py tests up for the new classes
252
class KernelLikeTreeCreator(TreeCreator):
253
    """Create a basic tree with ~10k unversioned files""" 
254
255
    def __init__(self, test, link_working=False, url=None):
256
        super(KernelLikeTreeCreator, self).__init__(test,
257
            tree_name='kernel_like_tree',
258
            link_working=link_working,
259
            link_bzr=False)
260
261
        self._url = url
262
263
    def create(self, root):
264
        """Create all the kernel files in the given location.
265
266
        This is overloaded for compatibility reasons.
267
        """
268
        if self._url is not None:
269
            b = bzrdir.BzrDir.create_branch_convenience(self._url)
270
            d = bzrdir.BzrDir.create(root)
271
            bzrlib.branch.BranchReferenceFormat().initialize(d, b)
272
            tree = d.create_workingtree()
273
        else:
274
            tree = bzrdir.BzrDir.create_standalone_workingtree(root)
275
276
        if not self._link_working or not self.is_caching_enabled():
277
            # Turns out that 'shutil.copytree()' is no faster than
278
            # just creating them. Probably the python overhead.
279
            # Plain _make_kernel_files takes 3-5s
280
            # cp -a takes 3s
281
            # using hardlinks takes < 1s.
282
            self._create_tree(root=root, in_cache=False)
283
            return tree
284
285
        self.ensure_cached()
286
        cache_dir = self._get_cache_dir()
287
        osutils.copy_tree(cache_dir, root,
288
                          handlers={'file':os.link})
289
        return tree
290
291
    def _create_tree(self, root, in_cache=False):
292
        # a kernel tree has ~10000 and 500 directory, with most files around 
293
        # 3-4 levels deep. 
294
        # we simulate this by three levels of dirs named 0-7, givin 512 dirs,
295
        # and 20 files each.
296
        files = []
297
        for outer in range(8):
298
            files.append("%s/" % outer)
299
            for middle in range(8):
300
                files.append("%s/%s/" % (outer, middle))
301
                for inner in range(8):
302
                    prefix = "%s/%s/%s/" % (outer, middle, inner)
303
                    files.append(prefix)
304
                    files.extend([prefix + str(foo) for foo in range(20)])
305
        cwd = osutils.getcwd()
306
        os.chdir(root)
307
        self._test.build_tree(files)
308
        os.chdir(cwd)
309
        if in_cache:
310
            self._protect_files(root)
311
312
313
class KernelLikeAddedTreeCreator(TreeCreator):
314
315
    def __init__(self, test, link_working=False, hot_cache=True):
316
        super(KernelLikeAddedTreeCreator, self).__init__(test,
317
            tree_name='kernel_like_added_tree',
318
            link_working=link_working,
319
            link_bzr=False,
320
            hot_cache=hot_cache)
321
322
    def _create_tree(self, root, in_cache=False):
323
        """Create a kernel-like tree with the all files added
324
325
        :param root: The root directory to create the files
326
        :param in_cache: Is this being created in the cache dir?
327
        """
328
        kernel_creator = KernelLikeTreeCreator(self._test,
329
                                               link_working=in_cache)
330
        tree = kernel_creator.create(root=root)
331
332
        # Add everything to it
333
        tree.lock_write()
334
        try:
335
            add.smart_add_tree(tree, [root], recurse=True, save=True)
336
            if in_cache:
337
                self._protect_files(root+'/.bzr')
338
        finally:
339
            tree.unlock()
340
        return tree
341
342
343
class KernelLikeCommittedTreeCreator(TreeCreator):
1908.2.15 by John Arbash Meinel
Switching many_merge and heavy_merge to new tree creator classes
344
    """Create a tree with ~10K files, and a single commit adding all of them"""
1908.2.14 by John Arbash Meinel
Hook the bench_bench.py tests up for the new classes
345
346
    def __init__(self, test, link_working=False, link_bzr=False,
347
                 hot_cache=True):
348
        super(KernelLikeCommittedTreeCreator, self).__init__(test,
349
            tree_name='kernel_like_committed_tree',
350
            link_working=link_working,
351
            link_bzr=link_bzr,
352
            hot_cache=hot_cache)
353
354
    def _create_tree(self, root, in_cache=False):
1908.2.15 by John Arbash Meinel
Switching many_merge and heavy_merge to new tree creator classes
355
        """Create a kernel-like tree with all files committed
1908.2.14 by John Arbash Meinel
Hook the bench_bench.py tests up for the new classes
356
357
        :param root: The root directory to create the files
358
        :param in_cache: Is this being created in the cache dir?
359
        """
360
        kernel_creator = KernelLikeAddedTreeCreator(self._test,
361
                                                    link_working=in_cache,
362
                                                    hot_cache=(not in_cache))
363
        tree = kernel_creator.create(root=root)
364
        tree.commit('first post', rev_id='r1')
365
366
        if in_cache:
367
            self._protect_files(root+'/.bzr')
368
        return tree
369
370
1908.2.15 by John Arbash Meinel
Switching many_merge and heavy_merge to new tree creator classes
371
class SimpleManyCommitTreeCreator(TreeCreator):
372
    """Create an empty tree with lots of commits"""
373
374
    def __init__(self, test, link_bzr=False):
375
        super(SimpleManyCommitTreeCreator, self).__init__(test,
376
            tree_name='many_commit_tree',
377
            link_bzr=link_bzr,
378
            link_working=False,
379
            hot_cache=True)
380
381
    def _create_tree(self, root, in_cache=False):
382
        tree = bzrdir.BzrDir.create_standalone_workingtree(root)
383
        tree.lock_write()
384
        try:
385
            for i in xrange(1000):
386
                tree.commit('no-changes commit %d' % i)
387
        finally:
388
            tree.unlock()
389
        if in_cache:
390
            self._protect_files(root+'/.bzr')
391
392
        return tree
393
394
395
class HeavilyMergedTreeCreator(TreeCreator):
396
    """Create a tree in which almost every commit is a merge.
397
   
398
    No file changes are included.  This produces two trees, 
399
    one of which is returned.  Except for the first commit, every
400
    commit in its revision-history is a merge of another commit in the other
401
    tree.  
402
    Not hardlinking the working tree, because there are no working tree files.
403
    """
404
405
    def __init__(self, test, link_bzr=True):
406
        super(HeavilyMergedTreeCreator, self).__init__(test,
407
            tree_name='heavily_merged_tree',
408
            link_bzr=link_bzr,
409
            link_working=False,
410
            hot_cache=True)
411
412
    def _create_tree(self, root, in_cache=False):
413
        try:
414
            os.mkdir(root)
415
        except (IOError, OSError), e:
416
            if e.errno not in (errno.EEXIST,):
417
                raise
418
419
        tree = bzrdir.BzrDir.create_standalone_workingtree(root)
420
        tree.lock_write()
421
        try:
422
            tree2 = tree.bzrdir.sprout(root + '/tree2').open_workingtree()
423
            tree2.lock_write()
424
            try:
425
                for i in xrange(250):
426
                    revision_id = tree.commit('no-changes commit %d-a' % i)
427
                    tree2.branch.fetch(tree.branch, revision_id)
428
                    tree2.set_pending_merges([revision_id])
429
                    revision_id = tree2.commit('no-changes commit %d-b' % i)
430
                    tree.branch.fetch(tree2.branch, revision_id)
431
                    tree.set_pending_merges([revision_id])
432
                tree.set_pending_merges([])
433
            finally:
434
                tree2.unlock()
435
        finally:
436
            tree.unlock()
437
        if in_cache:
438
            self._protect_files(root+'/.bzr')
439
        return tree
440
1908.2.14 by John Arbash Meinel
Hook the bench_bench.py tests up for the new classes
441
1707.2.2 by Robert Collins
Start on bench_add, an add benchtest.
442
def test_suite():
443
    """Build and return a TestSuite which contains benchmark tests only."""
444
    testmod_names = [ \
445
                   'bzrlib.benchmarks.bench_add',
1755.2.1 by Robert Collins
Add a benchmark for make_kernel_like_tree.
446
                   'bzrlib.benchmarks.bench_bench',
1714.1.4 by Robert Collins
Add new benchmarks for status and commit.
447
                   'bzrlib.benchmarks.bench_checkout',
1714.1.5 by Robert Collins
Add commit benchmark.
448
                   'bzrlib.benchmarks.bench_commit',
1757.2.10 by Robert Collins
Give all inventory entries __slots__ that are useful with the current codebase.
449
                   'bzrlib.benchmarks.bench_inventory',
1756.1.7 by Aaron Bentley
Merge bzr.dev
450
                   'bzrlib.benchmarks.bench_log',
1756.1.2 by Aaron Bentley
Show logs using get_revisions
451
                   'bzrlib.benchmarks.bench_osutils',
1752.1.2 by Aaron Bentley
Benchmark the rocks command
452
                   'bzrlib.benchmarks.bench_rocks',
1714.1.4 by Robert Collins
Add new benchmarks for status and commit.
453
                   'bzrlib.benchmarks.bench_status',
1534.10.33 by Aaron Bentley
Add canonicalize_path benchmark
454
                   'bzrlib.benchmarks.bench_transform',
1732.1.11 by John Arbash Meinel
Trying multiple things to get WorkingTree.list_files time down
455
                   'bzrlib.benchmarks.bench_workingtree',
1707.2.2 by Robert Collins
Start on bench_add, an add benchtest.
456
                   ]
1841.1.1 by John Arbash Meinel
Allow plugins to provide benchmarks just like they do tests
457
    suite = TestLoader().loadTestsFromModuleNames(testmod_names) 
458
459
    # Load any benchmarks from plugins
1711.2.78 by John Arbash Meinel
Cleanup the imports in bzrlib.benchmark
460
    for name, module in plugin.all_plugins().items():
461
        if getattr(module, 'bench_suite', None) is not None:
462
            suite.addTest(module.bench_suite())
1841.1.1 by John Arbash Meinel
Allow plugins to provide benchmarks just like they do tests
463
464
    return suite