Tensorflowtts: Is multi-gpu training possible?

Created on 2 Jul 2020  路  27Comments  路  Source: TensorSpeech/TensorFlowTTS

I am trying to train tacotron2 with my own dataset which is a little bit larger than LJSpeech and getting OOM errors with batch size 32 and 1 gpu as log below.

Is there any way to use multi-gpu training with the current code? Is reducing the batch size only option? I am not familiar with tf2.2 so can't come up with multi-gpu implementation right away, it is very simple with PyTorch though.

2020-07-02 04:54:41.501441: I tensorflow/core/common_runtime/bfc_allocator.cc:1002] Sum Total of in-use $
hunks: 10.13GiB                                                                                       
2020-07-02 04:54:41.501455: I tensorflow/core/common_runtime/bfc_allocator.cc:1004] total_region_allocat$
d_bytes_: 10874802688 memory_limit_: 10874802688 available bytes: 0 curr_region_allocation_bytes_: 21749$
05376                                                                                                    2020-07-02 04:54:41.501476: I tensorflow/core/common_runtime/bfc_allocator.cc:1010] Stats:               Limit:                 10874802688                                                                       InUse:                 10874794496                                                                       
MaxInUse:              10874802688                   
NumAllocs:                  825693                                                              
MaxAllocSize:            627179520                                                                       

2020-07-02 04:54:41.503330: W tensorflow/core/common_runtime/bfc_allocator.cc:439] *********************$
******************************************************************************  
2020-07-02 04:54:41.503383: W tensorflow/core/framework/op_kernel.cc:1753] OP_REQUIRES failed at cast_op$
cc:109 : Resource exhausted: OOM when allocating tensor with shape[32,256] and type float on /job:localh$
st/replica:0/task:0/device:GPU:0 by allocator GPU_0_bfc                    
Traceback (most recent call last):
  File "examples/tacotron2/train_tacotron2.py", line 594, in <module>
    main()
  File "examples/tacotron2/train_tacotron2.py", line 586, in main
    resume=args.resume,
  File "examples/tacotron2/train_tacotron2.py", line 392, in fit
    self.run()
  File "/usr/local/lib/python3.6/dist-packages/tensorflow_tts/trainers/base_trainer.py", line 72, in run
    self._train_epoch()
  File "/usr/local/lib/python3.6/dist-packages/tensorflow_tts/trainers/base_trainer.py", line 94, in _tr$
in_epoch
    self._train_step(batch)
  File "examples/tacotron2/train_tacotron2.py", line 117, in _train_step
    charactor, char_length, mel, mel_length, guided_attention
  File "/usr/local/lib/python3.6/dist-packages/tensorflow/python/eager/def_function.py", line 580, in __$
all__
    result = self._call(*args, **kwds)
  File "/usr/local/lib/python3.6/dist-packages/tensorflow/python/eager/def_function.py", line 644, in _ca
ll                                                                                                       
    return self._stateless_fn(*args, **kwds)
  File "/usr/local/lib/python3.6/dist-packages/tensorflow/python/eager/function.py", line 2420, in __cal$
__
    return graph_function._filtered_call(args, kwargs)  # pylint: disable=protected-access
  File "/usr/local/lib/python3.6/dist-packages/tensorflow/python/eager/function.py", line 1665, in _filt$
red_call
    self.captured_inputs)
  File "/usr/local/lib/python3.6/dist-packages/tensorflow/python/eager/function.py", line 1746, in _call$
flat
    ctx, args, cancellation_manager=cancellation_manager))
  File "/usr/local/lib/python3.6/dist-packages/tensorflow/python/eager/function.py", line 598, in call
    ctx=ctx)
  File "/usr/local/lib/python3.6/dist-packages/tensorflow/python/eager/execute.py", line 60, in quick_ex$
cute
    inputs, attrs, num_outputs)
tensorflow.python.framework.errors_impl.ResourceExhaustedError: 2 root error(s) found.
  (0) Resource exhausted:  OOM when allocating tensor with shape[32,512] and type float on /job:localhos$
/replica:0/task:0/device:GPU:0 by allocator GPU_0_bfc
         [[{{node tacotron2/StatefulPartitionedCall/decoder/while/body/_848/decoder_cell/Sum}}]]
