1
# Copyright (C) 2007 Canonical Ltd
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.
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.
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
17
"""Benchmarks for bzr DirState performance."""
28
from bzrlib.tests.compiled.test_dirstate_helpers import (
29
CompiledDirstateHelpersFeature,
33
class BenchmarkDirState(benchmarks.Benchmark):
35
def build_helper(self, layout):
36
"""This is a helper with the common build_??_dirstate funcs.
38
:param layout: [(num_dirs, files_per_dir)]
39
The number of directories per level, and the number of files to put
41
:return: A DirState object with the given layout.
43
self.build_tree(['dir/'])
45
self.build_tree_contents([('file', contents)])
46
file_stat = os.lstat('file')
47
dir_stat = os.lstat('dir')
48
file_sha1 = osutils.sha_string(contents)
50
state = dirstate.DirState.initialize('state')
52
def create_entries(base, layout):
55
num_dirs, num_files = layout[0]
56
for dnum in xrange(num_dirs):
58
path = '%s/%02d_directory' % (base, dnum)
60
path = '%02d_directory' % (dnum,)
61
dir_id = generate_ids.gen_file_id(path)
62
state.add(path, dir_id, 'directory', dir_stat, '')
63
for fnum in xrange(num_files):
64
fname = '%s/%02d_filename' % (path, fnum)
65
file_id = generate_ids.gen_file_id(fname)
66
state.add(fname, file_id, 'file', file_stat, file_sha1)
67
create_entries(path, layout[1:])
68
create_entries(None, layout)
74
def build_10k_dirstate_dirs(self):
75
"""Build a DirState file with 10k directories"""
76
return self.build_helper([(10, 0), (10, 0), (10, 0), (10, 1)])
78
def build_20k_dirstate(self):
79
"""Build a DirState file with 20k records.
81
This approximates a kernel tree, based on the number of directories
82
(1000), and number of files per directory (20) and depth (3).
83
Because DirState doesn't have to have actual disk records, we just add
85
We try to have reasonable filename lengths, as well as a reasonable
88
return self.build_helper([(10, 0), (10, 0), (10, 20)])
90
def test_build_20k_dirblocks(self):
91
state = self.time(self.build_20k_dirstate)
94
entries = list(state._iter_entries())
95
self.assertEqual(21111, len(entries))
99
def test__read_dirblocks_20k_tree_no_parents(self):
100
state = self.build_20k_dirstate()
103
self.assertEqual(dirstate.DirState.NOT_IN_MEMORY,
104
state._dirblock_state)
105
self.time(state._read_dirblocks_if_needed)
109
def do_bisect_list(self, bisect_func):
110
"""Call bisect_dirblock for each path."""
111
# We use self._paths and self._blocks because we expect it to be a very
112
# long list. And the interface for 'self.time()' causes the parameters
113
# to be printed when run with --lsprof-timed. Which is *really* ugly
114
# when the list is thousands of entries.
115
blocks = self._blocks
116
return [bisect_func(blocks, path) for path in self._paths]
118
def do_bisect_list_cached(self, bisect_func):
119
"""Same as do_bisect_list, but cache the split paths"""
121
blocks = self._blocks
122
return [bisect_func(blocks, path, cache=cache) for path in self._paths]
124
def setup_paths_and_offsets(self, state):
125
"""Get a list of paths and expected offsets.
127
This will be used to check do_bisect_list*
129
state._read_dirblocks_if_needed()
131
expected_offsets = [0]
132
for offset, info in enumerate(state._dirblocks):
134
# We already handled the empty path
137
# all paths are of the form ##_directory
138
# so search for ##_director, ##_directory
139
paths.extend([dirname[:-1], dirname])
140
expected_offsets.extend([offset, offset])
142
self._expected_offsets = expected_offsets
143
self._blocks = state._dirblocks
145
def checkOffsets(self, offsets):
146
"""Make sure offsets matches self._expected_offsets"""
147
# These are really long lists, so it is easier to compare them with
148
# assertEqualDiff. So turn them into strings.
149
expected_str = '\n'.join(str(x) for x in self._expected_offsets)
150
offset_str = '\n'.join(str(x) for x in offsets)
151
self.assertEqualDiff(expected_str, offset_str)
153
def test_py_bisect_dirblock(self):
154
state = self.build_10k_dirstate_dirs()
157
self.setup_paths_and_offsets(state)
158
offsets = self.time(self.do_bisect_list, dirstate.py_bisect_dirblock)
159
self.checkOffsets(offsets)
163
def test_py_bisect_dirblock_cached(self):
164
state = self.build_10k_dirstate_dirs()
167
self.setup_paths_and_offsets(state)
168
offsets = self.time(self.do_bisect_list_cached,
169
dirstate.py_bisect_dirblock)
170
self.checkOffsets(offsets)
174
def test_c_bisect_dirblock(self):
175
self.requireFeature(CompiledDirstateHelpersFeature)
176
from bzrlib.compiled.dirstate_helpers import c_bisect_dirblock
177
state = self.build_10k_dirstate_dirs()
180
self.setup_paths_and_offsets(state)
181
offsets = self.time(self.do_bisect_list, c_bisect_dirblock)
182
self.checkOffsets(offsets)