|
| 1 | +diff --git a/gensim/utils.py b/gensim/utils.py |
| 2 | +index 2abd5179e09671de1ea9ef48a9dc3fc4bce9b465..20bad4857bf4e159bc0e244522aa21bd41af8712 100644 |
| 3 | +--- a/gensim/utils.py |
| 4 | ++++ b/gensim/utils.py |
| 5 | +@@ -1302,90 +1302,68 @@ |
| 6 | + self.q.put(wrapped_chunk.pop(), block=True) |
| 7 | + |
| 8 | + |
| 9 | +-# Multiprocessing on Windows (and on OSX with python3.8+) uses "spawn" mode, which |
| 10 | +-# causes issues with pickling. |
| 11 | +-# So for these two platforms, use simpler serial processing in `chunkize`. |
| 12 | +-# See https://github.com/RaRe-Technologies/gensim/pull/2800#discussion_r410890171 |
| 13 | +-if os.name == 'nt' or (sys.platform == "darwin" and sys.version_info >= (3, 8)): |
| 14 | +- def chunkize(corpus, chunksize, maxsize=0, as_numpy=False): |
| 15 | +- """Split `corpus` into fixed-sized chunks, using :func:`~gensim.utils.chunkize_serial`. |
| 16 | ++def chunkize(corpus, chunksize, maxsize=0, as_numpy=False): |
| 17 | ++ """Split `corpus` into fixed-sized chunks, using :func:`~gensim.utils.chunkize_serial`. |
| 18 | + |
| 19 | +- Parameters |
| 20 | +- ---------- |
| 21 | +- corpus : iterable of object |
| 22 | +- An iterable. |
| 23 | +- chunksize : int |
| 24 | +- Split `corpus` into chunks of this size. |
| 25 | +- maxsize : int, optional |
| 26 | +- Ignored. For interface compatibility only. |
| 27 | +- as_numpy : bool, optional |
| 28 | +- Yield chunks as `np.ndarray` s instead of lists? |
| 29 | +- |
| 30 | +- Yields |
| 31 | +- ------ |
| 32 | +- list OR np.ndarray |
| 33 | +- "chunksize"-ed chunks of elements from `corpus`. |
| 34 | +- |
| 35 | +- """ |
| 36 | +- if maxsize > 0: |
| 37 | +- entity = "Windows" if os.name == 'nt' else "OSX with python3.8+" |
| 38 | +- warnings.warn("detected %s; aliasing chunkize to chunkize_serial" % entity) |
| 39 | +- for chunk in chunkize_serial(corpus, chunksize, as_numpy=as_numpy): |
| 40 | +- yield chunk |
| 41 | +-else: |
| 42 | +- def chunkize(corpus, chunksize, maxsize=0, as_numpy=False): |
| 43 | +- """Split `corpus` into fixed-sized chunks, using :func:`~gensim.utils.chunkize_serial`. |
| 44 | +- |
| 45 | +- Parameters |
| 46 | +- ---------- |
| 47 | +- corpus : iterable of object |
| 48 | +- An iterable. |
| 49 | +- chunksize : int |
| 50 | +- Split `corpus` into chunks of this size. |
| 51 | +- maxsize : int, optional |
| 52 | +- If > 0, prepare chunks in a background process, filling a chunk queue of size at most `maxsize`. |
| 53 | +- as_numpy : bool, optional |
| 54 | +- Yield chunks as `np.ndarray` instead of lists? |
| 55 | +- |
| 56 | +- Yields |
| 57 | +- ------ |
| 58 | +- list OR np.ndarray |
| 59 | +- "chunksize"-ed chunks of elements from `corpus`. |
| 60 | ++ Parameters |
| 61 | ++ ---------- |
| 62 | ++ corpus : iterable of object |
| 63 | ++ An iterable. |
| 64 | ++ chunksize : int |
| 65 | ++ Split `corpus` into chunks of this size. |
| 66 | ++ maxsize : int, optional |
| 67 | ++ If > 0, prepare chunks in a background process, filling a chunk queue of size at most `maxsize`. |
| 68 | ++ as_numpy : bool, optional |
| 69 | ++ Yield chunks as `np.ndarray` instead of lists? |
| 70 | + |
| 71 | +- Notes |
| 72 | +- ----- |
| 73 | +- Each chunk is of length `chunksize`, except the last one which may be smaller. |
| 74 | +- A once-only input stream (`corpus` from a generator) is ok, chunking is done efficiently via itertools. |
| 75 | ++ Yields |
| 76 | ++ ------ |
| 77 | ++ list OR np.ndarray |
| 78 | ++ "chunksize"-ed chunks of elements from `corpus`. |
| 79 | + |
| 80 | +- If `maxsize > 0`, don't wait idly in between successive chunk `yields`, but rather keep filling a short queue |
| 81 | +- (of size at most `maxsize`) with forthcoming chunks in advance. This is realized by starting a separate process, |
| 82 | +- and is meant to reduce I/O delays, which can be significant when `corpus` comes from a slow medium |
| 83 | +- like HDD, database or network. |
| 84 | ++ Notes |
| 85 | ++ ----- |
| 86 | ++ Each chunk is of length `chunksize`, except the last one which may be smaller. |
| 87 | ++ A once-only input stream (`corpus` from a generator) is ok, chunking is done efficiently via itertools. |
| 88 | + |
| 89 | +- If `maxsize == 0`, don't fool around with parallelism and simply yield the chunksize |
| 90 | +- via :func:`~gensim.utils.chunkize_serial` (no I/O optimizations). |
| 91 | ++ If `maxsize > 0`, don't wait idly in between successive chunk `yields`, but rather keep filling a short queue |
| 92 | ++ (of size at most `maxsize`) with forthcoming chunks in advance. This is realized by starting a separate process, |
| 93 | ++ and is meant to reduce I/O delays, which can be significant when `corpus` comes from a slow medium |
| 94 | ++ like HDD, database or network. |
| 95 | + |
| 96 | +- Yields |
| 97 | +- ------ |
| 98 | +- list of object OR np.ndarray |
| 99 | +- Groups based on `iterable` |
| 100 | ++ If `maxsize == 0`, don't fool around with parallelism and simply yield the chunksize |
| 101 | ++ via :func:`~gensim.utils.chunkize_serial` (no I/O optimizations). |
| 102 | + |
| 103 | +- """ |
| 104 | +- assert chunksize > 0 |
| 105 | ++ Yields |
| 106 | ++ ------ |
| 107 | ++ list of object OR np.ndarray |
| 108 | ++ Groups based on `iterable` |
| 109 | + |
| 110 | ++ """ |
| 111 | ++ assert chunksize > 0 |
| 112 | ++ |
| 113 | ++ # 1. Evaluate the start method dynamically at runtime |
| 114 | ++ # 2. Only attempt multiprocessing if maxsize > 0 AND the method is 'fork' |
| 115 | ++ # See https://github.com/RaRe-Technologies/gensim/pull/2800#discussion_r410890171 |
| 116 | ++ if maxsize > 0 and multiprocessing.get_start_method() == "fork": |
| 117 | ++ q = multiprocessing.Queue(maxsize=maxsize) |
| 118 | ++ worker = InputQueue(q, corpus, chunksize, maxsize=maxsize, as_numpy=as_numpy) |
| 119 | ++ worker.daemon = True |
| 120 | ++ worker.start() |
| 121 | ++ while True: |
| 122 | ++ chunk = [q.get(block=True)] |
| 123 | ++ if chunk[0] is None: |
| 124 | ++ break |
| 125 | ++ yield chunk.pop() |
| 126 | ++ else: |
| 127 | ++ # Fallback to serial processing |
| 128 | + if maxsize > 0: |
| 129 | +- q = multiprocessing.Queue(maxsize=maxsize) |
| 130 | +- worker = InputQueue(q, corpus, chunksize, maxsize=maxsize, as_numpy=as_numpy) |
| 131 | +- worker.daemon = True |
| 132 | +- worker.start() |
| 133 | +- while True: |
| 134 | +- chunk = [q.get(block=True)] |
| 135 | +- if chunk[0] is None: |
| 136 | +- break |
| 137 | +- yield chunk.pop() |
| 138 | +- else: |
| 139 | +- for chunk in chunkize_serial(corpus, chunksize, as_numpy=as_numpy): |
| 140 | +- yield chunk |
| 141 | ++ # We only warn if they asked for multiprocessing but we have to deny it |
| 142 | ++ current_method = multiprocessing.get_start_method() |
| 143 | ++ warnings.warn(f"start method is '{current_method}', not 'fork'; aliasing chunkize to chunkize_serial") |
| 144 | ++ |
| 145 | ++ for chunk in chunkize_serial(corpus, chunksize, as_numpy=as_numpy): |
| 146 | ++ yield chunk |
| 147 | + |
| 148 | + |
| 149 | + def smart_extension(fname, ext): |
0 commit comments