Hint: If you want to see a list of allocated tensors when OOM happens, add report_tensor_allocations_upo$
_oom to RunOptions for current allocation info.

         [[tacotron2/StatefulPartitionedCall/decoder/while/LoopCond/_1042/_144]]
Hint: If you want to see a list of allocated tensors when OOM happens, add report_tensor_allocations_upo$
_oom to RunOptions for current allocation info.

  (1) Resource exhausted:  OOM when allocating tensor with shape[32,512] and type float on /job:localhos$
/replica:0/task:0/device:GPU:0 by allocator GPU_0_bfc
         [[{{node tacotron2/StatefulPartitionedCall/decoder/while/body/_848/decoder_cell/Sum}}]]
Hint: If you want to see a list of allocated tensors when OOM happens, add report_tensor_allocations_upo$
_oom to RunOptions for current allocation info.

0 successful operations.
0 derived errors ignored. [Op:__inference__one_step_tacotron2_1349493]

Function call stack:
_one_step_tacotron2 -> _one_step_tacotron2

[train]:   0%|                                                                | 0/200000 [04:56<?, ?it/s$
Feature Request 馃 enhancement 馃殌

All 27 comments

@jun-danieloh we will support multi-gpu soon. it take some time :)). At this time, you need move long samples to validation folder :)) so the training dataset is not too long then you can be train with large batch_size

@dathudeptrai I can't come up with any way to parse long samples within training dataset. It consists of 40k wav files. Do you have anything in mind? I am planning to reduce the batch size.

@jun-danieloh reduce batch_size is still ok. we will implement multi-gpu soon. You can write the function to check the length of mel on training samples then move it into dev folder :)), i think it's ez :D.

@dathudeptrai What do you mean by dev folder?

@jun-danieloh i mean dump/valid :))

@dathudeptrai Do I need to modify metadata.csv file too?

No :D

@dathudeptrai
Then what I can do right away is just cut the half number of wav files and move them to dump/valid? Do you recommend this?

My quick try with batch 16 and even batch 1 fails...

2020-07-02 06:33:31.094803: W tensorflow/core/common_runtime/bfc_allocator.cc:439] **********************
******************************************************************************                           
2020-07-02 06:33:31.094864: W tensorflow/core/framework/op_kernel.cc:1753] OP_REQUIRES failed at reductio
n_ops_common.h:193 : Resource exhausted: OOM when allocating tensor with shape[16,512] and type float on 
/job:localhost/replica:0/task:0/device:GPU:0 by allocator GPU_0_bfc                                      
Traceback (most recent call last):                                                                       
  File "examples/tacotron2/train_tacotron2.py", line 594, in <module>                                    
    main()                                                                                               
  File "examples/tacotron2/train_tacotron2.py", line 586, in main                                        
    resume=args.resume,                                                                                  
  File "examples/tacotron2/train_tacotron2.py", line 392, in fit
    self.run()          
  File "/usr/local/lib/python3.6/dist-packages/tensorflow_tts/trainers/base_trainer.py", line 72, in run
    self._train_epoch()
  File "/usr/local/lib/python3.6/dist-packages/tensorflow_tts/trainers/base_trainer.py", line 94, in _tr$
in_epoch                                  
    self._train_step(batch)
  File "examples/tacotron2/train_tacotron2.py", line 117, in _train_step                                 
    charactor, char_length, mel, mel_length, guided_attention
  File "/usr/local/lib/python3.6/dist-packages/tensorflow/python/eager/def_function.py", line 580, in __$all__                       
    result = self._call(*args, **kwds)
  File "/usr/local/lib/python3.6/dist-packages/tensorflow/python/eager/def_function.py", line 644, in _c$
ll                            
    return self._stateless_fn(*args, **kwds)
  File "/usr/local/lib/python3.6/dist-packages/tensorflow/python/eager/function.py", line 2420, in __cal$
__
    return graph_function._filtered_call(args, kwargs)  # pylint: disable=protected-access
  File "/usr/local/lib/python3.6/dist-packages/tensorflow/python/eager/function.py", line 1665, in _filt$
red_call
    self.captured_inputs)
  File "/usr/local/lib/python3.6/dist-packages/tensorflow/python/eager/function.py", line 1746, in _call$
