Visitar URL original
Add flop counter utility · pytorch/pytorch@cea1bfe · GitHub
Skip to content

Commit cea1bfe

Browse files
committed
Add flop counter utility
ghstack-source-id: 7d04ddd Pull Request resolved: #95751
1 parent a0fc96d commit cea1bfe

2 files changed

Lines changed: 484 additions & 0 deletions

File tree

‎test/test_flop_counter.py‎

Lines changed: 161 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,161 @@
1+
# Owner(s): ["module: unknown"]
2+
3+
import torch
4+
from torch.testing._internal.common_utils import TestCase, run_tests
5+
import torch.utils.flop_counter
6+
import unittest
7+
8+
try:
9+
from torchvision import models as torchvision_models
10+
HAS_TORCHVISION = True
11+
except ImportError:
12+
HAS_TORCHVISION = False
13+
skipIfNoTorchVision = unittest.skipIf(not HAS_TORCHVISION, "no torchvision")
14+
15+
def FlopCounterMode(*args, **kwargs):
16+
return torch.utils.flop_counter.FlopCounterMode(*args, **kwargs, display=False)
17+
18+
def get_total_flops(mode):
19+
return str(sum([v for _, v in mode.flop_counts["Global"].items()]))
20+
21+
def T(*shape, requires_grad=False):
22+
return torch.randn(*shape, requires_grad=requires_grad)
23+
24+
class TestFlopCounter(TestCase):
25+
def test_flop_counter_variety(self):
26+
mode = FlopCounterMode()
27+
mod = torch.nn.Linear(9, 10)
28+
with mode:
29+
torch.mm(T(4, 5), T(5, 6))
30+
torch.addmm(T(4, 6), T(4, 5), T(5, 6), beta=0.5, alpha=0.5)
31+
torch.matmul(T(5, 6), T(6, 7))
32+
torch.einsum("ab,bc->ac", T(6, 7), T(7, 8))
33+
mod(T(8, 9))
34+
35+
self.assertExpectedInline(get_total_flops(mode), """3012""")
36+
37+
def test_op(self):
38+
mode = FlopCounterMode()
39+
with mode:
40+
torch.mm(T(4, 5), T(5, 6))
41+
# 4 * 6 * 2 * 5 = 240
42+
self.assertExpectedInline(get_total_flops(mode), """240""")
43+
44+
with mode:
45+
torch.bmm(T(3, 4, 5), T(3, 5, 6))
46+
# 3 * 4 * 6 * 2 * 5 = 720
47+
self.assertExpectedInline(get_total_flops(mode), """720""")
48+
49+
with mode:
50+
torch.addmm(T(4, 6), T(4, 5), T(5, 6))
51+
torch.addmm(T(4, 1), T(4, 5), T(5, 6))
52+
torch.addmm(T(6), T(4, 5), T(5, 6))
53+
54+
# 4 * 6 * 2 * 5 = 240
55+
self.assertExpectedInline(get_total_flops(mode), """720""")
56+
57+
with mode:
58+
torch.baddbmm(T(3, 4, 6), T(3, 4, 5), T(3, 5, 6))
59+
60+
# 3 * 4 * 6 * 2 * 5 = 720
61+
self.assertExpectedInline(get_total_flops(mode), """720""")
62+
63+
with mode:
64+
torch.conv2d(T(2, 3, 6, 6), T(6, 3, 4, 4), padding=1)
65+
66+
# out_image_size = 2 * 5 * 5
67+
# kernel_size = 4 * 4
68+
# c_out = 6
69+
# c_in = 3
70+
# out_image_size * kernel_size * c_out * 2 * c_in
71+
72+
# NB: I don't think this properly accounts for padding?
73+
self.assertExpectedInline(get_total_flops(mode), """28800""")
74+
75+
with mode:
76+
torch.conv1d(T(2, 3, 6), T(6, 3, 4), padding=1)
77+
78+
# out_image_size = 2 * 5
79+
# kernel_size = 4
80+
# c_out = 6
81+
# c_in = 3
82+
# out_image_size * kernel_size * c_out * 2 * c_in
83+
84+
# NB: I don't think this properly accounts for padding?
85+
self.assertExpectedInline(get_total_flops(mode), """1440""")
86+
87+
def test_backward(self):
88+
mode = FlopCounterMode()
89+
with mode:
90+
a = T(4, 5, requires_grad=True)
91+
a = torch.mm(a, T(5, 6))
92+
a = a.unsqueeze(0).expand(7, 4, 6)
93+
a = torch.bmm(a, T(7, 6, 7))
94+
a.sum().backward()
95+
96+
self.assertExpectedInline(get_total_flops(mode), """5184""")
97+
98+
def test_torchscript(self):
99+
def foo(x):
100+
return torch.mm(x, x)
101+
mode = FlopCounterMode()
102+
with mode:
103+
foo(T(5, 5))
104+
unscripted_flops = get_total_flops(mode)
105+
ts_foo = torch.jit.script(foo)
106+
with mode:
107+
ts_foo(T(5, 5))
108+
self.assertEqual(unscripted_flops, get_total_flops(mode))
109+
110+
def test_autograd_op(self):
111+
class _CustomOp(torch.autograd.Function):
112+
@staticmethod
113+
def forward(ctx, input: torch.Tensor) -> torch.Tensor:
114+
return torch.mm(input, input)
115+
116+
@staticmethod
117+
def backward(ctx, grad_output: torch.Tensor) -> torch.Tensor:
118+
return torch.mm(grad_output, grad_output) + torch.mm(grad_output, grad_output)
119+
120+
a = T(5, 5, requires_grad=True)
121+
mode = FlopCounterMode()
122+
with mode:
123+
a = _CustomOp.apply(a)
124+
a.sum().backward()
125+
126+
self.assertExpectedInline(get_total_flops(mode), """750""")
127+
128+
129+
130+
@skipIfNoTorchVision
131+
def test_module(self):
132+
resnet18 = torchvision_models.resnet18()
133+
mode = FlopCounterMode(resnet18)
134+
with mode:
135+
a = T(1, 3, 224, 224)
136+
resnet18(a)
137+
138+
self.assertExpectedInline(get_total_flops(mode), """3628146688""")
139+
layer1_conv_flops = mode.flop_counts['layer1'][torch.ops.aten.convolution]
140+
layer1_conv_back_flops = mode.flop_counts['layer1'][torch.ops.aten.convolution_backward]
141+
self.assertExpectedInline(str(layer1_conv_flops), """0""")
142+
self.assertExpectedInline(str(layer1_conv_back_flops), """0""")
143+
144+
def test_custom(self):
145+
mode = FlopCounterMode(custom_mapping={torch.ops.aten.add: lambda *args: 5})
146+
with mode:
147+
a = T(4, 5)
148+
a + a
149+
150+
self.assertExpectedInline(get_total_flops(mode), """5""")
151+
152+
def test_noop(self):
153+
mode = FlopCounterMode()
154+
with mode:
155+
T(4, 5).cos()
156+
157+
158+
159+
160+
if __name__ == '__main__':
161+
run_tests()

0 commit comments

Comments
 (0)