lowvram.py 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165
  1. from collections import namedtuple
  2. import torch
  3. from modules import devices, shared
  4. module_in_gpu = None
  5. cpu = torch.device("cpu")
  6. ModuleWithParent = namedtuple('ModuleWithParent', ['module', 'parent'], defaults=['None'])
  7. def send_everything_to_cpu():
  8. global module_in_gpu
  9. if module_in_gpu is not None:
  10. module_in_gpu.to(cpu)
  11. module_in_gpu = None
  12. def is_needed(sd_model):
  13. return shared.cmd_opts.lowvram or shared.cmd_opts.medvram or shared.cmd_opts.medvram_sdxl and hasattr(sd_model, 'conditioner')
  14. def apply(sd_model):
  15. enable = is_needed(sd_model)
  16. shared.parallel_processing_allowed = not enable
  17. if enable:
  18. setup_for_low_vram(sd_model, not shared.cmd_opts.lowvram)
  19. else:
  20. sd_model.lowvram = False
  21. def setup_for_low_vram(sd_model, use_medvram):
  22. if getattr(sd_model, 'lowvram', False):
  23. return
  24. sd_model.lowvram = True
  25. parents = {}
  26. def send_me_to_gpu(module, _):
  27. """send this module to GPU; send whatever tracked module was previous in GPU to CPU;
  28. we add this as forward_pre_hook to a lot of modules and this way all but one of them will
  29. be in CPU
  30. """
  31. global module_in_gpu
  32. module = parents.get(module, module)
  33. if module_in_gpu == module:
  34. return
  35. if module_in_gpu is not None:
  36. module_in_gpu.to(cpu)
  37. module.to(devices.device)
  38. module_in_gpu = module
  39. # see below for register_forward_pre_hook;
  40. # first_stage_model does not use forward(), it uses encode/decode, so register_forward_pre_hook is
  41. # useless here, and we just replace those methods
  42. first_stage_model = sd_model.first_stage_model
  43. first_stage_model_encode = sd_model.first_stage_model.encode
  44. first_stage_model_decode = sd_model.first_stage_model.decode
  45. def first_stage_model_encode_wrap(x):
  46. send_me_to_gpu(first_stage_model, None)
  47. return first_stage_model_encode(x)
  48. def first_stage_model_decode_wrap(z):
  49. send_me_to_gpu(first_stage_model, None)
  50. return first_stage_model_decode(z)
  51. to_remain_in_cpu = [
  52. (sd_model, 'first_stage_model'),
  53. (sd_model, 'depth_model'),
  54. (sd_model, 'embedder'),
  55. (sd_model, 'model'),
  56. ]
  57. is_sdxl = hasattr(sd_model, 'conditioner')
  58. is_sd2 = not is_sdxl and hasattr(sd_model.cond_stage_model, 'model')
  59. if hasattr(sd_model, 'medvram_fields'):
  60. to_remain_in_cpu = sd_model.medvram_fields()
  61. elif is_sdxl:
  62. to_remain_in_cpu.append((sd_model, 'conditioner'))
  63. elif is_sd2:
  64. to_remain_in_cpu.append((sd_model.cond_stage_model, 'model'))
  65. else:
  66. to_remain_in_cpu.append((sd_model.cond_stage_model, 'transformer'))
  67. # remove several big modules: cond, first_stage, depth/embedder (if applicable), and unet from the model
  68. stored = []
  69. for obj, field in to_remain_in_cpu:
  70. module = getattr(obj, field, None)
  71. stored.append(module)
  72. setattr(obj, field, None)
  73. # send the model to GPU.
  74. sd_model.to(devices.device)
  75. # put modules back. the modules will be in CPU.
  76. for (obj, field), module in zip(to_remain_in_cpu, stored):
  77. setattr(obj, field, module)
  78. # register hooks for those the first three models
  79. if hasattr(sd_model.cond_stage_model, "medvram_modules"):
  80. for module in sd_model.cond_stage_model.medvram_modules():
  81. if isinstance(module, ModuleWithParent):
  82. parent = module.parent
  83. module = module.module
  84. else:
  85. parent = None
  86. if module:
  87. module.register_forward_pre_hook(send_me_to_gpu)
  88. if parent:
  89. parents[module] = parent
  90. elif is_sdxl:
  91. sd_model.conditioner.register_forward_pre_hook(send_me_to_gpu)
  92. elif is_sd2:
  93. sd_model.cond_stage_model.model.register_forward_pre_hook(send_me_to_gpu)
  94. sd_model.cond_stage_model.model.token_embedding.register_forward_pre_hook(send_me_to_gpu)
  95. parents[sd_model.cond_stage_model.model] = sd_model.cond_stage_model
  96. parents[sd_model.cond_stage_model.model.token_embedding] = sd_model.cond_stage_model
  97. else:
  98. sd_model.cond_stage_model.transformer.register_forward_pre_hook(send_me_to_gpu)
  99. parents[sd_model.cond_stage_model.transformer] = sd_model.cond_stage_model
  100. sd_model.first_stage_model.register_forward_pre_hook(send_me_to_gpu)
  101. sd_model.first_stage_model.encode = first_stage_model_encode_wrap
  102. sd_model.first_stage_model.decode = first_stage_model_decode_wrap
  103. if hasattr(sd_model, 'depth_model'):
  104. sd_model.depth_model.register_forward_pre_hook(send_me_to_gpu)
  105. if hasattr(sd_model, 'embedder'):
  106. sd_model.embedder.register_forward_pre_hook(send_me_to_gpu)
  107. if use_medvram:
  108. sd_model.model.register_forward_pre_hook(send_me_to_gpu)
  109. else:
  110. diff_model = sd_model.model.diffusion_model
  111. # the third remaining model is still too big for 4 GB, so we also do the same for its submodules
  112. # so that only one of them is in GPU at a time
  113. stored = diff_model.input_blocks, diff_model.middle_block, diff_model.output_blocks, diff_model.time_embed
  114. diff_model.input_blocks, diff_model.middle_block, diff_model.output_blocks, diff_model.time_embed = None, None, None, None
  115. sd_model.model.to(devices.device)
  116. diff_model.input_blocks, diff_model.middle_block, diff_model.output_blocks, diff_model.time_embed = stored
  117. # install hooks for bits of third model
  118. diff_model.time_embed.register_forward_pre_hook(send_me_to_gpu)
  119. for block in diff_model.input_blocks:
  120. block.register_forward_pre_hook(send_me_to_gpu)
  121. diff_model.middle_block.register_forward_pre_hook(send_me_to_gpu)
  122. for block in diff_model.output_blocks:
  123. block.register_forward_pre_hook(send_me_to_gpu)
  124. def is_enabled(sd_model):
  125. return sd_model.lowvram