flat
    ctx, args, cancellation_manager=cancellation_manager))
  File "/usr/local/lib/python3.6/dist-packages/tensorflow/python/eager/function.py", line 598, in call
    ctx=ctx)
  File "/usr/local/lib/python3.6/dist-packages/tensorflow/python/eager/execute.py", line 60, in quick_ex$
cute
    inputs, attrs, num_outputs)
tensorflow.python.framework.errors_impl.ResourceExhaustedError: 2 root error(s) found.
  (0) Resource exhausted:  OOM when allocating tensor with shape[16,512] and type float on /job:localhost
/replica:0/task:0/device:GPU:0 by allocator GPU_0_bfc
         [[{{node tacotron2/StatefulPartitionedCall/decoder/while/body/_848/decoder_cell/Sum}}]]
Hint: If you want to see a list of allocated tensors when OOM happens, add report_tensor_allocations_upon
_oom to RunOptions for current allocation info.

         [[tacotron2/StatefulPartitionedCall/decoder/while/LoopCond/_1042/_144]]
Hint: If you want to see a list of allocated tensors when OOM happens, add report_tensor_allocations_upon
_oom to RunOptions for current allocation info.

  (1) Resource exhausted:  OOM when allocating tensor with shape[16,512] and type float on /job:localhost
/replica:0/task:0/device:GPU:0 by allocator GPU_0_bfc
         [[{{node tacotron2/StatefulPartitionedCall/decoder/while/body/_848/decoder_cell/Sum}}]]
Hint: If you want to see a list of allocated tensors when OOM happens, add report_tensor_allocations_upon
_oom to RunOptions for current allocation info.

0 successful operations.
0 derived errors ignored. [Op:__inference__one_step_tacotron2_1349493]

Function call stack:
_one_step_tacotron2 -> _one_step_tacotron2

[train]:   0%|                                                                | 0/200000 [05:08<?, ?it/s]

can you provide ur length mel histogram ?, if the length is too large, it's not ez to training Tacotron-2 even multi-gpu. Max mel length on training should be < 1000.

how can I check mel length histogram?

@dathudeptrai Then I need to filter out wav files based on max mel length, 1000. Is this because input dimension of Encoder is not big enough?

@dathudeptrai I got the mel length histogram as below. As you mentioned, I need to cut off ones having mel length more than 1000, right? Is your frame length 50ms as in paper? I am using 10ms frame length.

--- Mel Lengths Histogram ---
        bucket       | _____ Total Counts
[16      , 72.20000) |  495
[72.20000, 128.4000) |  1185
[128.4000, 184.6000) | 2266
[184.6000, 240.8000) |  2698
[240.8000,      297) |  2685
[297     , 353.2000) |  2885
[353.2000, 409.4000) | 3132
[409.4000, 465.6000) | 3683
[465.6000, 521.8000) | 4392
[521.8000,      578) | 4442
[578     , 634.2000) | 3523
[634.2000, 690.4000) | 2333
[690.4000, 746.6000) | 1637
[746.6000, 802.8000) | 1218
[802.8000,      859) | 841
[859     , 915.2000) | 560
[915.2000, 971.4000) | 364
[971.4000, 1027.600) | 252
[1027.600, 1083.800) | 217
[1083.800,     1140) | 152

My hope size is 256 so for ljspeech 22.05k it is 256/22.05k = 11.6 ms. What is ur gpu ?

@dathudeptrai My GPU is 1080TI 12GB. I guess your code sets the max_mel_length based on the largest one and pads other shorter ones so it is OOM? But I still don't understand why OOM still happens with batch 1.

There is something wrong if batch 1 oom, did you enable mixed precision? Pls set it to 0

@dathudeptrai Sorry. With batch 1, it is not OOM issue but a dimension issue. I pasted the log below. Let me continue debugging it. Please give a hint if you have any.

