Xla: LSTM model compiles very slowly

Created on 12 Sep 2019  路  16Comments  路  Source: pytorch/xla

I'm trying to train a language model on TPU. I've written some code that took way too long to compile the model for XLA. Here's some dummy code that reproduces my issue. I haven't come across any examples implementing RNNs for NLP in the test cases so maybe I'm doing something wrong?

import torch
import torch_xla
import torch.nn as nn
import time
import model

import torch_xla_py.xla_model as xm
import torch_xla_py.data_parallel as dp

class Loader:
    def __iter__(self):
        return self
    def __next__(self):
        return torch.rand(100, 100, 100)

m = nn.LSTM(100,100)
devices = xm.get_xla_supported_devices()
m = dp.DataParallel(m, device_ids=devices, batchdim=1)

def train_loop(model, loader, device, context):
    h = torch.rand(1, 100, 100).to(device), torch.rand(1, 100, 100).to(device)
    t = 0
    for _, e in loader:
        tic = time.time()
        out = model(e, h)
        toc = time.time()
        print(toc-tic)

m(train_loop, Loader())

I've also dumped XLA logs using export XLA_FLAGS=--xla_dump_to=/tmp/xlalogs and it gave the following logs :

module_0000.after_optimizations.txt
module_0000.after_optimizations-buffer-assignment.txt
module_0000.before_optimizations.txt
module_0000.ir-no-opt.txt
module_0000.ir-no-opt-noconst.txt
module_0000.txt

Most helpful comment

@BadrYoubiIdrissi The code is expected to run much faster after the first iteration as it's hitting cache and it's designed in this way to speed up. We mainly look at the speed after no new graph compilation.
We are currently working on the model coverage but haven't done a language model ourselves yet. If you see the speed is still not good after the second/third iteration, and there're missing lowered ops, we should be able to prioritize those. Thanks!

All 16 comments

Seems like you are using local XLA CPU, not TPU?
With pytorch/xla you cannot measure compile time like that.
Things are done asynchronously and compilation might even not happen within your timed section.
Did you export the environment variables . required to setup the TPU run?

https://github.com/pytorch/xla/blob/master/README.md

export XRT_TPU_CONFIG="tpu_worker;0;<IP of the TPU node>:8470"

You can get the graphs locally by using the XLA_SAVE_TENSORS_FILE and XLA_SAVE_TENSORS_FMT environment variables, or you can use (using out like in your example):

print(torch_xla._XLAC._get_xla_tensors_text([out]))

Or:

print(torch_xla._XLAC._get_xla_tensors_hlo([out]))

While debugging, I also suggest you export:

export XLA_IR_DEBUG=1 XLA_HLO_DEBUG=1

This gives Python location information in the graphs.

I have indeed explicitely used CPU to test the code out and it struck me as peculiar that the test networks compiled quite fast while this one takes around 10 to 20 minutes. The tic toc was to test the native pytorch cpu! But yeah I did notice that the execution was done asynchronously.

So does this mean that it is normal for an LSTM network to compile in such a long time (even on CPU) compared to other modules? I am going to try it out on TPU to see if it is much faster. I'll also check if the graph is correct. Thank you for the debugging suggestions!

What is likely happening there is that you have a model that makes us switch to pytorch CPU device as we do not implement a given operation, so we end up compiling (which is pretty fast on XLA CPU), and then running it.
The slow you are experiencing is the running it part.
If you can:

print(torch_xla._XLAC._xla_metrics_report())

After each step, that will give us a better idea of the operators that we do not lower.

The code seems to run at a normal speed after the first iteration though. I'm going to run with the metrics asap

Metric: OutboundData
  TotalSamples: 3
  Counter: 30.90MB
  ValueRate: 628.23MB / second
  Rate: 60.9892 / second
  Percentiles: 1%=393.75KB; 5%=393.75KB; 10%=393.75KB; 20%=393.75KB; 50%=15.26MB; 80%=15.26MB; 90%=15.26MB; 95%=15.26MB; 99%=15.26MB
