{
  "__type": "IngestedDoc",
  "__tag": 4010,
  "_content": {},
  "_ordered_sections": [],
  "item_file": null,
  "item_line": null,
  "item_type": null,
  "aliases": [],
  "example_section_data": {
    "__type": "Section",
    "__tag": 4015,
    "children": [],
    "title": [],
    "level": 0,
    "target": null
  },
  "see_also": [],
  "signature": null,
  "references": null,
  "qa": "reference:random:multithreading",
  "arbitrary": [
    {
      "__type": "Section",
      "__tag": 4015,
      "children": [
        {
          "__type": "Paragraph",
          "__tag": 4045,
          "children": [
            {
              "__type": "Text",
              "__tag": 4046,
              "value": "The four core distributions ("
            },
            {
              "__type": "InlineRole",
              "__tag": 4003,
              "value": "~.Generator.random",
              "domain": null,
              "role": "meth",
              "inventory": null
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": ", "
            },
            {
              "__type": "InlineRole",
              "__tag": 4003,
              "value": "~.Generator.standard_normal",
              "domain": null,
              "role": "meth",
              "inventory": null
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": ", "
            },
            {
              "__type": "InlineRole",
              "__tag": 4003,
              "value": "~.Generator.standard_exponential",
              "domain": null,
              "role": "meth",
              "inventory": null
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": ", and "
            },
            {
              "__type": "InlineRole",
              "__tag": 4003,
              "value": "~.Generator.standard_gamma",
              "domain": null,
              "role": "meth",
              "inventory": null
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": ") all allow existing arrays to be filled using the "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "out"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " keyword argument. Existing arrays need to be contiguous and well-behaved (writable and aligned). Under normal circumstances, arrays created using the common constructors such as "
            },
            {
              "__type": "CrossRef",
              "__tag": 4002,
              "value": "numpy.empty",
              "reference": {
                "__type": "RefInfo",
                "__tag": 4000,
                "module": "numpy",
                "version": "*",
                "kind": "api",
                "path": "numpy:empty"
              },
              "kind": "module"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " will satisfy these requirements."
            }
          ]
        },
        {
          "__type": "Admonition",
          "__tag": 4056,
          "kind": "seealso",
          "base_type": "note",
          "children": [
            {
              "__type": "AdmonitionTitle",
              "__tag": 4055,
              "children": [
                {
                  "__type": "Text",
                  "__tag": 4046,
                  "value": "seealso "
                }
              ]
            },
            {
              "__type": "Paragraph",
              "__tag": 4045,
              "children": [
                {
                  "__type": "CrossRef",
                  "__tag": 4002,
                  "value": "thread_safety",
                  "reference": {
                    "__type": "LocalRef",
                    "__tag": 4022,
                    "kind": "docs",
                    "path": "reference:thread_safety"
                  },
                  "kind": "exists"
                },
                {
                  "__type": "Text",
                  "__tag": 4046,
                  "value": " for general information about thread safety in NumPy."
                }
              ]
            }
          ]
        },
        {
          "__type": "Paragraph",
          "__tag": 4045,
          "children": [
            {
              "__type": "Text",
              "__tag": 4046,
              "value": "This example makes use of "
            },
            {
              "__type": "CrossRef",
              "__tag": 4002,
              "value": "concurrent.futures",
              "reference": {
                "__type": "RefInfo",
                "__tag": 4000,
                "module": "concurrent",
                "version": "*",
                "kind": "api",
                "path": "concurrent.futures"
              },
              "kind": "module"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " to fill an array using multiple threads.  Threads are long-lived so that repeated calls do not require any additional overheads from thread creation."
            }
          ]
        },
        {
          "__type": "Paragraph",
          "__tag": 4045,
          "children": [
            {
              "__type": "Text",
              "__tag": 4046,
              "value": "The random numbers generated are reproducible in the sense that the same seed will produce the same outputs, given that the number of threads does not change."
            }
          ]
        },
        {
          "__type": "Code",
          "__tag": 4050,
          "value": "from numpy.random import default_rng, SeedSequence\nimport multiprocessing\nimport concurrent.futures\nimport numpy as np\n\nclass MultithreadedRNG:\n    def __init__(self, n, seed=None, threads=None):\n        if threads is None:\n            threads = multiprocessing.cpu_count()\n        self.threads = threads\n\n        seq = SeedSequence(seed)\n        self._random_generators = [default_rng(s)\n                                   for s in seq.spawn(threads)]\n\n        self.n = n\n        self.executor = concurrent.futures.ThreadPoolExecutor(threads)\n        self.values = np.empty(n)\n        self.step = np.ceil(n / threads).astype(np.int_)\n\n    def fill(self):\n        def _fill(random_state, out, first, last):\n            random_state.standard_normal(out=out[first:last])\n\n        futures = {}\n        for i in range(self.threads):\n            args = (_fill,\n                    self._random_generators[i],\n                    self.values,\n                    i * self.step,\n                    (i + 1) * self.step)\n            futures[self.executor.submit(*args)] = i\n        concurrent.futures.wait(futures)\n\n    def __del__(self):\n        self.executor.shutdown(False)",
          "execution_status": null
        },
        {
          "__type": "Paragraph",
          "__tag": 4045,
          "children": [
            {
              "__type": "Text",
              "__tag": 4046,
              "value": "The multithreaded random number generator can be used to fill an array. The "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "values"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " attributes shows the zero-value before the fill and the random value after."
            }
          ]
        },
        {
          "__type": "Code",
          "__tag": 4050,
          "value": "In [2]: mrng = MultithreadedRNG(10000000, seed=12345)\n   ...: print(mrng.values[-1])\nOut[2]: 0.0\n\nIn [3]: mrng.fill()\n   ...: print(mrng.values[-1])\nOut[3]: 2.4545724517479104",
          "execution_status": null
        },
        {
          "__type": "Paragraph",
          "__tag": 4045,
          "children": [
            {
              "__type": "Text",
              "__tag": 4046,
              "value": "The time required to produce using multiple threads can be compared to the time required to generate using a single thread."
            }
          ]
        },
        {
          "__type": "Code",
          "__tag": 4050,
          "value": "In [4]: print(mrng.threads)\n   ...: %timeit mrng.fill()\n\nOut[4]: 4\n   ...: 32.8 ms ± 2.71 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)",
          "execution_status": null
        },
        {
          "__type": "Paragraph",
          "__tag": 4045,
          "children": [
            {
              "__type": "Text",
              "__tag": 4046,
              "value": "The single threaded call directly uses the BitGenerator."
            }
          ]
        },
        {
          "__type": "Code",
          "__tag": 4050,
          "value": "In [5]: values = np.empty(10000000)\n   ...: rg = default_rng()\n   ...: %timeit rg.standard_normal(out=values)\n\nOut[5]: 99.6 ms ± 222 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)",
          "execution_status": null
        },
        {
          "__type": "Paragraph",
          "__tag": 4045,
          "children": [
            {
              "__type": "Text",
              "__tag": 4046,
              "value": "The gains are substantial and the scaling is reasonable even for arrays that are only moderately large. The gains are even larger when compared to a call that does not use an existing array due to array creation overhead."
            }
          ]
        },
        {
          "__type": "Code",
          "__tag": 4050,
          "value": "In [6]: rg = default_rng()\n   ...: %timeit rg.standard_normal(10000000)\n\nOut[6]: 125 ms ± 309 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)",
          "execution_status": null
        },
        {
          "__type": "Paragraph",
          "__tag": 4045,
          "children": [
            {
              "__type": "Text",
              "__tag": 4046,
              "value": "Note that if "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "threads"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " is not set by the user, it will be determined by "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "multiprocessing.cpu_count()"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": "."
            }
          ]
        },
        {
          "__type": "Code",
          "__tag": 4050,
          "value": "In [7]: # simulate the behavior for `threads=None`, if the machine had only one thread\n   ...: mrng = MultithreadedRNG(10000000, seed=12345, threads=1)\n   ...: print(mrng.values[-1])\nOut[7]: 1.1800150052158556",
          "execution_status": null
        }
      ],
      "title": [
        {
          "__type": "Text",
          "__tag": 4046,
          "value": "Multithreaded generation"
        }
      ],
      "level": 0,
      "target": null
    }
  ],
  "local_refs": []
}