[train]:   0%|                                                                | 0/200000 [00:00<?, ?it/s]
2020-07-03 11:28:19.475545: I tensorflow/core/kernels/data/shuffle_dataset_op.cc:184] Filling up shuffle 
buffer (this may take a while): 1276 of 42871                                                            
2020-07-03 11:28:29.480409: I tensorflow/core/kernels/data/shuffle_dataset_op.cc:184] Filling up shuffle 
buffer (this may take a while): 2555 of 42871                                                            
2020-07-03 11:28:39.481795: I tensorflow/core/kernels/data/shuffle_dataset_op.cc:184] Filling up shuffle 
buffer (this may take a while): 3834 of 42871                                                            
2020-07-03 11:28:49.478325: I tensorflow/core/kernels/data/shuffle_dataset_op.cc:184] Filling up shuffle 
buffer (this may take a while): 5040 of 42871                                                            
2020-07-03 11:28:59.476736: I tensorflow/core/kernels/data/shuffle_dataset_op.cc:184] Filling up shuffle 
buffer (this may take a while): 6228 of 42871                                                            
2020-07-03 11:29:09.482035: I tensorflow/core/kernels/data/shuffle_dataset_op.cc:184] Filling up shuffle 
buffer (this may take a while): 7487 of 42871                                                            
2020-07-03 11:29:19.478093: I tensorflow/core/kernels/data/shuffle_dataset_op.cc:184] Filling up shuffle 
buffer (this may take a while): 8719 of 42871                                                            
2020-07-03 11:29:29.485334: I tensorflow/core/kernels/data/shuffle_dataset_op.cc:184] Filling up shuffle 
buffer (this may take a while): 9957 of 42871                                                            
2020-07-03 11:29:39.478977: I tensorflow/core/kernels/data/shuffle_dataset_op.cc:184] Filling up shuffle 
buffer (this may take a while): 11151 of 42871                                                           
2020-07-03 11:29:49.482108: I tensorflow/core/kernels/data/shuffle_dataset_op.cc:184] Filling up shuffle 
buffer (this may take a while): 12351 of 42871                                                           
2020-07-03 11:29:59.481329: I tensorflow/core/kernels/data/shuffle_dataset_op.cc:184] Filling up shuffle 
buffer (this may take a while): 13515 of 42871                                                           
2020-07-03 11:30:09.477389: I tensorflow/core/kernels/data/shuffle_dataset_op.cc:184] Filling up shuffle 
buffer (this may take a while): 14763 of 42871                                                           
2020-07-03 11:30:19.478691: I tensorflow/core/kernels/data/shuffle_dataset_op.cc:184] Filling up shuffle 
buffer (this may take a while): 15985 of 42871                                                           
2020-07-03 11:30:29.477388: I tensorflow/core/kernels/data/shuffle_dataset_op.cc:184] Filling up shuffle 
buffer (this may take a while): 17201 of 42871 
2020-07-03 11:30:39.481878: I tensorflow/core/kernels/data/shuffle_dataset_op.cc:184] Filling up shuffle 
buffer (this may take a while): 18390 of 42871                                                           
2020-07-03 11:30:49.477608: I tensorflow/core/kernels/data/shuffle_dataset_op.cc:184] Filling up shuffle 
buffer (this may take a while): 19593 of 42871                                                           
2020-07-03 11:30:59.479518: I tensorflow/core/kernels/data/shuffle_dataset_op.cc:184] Filling up shuffle 
buffer (this may take a while): 20769 of 42871                                                           
2020-07-03 11:31:09.481159: I tensorflow/core/kernels/data/shuffle_dataset_op.cc:184] Filling up shuffle 
buffer (this may take a while): 21988 of 42871                                                           
2020-07-03 11:31:19.475988: I tensorflow/core/kernels/data/shuffle_dataset_op.cc:184] Filling up shuffle 
buffer (this may take a while): 23201 of 42871                                                           
2020-07-03 11:31:29.475032: I tensorflow/core/kernels/data/shuffle_dataset_op.cc:184] Filling up shuffle 
buffer (this may take a while): 24356 of 42871                                                           
2020-07-03 11:31:39.484137: I tensorflow/core/kernels/data/shuffle_dataset_op.cc:184] Filling up shuffle 
buffer (this may take a while): 25471 of 42871                                                           
2020-07-03 11:31:49.479655: I tensorflow/core/kernels/data/shuffle_dataset_op.cc:184] Filling up shuffle 
buffer (this may take a while): 26653 of 42871                                                           
2020-07-03 11:31:59.487958: I tensorflow/core/kernels/data/shuffle_dataset_op.cc:184] Filling up shuffle 
buffer (this may take a while): 27802 of 42871                                                           
2020-07-03 11:32:09.478593: I tensorflow/core/kernels/data/shuffle_dataset_op.cc:184] Filling up shuffle 
buffer (this may take a while): 28940 of 42871                                                           
2020-07-03 11:32:19.477170: I tensorflow/core/kernels/data/shuffle_dataset_op.cc:184] Filling up shuffle 
buffer (this may take a while): 30125 of 42871                                                           
2020-07-03 11:32:29.481314: I tensorflow/core/kernels/data/shuffle_dataset_op.cc:184] Filling up shuffle 
buffer (this may take a while): 31276 of 42871                                                           
2020-07-03 11:32:39.480089: I tensorflow/core/kernels/data/shuffle_dataset_op.cc:184] Filling up shuffle 
buffer (this may take a while): 32445 of 42871                                                           
2020-07-03 11:32:49.475567: I tensorflow/core/kernels/data/shuffle_dataset_op.cc:184] Filling up shuffle 
buffer (this may take a while): 33594 of 42871                                                           
2020-07-03 11:32:59.477557: I tensorflow/core/kernels/data/shuffle_dataset_op.cc:184] Filling up shuffle 
buffer (this may take a while): 34741 of 42871                                                           
2020-07-03 11:33:09.476903: I tensorflow/core/kernels/data/shuffle_dataset_op.cc:184] Filling up shuffle 
buffer (this may take a while): 35927 of 42871                                                           
2020-07-03 11:33:19.477186: I tensorflow/core/kernels/data/shuffle_dataset_op.cc:184] Filling up shuffle 
buffer (this may take a while): 37104 of 42871                                                           
2020-07-03 11:33:29.481865: I tensorflow/core/kernels/data/shuffle_dataset_op.cc:184] Filling up shuffle 
buffer (this may take a while): 38213 of 42871                                                           
2020-07-03 11:33:39.480696: I tensorflow/core/kernels/data/shuffle_dataset_op.cc:184] Filling up shuffle 
buffer (this may take a while): 39332 of 42871                                                           
2020-07-03 11:33:49.477449: I tensorflow/core/kernels/data/shuffle_dataset_op.cc:184] Filling up shuffle 
buffer (this may take a while): 40367 of 42871                                                           
2020-07-03 11:33:59.478973: I tensorflow/core/kernels/data/shuffle_dataset_op.cc:184] Filling up shuffle 
buffer (this may take a while): 41414 of 42871  
2020-07-03 11:34:09.478397: I tensorflow/core/kernels/data/shuffle_dataset_op.cc:184] Filling up shuffle 
buffer (this may take a while): 42466 of 42871                                                           
2020-07-03 11:34:13.293087: I tensorflow/core/kernels/data/shuffle_dataset_op.cc:233] Shuffle buffer fill
ed.                                                                                                      
2020-07-03 11:34:21.603309: E tensorflow/core/grappler/optimizers/meta_optimizer.cc:563] function_optimiz
er failed: Invalid argument: Node 'tacotron2/StatefulPartitionedCall/encoder/bilstm/forward_lstm/Stateful
PartitionedCall_Func/tacotron2/StatefulPartitionedCall/output/_325': Connecting to invalid output 29 of s
ource node tacotron2/StatefulPartitionedCall/encoder/bilstm/forward_lstm/StatefulPartitionedCall which ha
s 29 outputs.                                                                                            
2020-07-03 11:34:22.141478: E tensorflow/core/grappler/optimizers/meta_optimizer.cc:563] shape_optimizer 
failed: Out of range: src_output = 29, but num_outputs is only 29                                        
2020-07-03 11:34:22.352684: E tensorflow/core/grappler/optimizers/meta_optimizer.cc:563] layout failed: O
ut of range: src_output = 29, but num_outputs is only 29                                                 
Traceback (most recent call last):                                                                       
  File "examples/tacotron2/train_tacotron2.py", line 594, in <module>                                    
    main()                                                                                               
  File "examples/tacotron2/train_tacotron2.py", line 586, in main   
    resume=args.resume,                                                                          
  File "examples/tacotron2/train_tacotron2.py", line 392, in fit
    self.run()
  File "/usr/local/lib/python3.6/dist-packages/tensorflow_tts/trainers/base_trainer.py", line 72, in run
    self._train_epoch()
  File "/usr/local/lib/python3.6/dist-packages/tensorflow_tts/trainers/base_trainer.py", line 94, in _tra
