MegaBlocks

repository·main·Indexed 23 days ago

https://github.com/databricks/megablocks

A lightweight library for efficient Mixture-of-Experts (MoE) training. It implements 'dropless-MoE' (dMoE) using block-sparse operations to avoid token dropping while maintaining hardware efficiency. MegaBlocks provides scripts for pre-training Transformer MoE and dMoE language models and supports grouped GEMM for Hopper-generation GPUs.

Tokens
3.3K
Snippets
11
Records
14
Agent score
32%

What's inside MegaBlocks

  1. Use `assert` for invariants, not data validation

    main

    Use assert only for verifying invariants (often for type checking) or in test cases. Do not use assert for data validation, as assertions can be disabled in production using the -O flag.

    Correct data validation:

    if parameter is None:
        raise ValueError("parameter must be specified and cannot be None")
  2. Design Public APIs

    main

    Public APIs (those without a leading underscore in the path) must follow these rules:

    1. Documentation: All public APIs must have a docstring.
    2. Type Annotations: All parameters must have type annotations.
    3. Minimize Imports: Use native Python or PyTorch types for parameters whenever possible. It is acceptable to use a Union where one option is a primitive to allow users to pass strings instead of custom objects.
      # Example: allowing a string or a custom Device object
      def __init__(self, device: Union[str, Device]):
          if isinstance(device, str):
              device = Device(device)
    4. Sequence Parameters: If a parameter accepts a sequence, allow None or a singleton to simplify the API. Use helpers like ensure_tuple to normalize inputs.
      from typing import Optional, Sequence, Union
      from torch import Tensor
      from composer.utils import ensure_tuple
      
      def foo(x: Optional[Union[Tensor, Sequence[Tensor]]]) -> tuple[Tensor, ...]:
          return ensure_tuple(x)
  3. Export public members with `__all__`

    main

    All public modules must define __all__ to explicitly list the members that should be re-exported. This limits what from XXX import * imports and ensures documentation only includes intended exported members.

    __all__ = ["MemoryMonitor"]
    
    class MemoryMonitor(Callback):
        ...
  4. Handle falsy values in functions

    main

    When designing functions, avoid checking for falsy values (like None) inside the callee if it results in a no-op. Instead, require the caller to handle the check. This ensures the function itself remains focused on its primary task.

    Anti-pattern (Don't):

    def configure_deepspeed(deepspeed_config: Optional[dict]):
        if deepspeed_config is None:
            return  # The callee performs a no-op check
        ...

    Correct pattern (Do):

    def configure_deepspeed(deepspeed_config: dict):
        ...
    
    def trainer(deepspeed_config: Optional[dict]):
        if deepspeed_config is not None:
            # The caller handles the check
            configure_deepspeed(deepspeed_config)
        ...
    def configure_deepspeed(deepspeed_config: dict):
        ...
    
    def trainer(deepspeed_config: Optional[dict]):
        if deepspeed_config is not None:
            configure_deepspeed(deepspeed_config)
        ...
  5. Manage conditional dependencies and imports

    main

    For non-core dependencies (e.g., specific models or algorithms), follow these steps to keep the base installation minimal:

    1. Specify in setup.py: Add the dependency to the extra_deps dictionary. Users can then install it via pip install 'megablocks[extra_name]'.
    2. Conditional Imports: Use a try/except block with MissingConditionalImportError to provide clear error messages if the dependency is missing.
    from composer import Callback
    from composer.utils import MissingConditionalImportError
    
    class SystemMetricsMonitor(Callback):
        try:
            import pynvml
        except ImportError as e:
            raise MissingConditionalImportError(
                extra_deps_group="system_metrics_monitor",
                conda_package="pynvml",
                conda_channel="conda-forge",
            ) from e

    If the dependency is core to MegaBlocks, add it to install_requires in setup.py and requirements.run in meta.yaml.

  6. Write and run doctests

    main

    MegaBlocks uses .. doctest or .. testcode directives to provide executable examples in docstrings. These are verified in CI/CD to ensure documentation remains accurate.

    Writing Doctests

    • Use .. testcode:: for examples shown in the docs. Do not use .. code-block:: for Python examples as they are not tested.
    • Use .. testsetup:: for setup code that should run before the test but not be displayed in the documentation.
    • For global test fixtures, use docs/source/doctest_fixtures.py. For specific setup, use .. testsetup:: to avoid polluting the global namespace.

    Running Doctests

    You must complete an HTML build before running doctests to ensure all tests are identified.

    1. Activate your virtual environment.
    2. Navigate to the docs folder.
    3. Run make clean.
    4. Run make html.
    5. Run make doctest.
    import torch
    from typing import Optional
    
    def my_function(x: Optional[torch.Tensor]) -> torch.Tensor:
        """blah function
    
        Args:
            input (torch.Tensor): Your guess.
    
        Returns:
            torch.Tensor: How good your input is.
    
        Raises:
            ValueError: If your input is negative.
    
        Example:
            .. testsetup::
    
                # optional setup section, not shown in docs
                import torch
                x = torch.randn(42)
    
    
            .. testcode::
    
                # shown in docs; runs after testsetup
                my_function(x)
        """
        ...
    source path/to/megablocks_venv/bin/activate
    cd megablocks/docs
    make clean
    make html
    make doctest 2>/dev/null # For more verbosity, do not direct stderr to /dev/null
  7. Validate type annotations with PyRight

    main

    MegaBlocks uses pyright to validate type annotations. You can run it manually via pre-commit:

    pre-commit run pyright --all-files

    Debugging PyRight errors

    1. Handling Unions/Optional types: If a variable could be None, add an explicit check or an assert to satisfy the type checker.
      from typing import Union
      
      def foo(x: Union[int, None]):
          if x is None:
              raise TypeError("x must be an integer, not None!")
          return x + 5  # valid
    2. Type Casting: Use typing.cast when PyRight cannot infer the correct type.
    3. Silencing errors: As a last resort, use # type: ignore. Always include the specific error message on the following line so others understand why it was silenced.
      # type: ignore error_code_here
  8. Format docstrings using Google Style

    main

    MegaBlocks uses Google Style Docstrings. All public APIs must be documented.

    Key formatting rules:

    1. Content: Include a summary of the function/class, arguments, return statements (if not None), and custom exceptions.
    2. Class __init__: Document arguments for the __init__ signature under the class-level docstring. Do not create a separate docstring for __init__.
    3. Argument Annotations: Include the type. If an argument has a default value, specify optional in the type annotation and state the default value in the description.
    4. Returns: Document the return type and description. For multiple return values (e.g., tuples), document each element individually.
    from typing import Optional, Union
    
    def foo(bar: int):
        """Foo.
    
        Args:
            bar (int): Required bar.
        """
        ...
    
    def foo2(bar: int = 42):
        """Foo2.
    
        Args:
            bar (int, optional): The first Argument. Default: ``42``.
        """
        ...
    
    def foo3(bar: Optional[int] = None):
        """Foo3.
    
        Args:
            bar (int, optional): The first Argument. Default: ``None``.
        """
        ...
    
    def foo4(bar: Union[int, str] = 42):
        """Foo4.
    
        Args:
            bar (int | str, optional): The first Argument. Default: ``42``.
        """
        ...
    
    def foo5(bar: int) -> int:
        """Foo5.
    
        Args:
            bar (int): Required bar.
    
        Returns:
            int: Description of return statement.
        """
        ...
    
    def foo6(bar: int) -> tuple[int, str]:
        """Foo6.
    
        Args:
            bar (int): Required bar.
    
        Returns:
            a (int): Returned value.
            b (str): Returned value.
        """
        ...
  9. Set up pre-commit hooks for MegaBlocks

    main

    MegaBlocks uses pre-commit to enforce style checks. To configure and install the hooks in your development environment, run:

    pip install '.[dev]'  # if not already installed
    pre-commit install

    Once installed, hooks run automatically before each commit. You can also run them manually:

    pre-commit run           # run all hooks on changed files
    pre-commit run --all-files  # run all hooks on all files
    pip install '.[dev]'
    pre-commit install
  10. Use MegaBlocks for MoE training

    main

    MegaBlocks provides scripts for pre-training Transformer MoE and dMoE language models.

    • Pre-training scripts: Located in the megablocks/ top-level directory.
    • Experiment launch scripts: Located in the exp/ directory for the quickest way to get started.

    Requirement: These scripts require a dataset in Megatron-LM's format. You can follow the Megatron-LM data preprocessing instructions to prepare your data.

  11. Build and view documentation locally

    main

    To build and preview MegaBlocks documentation locally, follow these steps. Note that Jenkins treats Sphinx warnings as errors, so it is recommended to build locally to catch and fix warnings before submitting a PR.

    1. Build the HTML: In one terminal, activate your virtual environment, navigate to the docs folder, and run make clean and make html.
    2. Serve the docs: In a second terminal, navigate to the docs folder and start a local HTTP server pointing to the build directory.
    3. View: Open http://localhost:8000 in your browser.
    # Terminal 1: Build
    source path/to/megablocks_venv/bin/activate
    cd megablocks/docs
    make clean
    make html
    
    # Terminal 2: Serve
    cd megablocks/docs
    python3 -m http.server --directory _build/html/