QuantizedConv2d with stride=(2,2) works extremely slow

e_sh: <p>I’ve discovered that issue when I was trying to understand why does my quantized version of EfficientNet-b4 work slower than the float one.</p>
<p>I use torch 1.6.0, fbgemm default config (<code>torch.quantization.get_default_qconfig('fbgemm')</code>)</p>
<p>torchprof library shows that _depthwise_conv in blocks 2, 6, 10, 22 are the key problem: they are 3-10 times slower than in float model!<br>
These layers differ from others only with (2, 2) stride instead of (1,1), (3,3), (5,5). I do not give the full list of hassle-free layers in the screenshot, but I’ve checked that</p>
<p>Is this the expected behavior? What could be the reason?<br>
<div class="lightbox-wrapper"><a class="lightbox" href="https://discuss.pytorch.org/uploads/default/original/3X/f/2/f2ed60f7ef226a86affdfa4300e8d0cfda013b79.png" data-download-href="https://discuss.pytorch.org/uploads/default/f2ed60f7ef226a86affdfa4300e8d0cfda013b79" title="stride_issue"><img src="https://discuss.pytorch.org/uploads/default/optimized/3X/f/2/f2ed60f7ef226a86affdfa4300e8d0cfda013b79_2_531x500.png" alt="stride_issue" data-base62-sha1="yF281YseuD5DDNtqgcM3ecjT3D3" width="531" height="500" srcset="https://discuss.pytorch.org/uploads/default/optimized/3X/f/2/f2ed60f7ef226a86affdfa4300e8d0cfda013b79_2_531x500.png, https://discuss.pytorch.org/uploads/default/optimized/3X/f/2/f2ed60f7ef226a86affdfa4300e8d0cfda013b79_2_796x750.png 1.5x, https://discuss.pytorch.org/uploads/default/optimized/3X/f/2/f2ed60f7ef226a86affdfa4300e8d0cfda013b79_2_1062x1000.png 2x" data-dominant-color="F7F7F7"><div class="meta"><svg class="fa d-icon d-icon-far-image svg-icon" aria-hidden="true"><use href="#far-image"></use></svg><span class="filename">stride_issue</span><span class="informations">1201×1130 143 KB</span><svg class="fa d-icon d-icon-discourse-expand svg-icon" aria-hidden="true"><use href="#discourse-expand"></use></svg></div></a></div></p>

Shisho_Sama: <p>How do you do the quantization? manually or through graph quantization?<br>
Does graph quantization give you the same issues?</p>

Vasiliy_Kuznetsov: <p>cc <a class="mention" href="/u/dskhudia">@dskhudia</a> for whether this is expected in fbgemm</p>

dskhudia: <p>It works much better if you use equal padding for your 2, 6, 10 and 22 depthwise. For equal padding, depthwise conv goes through a fast path.</p>
<p>equal padding for (3, 3) kernel would be (1, 1) and (5, 5) it would be (2, 2). (Similar to the padding you have for (3, 3) and (5, 5) kernel sizes for other convolutions).</p>

e_sh: <p>unfortunately, I do not understand what do you mean by saying “manually or through graph quantization”</p>
<p>If you mean, do I profile Torchscript model or original python one: I’ve done both, and both of them show bad performance. The layer-by-layer profiling via torchprof does not work with TorchScript models, but I expect that “slow” layers stay slow both in original model and in a Torchscript one, even if timing original model is not so precise</p>
<p>Quantization is done the same way as in <a href="https://pytorch.org/tutorials/advanced/static_quantization_tutorial.html" rel="noopener nofollow ugc">MobileNet tutorial</a>.<br>
Here is the code snippet:</p>
<pre><code class="lang-auto">def try_config(qconfig, one_thread_inference=True, calibration_max_batches=None, metrics_max_batches=None):
    # Fuse modules  (my own implementation for EfficientNet; do you need it?)
    q_model = fuse_modules()
    
    # apply config (this looks complex because I do not quantize _conv_stem (1st convolution)
    for block in q_model.feature_extractor._blocks:
        block.qconfig = qconfig    
    q_model.quant = QuantStub(qconfig)
    print(qconfig)

    torch.quantization.prepare(q_model, inplace=True,
                               white_list=(
                                   torch,nn.Conv2d,
                                   torch.nn.BatchNorm2d,
                                   torch.quantization.stubs.QuantStub,
                                   torch.nn.quantized.modules.functional_modules.FloatFunctional
                               ))
    q_model.eval()
    
    print('Post Training Quantization Prepare: Inserting Observers')
    if one_thread_inference:
        torch.set_num_threads(multiprocessing.cpu_count())
    inference(q_model, dev_loader, max_batches=calibration_max_batches)  # custom func just for model inference

    print('Post Training Quantization: Calibration done')

    # Convert to quantized model
    torch.quantization.convert(q_model, inplace=True)
    print('Post Training Quantization: Convert done')

   
</code></pre>