in_epoch
    self._train_step(batch)
  File "examples/tacotron2/train_tacotron2.py", line 117, in _train_step
    charactor, char_length, mel, mel_length, guided_attention
  File "/usr/local/lib/python3.6/dist-packages/tensorflow/python/eager/def_function.py", line 580, in __c
all__
    result = self._call(*args, **kwds)
  File "/usr/local/lib/python3.6/dist-packages/tensorflow/python/eager/def_function.py", line 644, in _ca
ll
    return self._stateless_fn(*args, **kwds)
  File "/usr/local/lib/python3.6/dist-packages/tensorflow/python/eager/function.py", line 2420, in __call
__
    return graph_function._filtered_call(args, kwargs)  # pylint: disable=protected-access
  File "/usr/local/lib/python3.6/dist-packages/tensorflow/python/eager/function.py", line 1665, in _filte
red_call
    self.captured_inputs)
  File "/usr/local/lib/python3.6/dist-packages/tensorflow/python/eager/function.py", line 1746, in _call_
flat
    ctx, args, cancellation_manager=cancellation_manager))
  File "/usr/local/lib/python3.6/dist-packages/tensorflow/python/eager/function.py", line 598, in call
    ctx=ctx)
  File "/usr/local/lib/python3.6/dist-packages/tensorflow/python/eager/execute.py", line 60, in quick_exe
