Skip to content

Commit d2c8d98

Browse files
committed
docs: update readme and benchmarks
1 parent 2362846 commit d2c8d98

File tree

3 files changed

+94
-60
lines changed

3 files changed

+94
-60
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
[![Coverage](https://raw.githubusercontent.com/aphp/foldedtensor/coverage/coverage.svg)](https://raw.githubusercontent.com/aphp/foldedtensor/coverage/coverage.txt)
1313
[![License](https://img.shields.io/github/license/aphp/foldedtensor?color=x&style=flat-square)](https://github.com/aphp/foldedtensor/blob/main/LICENSE)
1414
![PyPI - Downloads](https://img.shields.io/pypi/dm/foldedtensor?style=flat-square&color=purple)
15+
![Python versions](https://img.shields.io/pypi/pyversions/foldedtensor?style=flat-square)
1516

1617
# FoldedTensor: PyTorch extension for handling deeply nested sequences of variable length
1718

docs/benchmark.md

Lines changed: 36 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,11 @@ This file was generated from [`scripts/benchmark.py`](../scripts/benchmark.py).
77
It compares the performance of `foldedtensor` with various alternatives for padding
88
and working with nested lists and tensors.
99

10-
Versions:
11-
- `torch.__version__ == '2.0.1'`
12-
- `foldedtensor.__version__ == '0.3.3'`
10+
Environment:
11+
- `torch.__version__ == '2.6.0'`
12+
- `foldedtensor.__version__ == '0.4.0'`
13+
- `python == 3.9.20`
14+
- `sys.platform == 'darwin'`
1315

1416

1517
## Case 1 (pad variable lengths nested list)
@@ -20,79 +22,79 @@ nested_list = make_nested_list(32, (50, 100), (25, 30), value=1)
2022

2123
Comparisons:
2224
%timeit python_padding(nested_list)
23-
# 100 loops, best of 5: 13.24 ms per loop
25+
# 100 loops, best of 5: 15.09 ms per loop
2426

2527
%timeit foldedtensor.as_folded_tensor(nested_list)
26-
# 100 loops, best of 5: 0.63 ms per loop
28+
# 100 loops, best of 5: 0.73 ms per loop
2729

2830
```
29-
31+
Speedup against best alternative: **20.67x** :rocket:
3032

3133
## Case 2 (same lengths nested lists)
3234

3335
```python
3436
nested_list = make_nested_list(32, 100, 30, value=1)
3537

3638
%timeit torch.tensor(nested_list)
37-
# 100 loops, best of 5: 6.44 ms per loop
39+
# 100 loops, best of 5: 6.51 ms per loop
3840

3941
%timeit torch.LongTensor(nested_list)
40-
# 100 loops, best of 5: 2.64 ms per loop
42+
# 100 loops, best of 5: 2.78 ms per loop
4143

4244
%timeit python_padding(nested_list)
43-
# 100 loops, best of 5: 16.68 ms per loop
45+
# 100 loops, best of 5: 18.38 ms per loop
4446

4547
%timeit torch.nested.nested_tensor([torch.LongTensor(sub) for sub in nested_list]).to_padded_tensor(0)
46-
# 100 loops, best of 5: 2.90 ms per loop
48+
# 100 loops, best of 5: 3.00 ms per loop
4749

4850
%timeit foldedtensor.as_folded_tensor(nested_list)
49-
# 100 loops, best of 5: 0.96 ms per loop
51+
# 100 loops, best of 5: 1.08 ms per loop
5052

5153
```
52-
54+
Speedup against best alternative: **2.58x** :rocket:
5355

5456
## Case 3 (simple list)
5557

5658
```python
5759
simple_list = make_nested_list(10000, value=1)
5860

5961
%timeit torch.tensor(simple_list)
60-
# 100 loops, best of 5: 0.65 ms per loop
62+
# 100 loops, best of 5: 0.63 ms per loop
6163

6264
%timeit torch.LongTensor(simple_list)
6365
# 100 loops, best of 5: 0.27 ms per loop
6466

6567
%timeit python_padding(simple_list)
66-
# 100 loops, best of 5: 0.27 ms per loop
68+
# 100 loops, best of 5: 0.28 ms per loop
6769

6870
%timeit foldedtensor.as_folded_tensor(simple_list)
6971
# 100 loops, best of 5: 0.08 ms per loop
7072

7173
```
72-
74+
Speedup against best alternative: **3.32x** :rocket:
7375

7476
## Case 4 (same lengths nested lists to flat tensor)
7577

7678
```python
7779
nested_list = make_nested_list(32, 100, 30, value=1)
7880

7981
%timeit torch.tensor(nested_list).view(-1)
80-
# 100 loops, best of 5: 6.67 ms per loop
82+
# 100 loops, best of 5: 6.52 ms per loop
8183

8284
%timeit torch.LongTensor(nested_list).view(-1)
83-
# 100 loops, best of 5: 2.74 ms per loop
85+
# 100 loops, best of 5: 2.76 ms per loop
8486

8587
%timeit python_padding(nested_list).view(-1)
86-
# 100 loops, best of 5: 17.16 ms per loop
88+
# 100 loops, best of 5: 18.62 ms per loop
8789

8890
%timeit foldedtensor.as_folded_tensor(nested_list).view(-1)
89-
# 100 loops, best of 5: 1.02 ms per loop
91+
# 100 loops, best of 5: 1.12 ms per loop
9092

9193
%timeit foldedtensor.as_folded_tensor(nested_list, data_dims=(2,))
92-
# 100 loops, best of 5: 0.95 ms per loop
94+
# 100 loops, best of 5: 1.08 ms per loop
9395

9496
```
95-
97+
Speedup against best alternative: **2.47x** :rocket:
9698
## Case 5 (variable lengths nested lists) to padded embeddings
9799

98100
Nested lists with different lengths (second level lists have lengths between 50 and 150). We compare `foldedtensor` with `torch.nested`.
@@ -102,37 +104,41 @@ nested_list = make_nested_list(32, (50, 150), 30, value=1)
102104
# Padding with 0
103105

104106
%timeit torch.nested.nested_tensor([torch.LongTensor(sub) for sub in nested_list]).to_padded_tensor(0)
105-
# 100 loops, best of 5: 3.11 ms per loop
107+
# 100 loops, best of 5: 3.02 ms per loop
106108

107109
%timeit foldedtensor.as_folded_tensor(nested_list).as_tensor()
108-
# 100 loops, best of 5: 0.90 ms per loop
110+
# 100 loops, best of 5: 1.03 ms per loop
109111

112+
```
113+
Speedup against best alternative: **2.95x** :rocket:
114+
```python
110115
# Padding with 1
111116

112117
%timeit torch.nested.nested_tensor([torch.FloatTensor(sub) for sub in nested_list]).to_padded_tensor(1)
113-
# 100 loops, best of 5: 3.57 ms per loop
118+
# 100 loops, best of 5: 3.72 ms per loop
114119

115120
%timeit x = foldedtensor.as_folded_tensor(nested_list); x.masked_fill_(x.mask, 1)
116-
# 100 loops, best of 5: 1.33 ms per loop
121+
# 100 loops, best of 5: 1.62 ms per loop
117122

118123
```
119-
124+
Speedup against best alternative: **2.30x** :rocket:
120125

121126
## Case 6 (2d padding)
122127

123128
```python
124129
nested_list = make_nested_list(160, (50, 150), value=1)
125130

126131
%timeit python_padding(nested_list)
127-
# 100 loops, best of 5: 1.24 ms per loop
132+
# 100 loops, best of 5: 1.33 ms per loop
128133

129134
%timeit torch.nested.nested_tensor([torch.LongTensor(sub) for sub in nested_list]).to_padded_tensor(0)
130-
# 100 loops, best of 5: 1.09 ms per loop
135+
# 100 loops, best of 5: 1.14 ms per loop
131136

132137
%timeit torch.nn.utils.rnn.pad_sequence([torch.LongTensor(sub) for sub in nested_list], batch_first=True, padding_value=0)
133-
# 100 loops, best of 5: 0.78 ms per loop
138+
# 100 loops, best of 5: 0.86 ms per loop
134139

135140
%timeit foldedtensor.as_folded_tensor(nested_list)
136-
# 100 loops, best of 5: 0.13 ms per loop
141+
# 100 loops, best of 5: 0.15 ms per loop
137142

138143
```
144+
Speedup against best alternative: **5.88x** :rocket:

scripts/benchmark.py

Lines changed: 57 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,16 @@
11
# ruff: noqa: F401, E501
22
import contextlib
33
import random
4+
import subprocess
5+
import sys
46
import warnings
57
from timeit import Timer
68

9+
import foldedtensor # noqa: F401
710
import torch
811
import torch.nested
912
import torch.nn.utils.rnn
1013

11-
import foldedtensor # noqa: F401
12-
1314
warnings.filterwarnings("ignore")
1415

1516
torch.set_default_device("cpu")
@@ -108,6 +109,7 @@ def format_time(dt):
108109
"# %d loop%s, best of %d: %s per loop\n"
109110
% (number, "s" if number != 1 else "", repeat, format_time(best))
110111
)
112+
return best
111113

112114

113115
print(
@@ -120,9 +122,11 @@ def format_time(dt):
120122
It compares the performance of `foldedtensor` with various alternatives for padding
121123
and working with nested lists and tensors.
122124
123-
Versions:
125+
Environment:
124126
- `torch.__version__ == {torch.__version__!r}`
125127
- `foldedtensor.__version__ == {foldedtensor.__version__!r}`
128+
- `python == {sys.version_info.major}.{sys.version_info.minor}.{sys.version_info.micro}`
129+
- `sys.platform == {sys.platform!r}`
126130
"""
127131
)
128132

@@ -139,40 +143,53 @@ def format_time(dt):
139143
exec_and_print("nested_list = make_nested_list(32, (50, 100), (25, 30), value=1)")
140144

141145
print("Comparisons:")
142-
timeit("python_padding(nested_list)")
143-
timeit("foldedtensor.as_folded_tensor(nested_list)")
146+
alt = []
147+
alt.append(timeit("python_padding(nested_list)"))
148+
ft_time = timeit("foldedtensor.as_folded_tensor(nested_list)")
149+
150+
print(f"Speedup against best alternative: **{alt[0] / ft_time:.2f}x** :rocket:")
144151

145152
if 2 in cases:
146153
print("\n## Case 2 (same lengths nested lists)\n")
147154

148155
with block_code():
149156
exec_and_print("nested_list = make_nested_list(32, 100, 30, value=1)")
150-
timeit("torch.tensor(nested_list)")
151-
timeit("torch.LongTensor(nested_list)")
152-
timeit("python_padding(nested_list)")
153-
timeit("torch.nested.nested_tensor([torch.LongTensor(sub) for sub in nested_list]).to_padded_tensor(0)")
154-
timeit("foldedtensor.as_folded_tensor(nested_list)")
157+
alt = []
158+
alt.append(timeit("torch.tensor(nested_list)"))
159+
alt.append(timeit("torch.LongTensor(nested_list)"))
160+
alt.append(timeit("python_padding(nested_list)"))
161+
alt.append(timeit("torch.nested.nested_tensor([torch.LongTensor(sub) for sub in nested_list]).to_padded_tensor(0)"))
162+
ft_time = timeit("foldedtensor.as_folded_tensor(nested_list)")
163+
164+
print(f"Speedup against best alternative: **{min(alt) / ft_time:.2f}x** :rocket:")
155165

156166
if 3 in cases:
157167
print("\n## Case 3 (simple list)\n")
158168

159169
with block_code():
160170
exec_and_print("simple_list = make_nested_list(10000, value=1)")
161-
timeit("torch.tensor(simple_list)")
162-
timeit("torch.LongTensor(simple_list)")
163-
timeit("python_padding(simple_list)")
164-
timeit("foldedtensor.as_folded_tensor(simple_list)")
171+
alt = []
172+
alt.append(timeit("torch.tensor(simple_list)"))
173+
alt.append(timeit("torch.LongTensor(simple_list)"))
174+
alt.append(timeit("python_padding(simple_list)"))
175+
ft_time = timeit("foldedtensor.as_folded_tensor(simple_list)")
176+
177+
print(f"Speedup against best alternative: **{min(alt) / ft_time:.2f}x** :rocket:")
165178

166179
if 4 in cases:
167180
print("\n## Case 4 (same lengths nested lists to flat tensor)\n")
168181

169182
with block_code():
170183
exec_and_print("nested_list = make_nested_list(32, 100, 30, value=1)")
171-
timeit("torch.tensor(nested_list).view(-1)")
172-
timeit("torch.LongTensor(nested_list).view(-1)")
173-
timeit("python_padding(nested_list).view(-1)")
174-
timeit("foldedtensor.as_folded_tensor(nested_list).view(-1)")
175-
timeit("foldedtensor.as_folded_tensor(nested_list, data_dims=(2,))")
184+
alt = []
185+
ft_times = []
186+
alt.append(timeit("torch.tensor(nested_list).view(-1)"))
187+
alt.append(timeit("torch.LongTensor(nested_list).view(-1)"))
188+
alt.append(timeit("python_padding(nested_list).view(-1)"))
189+
ft_times.append(timeit("foldedtensor.as_folded_tensor(nested_list).view(-1)"))
190+
ft_times.append(timeit("foldedtensor.as_folded_tensor(nested_list, data_dims=(2,))"))
191+
192+
print(f"Speedup against best alternative: **{min(alt) / max(ft_times):.2f}x** :rocket:")
176193

177194
if 5 in cases:
178195
print("## Case 5 (variable lengths nested lists) to padded embeddings\n")
@@ -184,26 +201,34 @@ def format_time(dt):
184201

185202
print("# Padding with 0\n")
186203

187-
timeit("torch.nested.nested_tensor([torch.LongTensor(sub) for sub in nested_list]).to_padded_tensor(0)")
188-
timeit("foldedtensor.as_folded_tensor(nested_list).as_tensor()")
204+
nt_time = timeit("torch.nested.nested_tensor([torch.LongTensor(sub) for sub in nested_list]).to_padded_tensor(0)")
205+
ft_time = timeit("foldedtensor.as_folded_tensor(nested_list).as_tensor()")
206+
207+
print(f"Speedup against best alternative: **{nt_time / ft_time:.2f}x** :rocket:")
189208

209+
with block_code():
190210
print("# Padding with 1\n")
191-
timeit("torch.nested.nested_tensor([torch.FloatTensor(sub) for sub in nested_list]).to_padded_tensor(1)")
192-
timeit("x = foldedtensor.as_folded_tensor(nested_list); x.masked_fill_(x.mask, 1)")
211+
nt_time = timeit("torch.nested.nested_tensor([torch.FloatTensor(sub) for sub in nested_list]).to_padded_tensor(1)")
212+
ft_time = timeit("x = foldedtensor.as_folded_tensor(nested_list); x.masked_fill_(x.mask, 1)")
213+
214+
print(f"Speedup against best alternative: **{nt_time / ft_time:.2f}x** :rocket:")
193215

194216
if 6 in cases:
195217
print("\n## Case 6 (2d padding)\n")
196218

197219
with block_code():
198220
exec_and_print("nested_list = make_nested_list(160, (50, 150), value=1)")
199221

200-
timeit("python_padding(nested_list)")
201-
timeit("torch.nested.nested_tensor([torch.LongTensor(sub) for sub in nested_list]).to_padded_tensor(0)")
202-
timeit(
203-
"torch.nn.utils.rnn.pad_sequence([torch.LongTensor(sub) for sub in nested_list], batch_first=True, padding_value=0)")
204-
timeit("foldedtensor.as_folded_tensor(nested_list)")
222+
alt = []
223+
alt.append(timeit("python_padding(nested_list)"))
224+
alt.append(timeit("torch.nested.nested_tensor([torch.LongTensor(sub) for sub in nested_list]).to_padded_tensor(0)"))
225+
alt.append(timeit("torch.nn.utils.rnn.pad_sequence([torch.LongTensor(sub) for sub in nested_list], batch_first=True, padding_value=0)"))
226+
ft_time = timeit("foldedtensor.as_folded_tensor(nested_list)")
227+
228+
print(f"Speedup against best alternative: **{min(alt) / ft_time:.2f}x** :rocket:")
205229

206230
if 7 in cases:
231+
# Test case not working yet
207232

208233
def sum_all_words_per_sample(ft):
209234
lengths = ft.lengths
@@ -235,8 +260,10 @@ def sum_all_words_per_sample(ft):
235260
"nt = embedder(nt)\n"
236261
)
237262

238-
timeit("nt.sum(dim=1)")
239-
timeit("sum_all_words_per_sample(ft)")
263+
nt_time = timeit("nt.sum(dim=1)")
264+
ft_time = timeit("sum_all_words_per_sample(ft)")
265+
266+
print(f"Speedup against best alternative: **{nt_time / ft_time:.2f}x** :rocket:")
240267

241268
# timeit("embedder(ft)")
242269
# timeit("embedder(ft).refold(0, 1)")

0 commit comments

Comments
 (0)