Metric: TransferToServerTime
  TotalSamples: 3
  Counter: 043ms23.650us
  ValueRate: 01s268ms36.562us / second
  Rate: 88.419 / second
  Percentiles: 1%=006ms552.906us; 5%=006ms552.906us; 10%=006ms552.906us; 20%=006ms552.906us; 50%=018ms932.658us; 80%=020ms538.086us; 90%=020ms538.086us; 95%=020ms538.086us; 99%=020ms538.086us
Counter: CreateDataHandles
  Value: 14
Counter: CreateXlaTensor
  Value: 1421
Counter: DestroyXlaTensor
  Value: 304
Counter: SyncTensorsToData
  Value: 6
Counter: XRTAllocateFromTensor_Empty
  Value: 10
Counter: XrtSessionCount
  Value: 2

This is what I get when printing metrics in the first iteration.

EDIT : I don't understand how or why but when i change this line out = model(e, h) to model(e,h) the model compiles and runs fast. Do we have to manually delete the variables in the train loop?

I've also tried to run the model by transposing it to the xla device (CPU in this case) and the loop is done without any issues.

This code runs without any issue :

import torch
import torch_xla
import torch.nn as nn
import model

device = 'xla:0'

m = nn.LSTM(100,100).to(device)
a = torch.rand(100, 100, 100).to(device)
h = torch.rand(1, 100, 100).to(device), torch.rand(1, 100, 100).to(device)
for i in range(100):
    out = m(a, h)

From the metrics report you posted, that loop does not do anything.
I mean, we build an IR graph mapping your tensor operations, but you never materialize it (look at the result).

I'm sorry I don't quite get how I can materialize it? Could you explain a little further?

In "normal" pytorch, when you do:

c = a + b
d = c + 3

The tensors c and d are actually calculated at the time that Python line executes.
In pytorch/xla we use an approach called Lazy Tensor which instead of immediately computing the pytorch operations, it builds an IR graph.
It is only when you "look" (ie, print()) at the value, that the whole graph rooted to the materialized value, gets executed.

Okay I think I understand ! So this code

import torch
import torch_xla
import torch.nn as nn
import model

device = 'xla:0'

m = nn.LSTM(100,100).to(device)
a = torch.rand(100, 100, 100).to(device)
h = torch.rand(1, 100, 100).to(device), torch.rand(1, 100, 100).to(device)
for i in range(100):
    out = m(a, h)

only seems to run fast because nothing is actually evaluated ? That would also explain why deleting the variable makes it faster. However it doesn't explain why the code goes faster after the first iteration in the original code (If the graph is built fast and it is only the evaluation that is costly)

If the problem lies in an unsupported operation in the LSTM how would you suggest I go on about training a language model with pytorch/XLA? Has it ever been attempted in your tests?

@BadrYoubiIdrissi The code is expected to run much faster after the first iteration as it's hitting cache and it's designed in this way to speed up. We mainly look at the speed after no new graph compilation.
We are currently working on the model coverage but haven't done a language model ourselves yet. If you see the speed is still not good after the second/third iteration, and there're missing lowered ops, we should be able to prioritize those. Thanks!

It does definitely speed up after the first iteration (nearly instant compared to the first) I'll update this issue once I have measured results on TPU with the language model. Thank you very much for your help!

I finally (successfully) ran some tests on a real TPU and indeed compilation time is good. The same model ran on TPU is approximately 3 times faster (when normalizing with batch size*sequence length) than a GTX 1080. However the TPU's usage is very low (5% to 10%). How could I identify operations that are running slowly? (Maybe those processed on CPU). CPU usage is not at 100% either and Network usage isn't exceptionally large so I'm guessing it's not the data loading that is bottlenecking(?)

The metrics report can give some insights:

print(torch_xla._XLAC._xla_metrics_report())

I am closing this, but if there is further investigation feel free to re-open.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

david-alexander-white picture david-alexander-white  路  6Comments

Arjuna197 picture Arjuna197  路  5Comments

butchland picture butchland  路  7Comments

hrbigelow picture hrbigelow  路  3Comments

myleott picture myleott  路  6Comments