cute
    inputs, attrs, num_outputs)
tensorflow.python.framework.errors_impl.InvalidArgumentError:  Incompatible shapes: [1,2000,80] vs. [1,25
47,80]
         [[node mean_absolute_error/sub (defined at examples/tacotron2/train_tacotron2.py:157) ]] [Op:__i
nference__one_step_tacotron2_1349465]

Errors may have originated from an input operation.
Input Source operations connected to node mean_absolute_error/sub:
 tacotron2/StatefulPartitionedCall (defined at examples/tacotron2/train_tacotron2.py:142)
 mel (defined at examples/tacotron2/train_tacotron2.py:117)

Function call stack:
_one_step_tacotron2

[train]:   0%|                                                                | 0/200000 [06:21<?, ?it/s]
root@TensorflowTTS#

@jun-danieloh see the error

incompatible shapes: [1,2000,80] vs. [1,2547,80]

That mean there is a sample has a length = 2547 while in my implementation, max length for decoder is 2000 (see https://github.com/TensorSpeech/TensorflowTTS/blob/master/tensorflow_tts/models/tacotron2.py#L745). So ur mel_length statistic is wrong somehow :D

@dathudeptrai I managed to run it finally. Thanks :)

@jun-danieloh can you share ur problem and how you fix it ?, may helpful with other people.

@dathudeptrai

As you suggested, I noticed that 5 out of ~40k wav files are over 20 secs. Your hop size is 11.6 ms and max_mel_length is 2000 so max wav file length that your code support without OOM would be about 11.6m*2000=~20secs(For exact caculations, we need to know window size). I didn't do math precisely but just cut out wav files longer than 20 sec and now it is running fine without OOM issues. In order to support wav files longer than 20 secs, we might need to support multi-GPU training or padding input sequences dynamically.

@jun-danieloh padding dynamically also supported, you just need to set use_fixed_shapes=False, but note that i don't really know if dynamic shape is faster than fixed shape. In my experiments, fixed shape faster than dynamic shape but there is a man report that dynamic shape is faster, maybe you want try it by urself. Multigpu/tpu will support on the next version.

@dathudeptrai Can we avoid OOM issues with dynamic shape at least? I didn't try this yet. When do you expect for the next version release?

@jun-danieloh no, it based on ur max sample length. Dynamic shape padding batch based on the max sample length, so if ur max sample length make OOM so dynamic shape can not solve this problem. Atleast 1 month for next release, it may include chinese support, multigpu/tpu support.

@dathudeptrai A quick question. Current code is running evaluation every 500 iterations. I think it too overheaded so how do I release this number?

@jun-danieloh you can pull my multi-gpu branch to training with mutiGPU :)), i need add some doc before merge to master. https://github.com/TensorSpeech/TensorflowTTS/pull/120

Was this page helpful?
0 / 5 - 0 ratings

Related issues

jun-danieloh picture jun-danieloh  路  6Comments

yanglu1994 picture yanglu1994  路  3Comments

sujeendran picture sujeendran  路  8Comments

tienanh-1999 picture tienanh-1999  路  6Comments

linhld0811 picture linhld0811  路  3Comments