From fdbfd0729a85dcdd38427f68dec23358cb5aa868 Mon Sep 17 00:00:00 2001 From: jeremiedbb Date: Tue, 21 Feb 2023 14:50:39 +0100 Subject: [PATCH 01/21] try using _OPENMP --- sklearn/cluster/_k_means_lloyd.pyx | 29 ++++++++++++++++------------- sklearn/utils/_openmp_helpers.pxd | 16 ++++++++++++++++ 2 files changed, 32 insertions(+), 13 deletions(-) diff --git a/sklearn/cluster/_k_means_lloyd.pyx b/sklearn/cluster/_k_means_lloyd.pyx index 63e5ee3530d6e..97247302a8f31 100644 --- a/sklearn/cluster/_k_means_lloyd.pyx +++ b/sklearn/cluster/_k_means_lloyd.pyx @@ -4,8 +4,12 @@ # fused types and when the array may be read-only (for instance when it's # provided by the user). This is fixed in cython > 0.3. -IF SKLEARN_OPENMP_PARALLELISM_ENABLED: +from ..utils._openmp_helpers cimport USE_OPENMP +from ..utils._openmp_helpers cimport omp_lock_t + +if USE_OPENMP: cimport openmp + from cython cimport floating from cython.parallel import prange, parallel from libc.stdlib cimport malloc, calloc, free @@ -94,8 +98,8 @@ def lloyd_iter_chunked_dense( floating *centers_new_chunk floating *weight_in_clusters_chunk floating *pairwise_distances_chunk - IF SKLEARN_OPENMP_PARALLELISM_ENABLED: - openmp.omp_lock_t lock + + omp_lock_t lock # count remainder chunk in total number of chunks n_chunks += n_samples != n_chunks * n_samples_chunk @@ -106,7 +110,7 @@ def lloyd_iter_chunked_dense( if update_centers: memset(¢ers_new[0, 0], 0, n_clusters * n_features * sizeof(floating)) memset(&weight_in_clusters[0], 0, n_clusters * sizeof(floating)) - IF SKLEARN_OPENMP_PARALLELISM_ENABLED: + if USE_OPENMP: openmp.omp_init_lock(&lock) with nogil, parallel(num_threads=n_threads): @@ -135,7 +139,7 @@ def lloyd_iter_chunked_dense( # reduction from local buffers. if update_centers: - IF SKLEARN_OPENMP_PARALLELISM_ENABLED: + if USE_OPENMP: # The lock is necessary to avoid race conditions when aggregating # info from different thread-local buffers. openmp.omp_set_lock(&lock) @@ -143,7 +147,7 @@ def lloyd_iter_chunked_dense( weight_in_clusters[j] += weight_in_clusters_chunk[j] for k in range(n_features): centers_new[j, k] += centers_new_chunk[j * n_features + k] - IF SKLEARN_OPENMP_PARALLELISM_ENABLED: + if USE_OPENMP: openmp.omp_unset_lock(&lock) free(centers_new_chunk) @@ -151,7 +155,7 @@ def lloyd_iter_chunked_dense( free(pairwise_distances_chunk) if update_centers: - IF SKLEARN_OPENMP_PARALLELISM_ENABLED: + if USE_OPENMP: openmp.omp_destroy_lock(&lock) _relocate_empty_clusters_dense(X, sample_weight, centers_old, centers_new, weight_in_clusters, labels) @@ -292,8 +296,7 @@ def lloyd_iter_chunked_sparse( floating *centers_new_chunk floating *weight_in_clusters_chunk - IF SKLEARN_OPENMP_PARALLELISM_ENABLED: - openmp.omp_lock_t lock + omp_lock_t lock # count remainder chunk in total number of chunks n_chunks += n_samples != n_chunks * n_samples_chunk @@ -304,7 +307,7 @@ def lloyd_iter_chunked_sparse( if update_centers: memset(¢ers_new[0, 0], 0, n_clusters * n_features * sizeof(floating)) memset(&weight_in_clusters[0], 0, n_clusters * sizeof(floating)) - IF SKLEARN_OPENMP_PARALLELISM_ENABLED: + if USE_OPENMP: openmp.omp_init_lock(&lock) with nogil, parallel(num_threads=n_threads): @@ -333,7 +336,7 @@ def lloyd_iter_chunked_sparse( # reduction from local buffers. if update_centers: - IF SKLEARN_OPENMP_PARALLELISM_ENABLED: + if USE_OPENMP: # The lock is necessary to avoid race conditions when aggregating # info from different thread-local buffers. openmp.omp_set_lock(&lock) @@ -341,14 +344,14 @@ def lloyd_iter_chunked_sparse( weight_in_clusters[j] += weight_in_clusters_chunk[j] for k in range(n_features): centers_new[j, k] += centers_new_chunk[j * n_features + k] - IF SKLEARN_OPENMP_PARALLELISM_ENABLED: + if USE_OPENMP: openmp.omp_unset_lock(&lock) free(centers_new_chunk) free(weight_in_clusters_chunk) if update_centers: - IF SKLEARN_OPENMP_PARALLELISM_ENABLED: + if USE_OPENMP: openmp.omp_destroy_lock(&lock) _relocate_empty_clusters_sparse( X_data, X_indices, X_indptr, sample_weight, diff --git a/sklearn/utils/_openmp_helpers.pxd b/sklearn/utils/_openmp_helpers.pxd index 6819824785424..9d0d8f48fa158 100644 --- a/sklearn/utils/_openmp_helpers.pxd +++ b/sklearn/utils/_openmp_helpers.pxd @@ -3,4 +3,20 @@ # Those interfaces act as indirections which allows the non-support of OpenMP # for implementations which have been written for it. +cdef extern from *: + """ + #ifdef _OPENMP + #define USE_OPENMP 1 + #include + #else + #define USE_OPENMP 0 + struct omp_lock_t { + int dummy; + }; + #endif + """ + bint USE_OPENMP + ctypedef struct omp_lock_t: + pass + cdef int _openmp_thread_num() noexcept nogil From d187922d89a5c506cab47423238de27b89189dc1 Mon Sep 17 00:00:00 2001 From: jeremiedbb Date: Tue, 21 Feb 2023 16:27:23 +0100 Subject: [PATCH 02/21] debug --- setup.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/setup.py b/setup.py index a6b6e33afd7b0..b32fbf2d4d9ad 100755 --- a/setup.py +++ b/setup.py @@ -201,6 +201,11 @@ def build_extensions(self): if sklearn._OPENMP_SUPPORTED: openmp_flag = get_openmp_flag(self.compiler) + print("#"*80) + print("#"*80) + print("OPENMP FLAG", openmp_flag) + print("#"*80) + print("#"*80) for e in self.extensions: e.extra_compile_args += openmp_flag From ff012785358a65b411317d355623a4cdd196cd6e Mon Sep 17 00:00:00 2001 From: jeremiedbb Date: Tue, 21 Feb 2023 16:33:34 +0100 Subject: [PATCH 03/21] debug [azure parallel] --- setup.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/setup.py b/setup.py index b32fbf2d4d9ad..a613d2d562f71 100755 --- a/setup.py +++ b/setup.py @@ -201,11 +201,11 @@ def build_extensions(self): if sklearn._OPENMP_SUPPORTED: openmp_flag = get_openmp_flag(self.compiler) - print("#"*80) - print("#"*80) + print("#" * 80) + print("#" * 80) print("OPENMP FLAG", openmp_flag) - print("#"*80) - print("#"*80) + print("#" * 80) + print("#" * 80) for e in self.extensions: e.extra_compile_args += openmp_flag From ebe95c15a9e9e13c9f25cc18f76e580a5ac940a4 Mon Sep 17 00:00:00 2001 From: jeremiedbb Date: Tue, 21 Feb 2023 17:02:50 +0100 Subject: [PATCH 04/21] iter [azure parallel] --- sklearn/cluster/_k_means_lloyd.pyx | 41 +++++++++++++----------------- sklearn/utils/_openmp_helpers.pxd | 9 +++++++ 2 files changed, 26 insertions(+), 24 deletions(-) diff --git a/sklearn/cluster/_k_means_lloyd.pyx b/sklearn/cluster/_k_means_lloyd.pyx index 97247302a8f31..977275213520a 100644 --- a/sklearn/cluster/_k_means_lloyd.pyx +++ b/sklearn/cluster/_k_means_lloyd.pyx @@ -4,11 +4,11 @@ # fused types and when the array may be read-only (for instance when it's # provided by the user). This is fixed in cython > 0.3. -from ..utils._openmp_helpers cimport USE_OPENMP from ..utils._openmp_helpers cimport omp_lock_t - -if USE_OPENMP: - cimport openmp +from ..utils._openmp_helpers cimport omp_init_lock +from ..utils._openmp_helpers cimport omp_destroy_lock +from ..utils._openmp_helpers cimport omp_set_lock +from ..utils._openmp_helpers cimport omp_unset_lock from cython cimport floating from cython.parallel import prange, parallel @@ -110,8 +110,7 @@ def lloyd_iter_chunked_dense( if update_centers: memset(¢ers_new[0, 0], 0, n_clusters * n_features * sizeof(floating)) memset(&weight_in_clusters[0], 0, n_clusters * sizeof(floating)) - if USE_OPENMP: - openmp.omp_init_lock(&lock) + omp_init_lock(&lock) with nogil, parallel(num_threads=n_threads): # thread local buffers @@ -139,24 +138,22 @@ def lloyd_iter_chunked_dense( # reduction from local buffers. if update_centers: - if USE_OPENMP: - # The lock is necessary to avoid race conditions when aggregating - # info from different thread-local buffers. - openmp.omp_set_lock(&lock) + # The lock is necessary to avoid race conditions when aggregating + # info from different thread-local buffers. + omp_set_lock(&lock) for j in range(n_clusters): weight_in_clusters[j] += weight_in_clusters_chunk[j] for k in range(n_features): centers_new[j, k] += centers_new_chunk[j * n_features + k] - if USE_OPENMP: - openmp.omp_unset_lock(&lock) + + omp_unset_lock(&lock) free(centers_new_chunk) free(weight_in_clusters_chunk) free(pairwise_distances_chunk) if update_centers: - if USE_OPENMP: - openmp.omp_destroy_lock(&lock) + omp_destroy_lock(&lock) _relocate_empty_clusters_dense(X, sample_weight, centers_old, centers_new, weight_in_clusters, labels) @@ -307,8 +304,7 @@ def lloyd_iter_chunked_sparse( if update_centers: memset(¢ers_new[0, 0], 0, n_clusters * n_features * sizeof(floating)) memset(&weight_in_clusters[0], 0, n_clusters * sizeof(floating)) - if USE_OPENMP: - openmp.omp_init_lock(&lock) + omp_init_lock(&lock) with nogil, parallel(num_threads=n_threads): # thread local buffers @@ -336,23 +332,20 @@ def lloyd_iter_chunked_sparse( # reduction from local buffers. if update_centers: - if USE_OPENMP: - # The lock is necessary to avoid race conditions when aggregating - # info from different thread-local buffers. - openmp.omp_set_lock(&lock) + # The lock is necessary to avoid race conditions when aggregating + # info from different thread-local buffers. + omp_set_lock(&lock) for j in range(n_clusters): weight_in_clusters[j] += weight_in_clusters_chunk[j] for k in range(n_features): centers_new[j, k] += centers_new_chunk[j * n_features + k] - if USE_OPENMP: - openmp.omp_unset_lock(&lock) + omp_unset_lock(&lock) free(centers_new_chunk) free(weight_in_clusters_chunk) if update_centers: - if USE_OPENMP: - openmp.omp_destroy_lock(&lock) + omp_destroy_lock(&lock) _relocate_empty_clusters_sparse( X_data, X_indices, X_indptr, sample_weight, centers_old, centers_new, weight_in_clusters, labels) diff --git a/sklearn/utils/_openmp_helpers.pxd b/sklearn/utils/_openmp_helpers.pxd index 9d0d8f48fa158..ab16782e7ebdd 100644 --- a/sklearn/utils/_openmp_helpers.pxd +++ b/sklearn/utils/_openmp_helpers.pxd @@ -13,10 +13,19 @@ cdef extern from *: struct omp_lock_t { int dummy; }; + void omp_init_lock(omp_lock_t *lock) {} + void omp_destroy_lock(omp_lock_t *lock) {} + void omp_set_lock(omp_lock_t *lock) {} + void omp_unset_lock(omp_lock_t *lock) {} #endif """ bint USE_OPENMP ctypedef struct omp_lock_t: pass + + void omp_init_lock(omp_lock_t *lock) nogil + void omp_destroy_lock(omp_lock_t *lock) nogil + void omp_set_lock(omp_lock_t *lock) nogil + void omp_unset_lock(omp_lock_t *lock) nogil cdef int _openmp_thread_num() noexcept nogil From 0233a6e8389a354b637485b558ae46f05ddce261 Mon Sep 17 00:00:00 2001 From: jeremiedbb Date: Tue, 21 Feb 2023 17:17:06 +0100 Subject: [PATCH 05/21] iter [azure parallel] --- setup.py | 5 ----- sklearn/utils/_openmp_helpers.pxd | 2 +- 2 files changed, 1 insertion(+), 6 deletions(-) diff --git a/setup.py b/setup.py index a613d2d562f71..a6b6e33afd7b0 100755 --- a/setup.py +++ b/setup.py @@ -201,11 +201,6 @@ def build_extensions(self): if sklearn._OPENMP_SUPPORTED: openmp_flag = get_openmp_flag(self.compiler) - print("#" * 80) - print("#" * 80) - print("OPENMP FLAG", openmp_flag) - print("#" * 80) - print("#" * 80) for e in self.extensions: e.extra_compile_args += openmp_flag diff --git a/sklearn/utils/_openmp_helpers.pxd b/sklearn/utils/_openmp_helpers.pxd index ab16782e7ebdd..233121be77e2c 100644 --- a/sklearn/utils/_openmp_helpers.pxd +++ b/sklearn/utils/_openmp_helpers.pxd @@ -12,7 +12,7 @@ cdef extern from *: #define USE_OPENMP 0 struct omp_lock_t { int dummy; - }; + } omp_lock_t; void omp_init_lock(omp_lock_t *lock) {} void omp_destroy_lock(omp_lock_t *lock) {} void omp_set_lock(omp_lock_t *lock) {} From e6f577e8863ca9f5a7d360e008aed06531202737 Mon Sep 17 00:00:00 2001 From: jeremiedbb Date: Tue, 21 Feb 2023 17:40:27 +0100 Subject: [PATCH 06/21] iter [azure parallel] --- sklearn/utils/_openmp_helpers.pxd | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/sklearn/utils/_openmp_helpers.pxd b/sklearn/utils/_openmp_helpers.pxd index 233121be77e2c..44848e585eb2b 100644 --- a/sklearn/utils/_openmp_helpers.pxd +++ b/sklearn/utils/_openmp_helpers.pxd @@ -12,11 +12,11 @@ cdef extern from *: #define USE_OPENMP 0 struct omp_lock_t { int dummy; - } omp_lock_t; - void omp_init_lock(omp_lock_t *lock) {} - void omp_destroy_lock(omp_lock_t *lock) {} - void omp_set_lock(omp_lock_t *lock) {} - void omp_unset_lock(omp_lock_t *lock) {} + }; + void omp_init_lock(struct omp_lock_t *lock) {} + void omp_destroy_lock(struct omp_lock_t *lock) {} + void omp_set_lock(struct omp_lock_t *lock) {} + void omp_unset_lock(struct omp_lock_t *lock) {} #endif """ bint USE_OPENMP From 067086bb05b902cc58a247a41e0a6021cbb66ae5 Mon Sep 17 00:00:00 2001 From: jeremiedbb Date: Tue, 21 Feb 2023 17:56:49 +0100 Subject: [PATCH 07/21] iter [azure parallel] --- sklearn/utils/_openmp_helpers.pxd | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/sklearn/utils/_openmp_helpers.pxd b/sklearn/utils/_openmp_helpers.pxd index 44848e585eb2b..198c42592586e 100644 --- a/sklearn/utils/_openmp_helpers.pxd +++ b/sklearn/utils/_openmp_helpers.pxd @@ -23,9 +23,9 @@ cdef extern from *: ctypedef struct omp_lock_t: pass - void omp_init_lock(omp_lock_t *lock) nogil - void omp_destroy_lock(omp_lock_t *lock) nogil - void omp_set_lock(omp_lock_t *lock) nogil - void omp_unset_lock(omp_lock_t *lock) nogil + void omp_init_lock(struct omp_lock_t *lock) nogil + void omp_destroy_lock(struct omp_lock_t *lock) nogil + void omp_set_lock(struct omp_lock_t *lock) nogil + void omp_unset_lock(struct omp_lock_t *lock) nogil cdef int _openmp_thread_num() noexcept nogil From d5769c511faf9e3036ad02c6909850dd8f29ee27 Mon Sep 17 00:00:00 2001 From: jeremiedbb Date: Tue, 21 Feb 2023 18:07:30 +0100 Subject: [PATCH 08/21] iter [azure_parallel] --- sklearn/utils/_openmp_helpers.pxd | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/sklearn/utils/_openmp_helpers.pxd b/sklearn/utils/_openmp_helpers.pxd index 198c42592586e..e657b457a354c 100644 --- a/sklearn/utils/_openmp_helpers.pxd +++ b/sklearn/utils/_openmp_helpers.pxd @@ -23,9 +23,9 @@ cdef extern from *: ctypedef struct omp_lock_t: pass - void omp_init_lock(struct omp_lock_t *lock) nogil - void omp_destroy_lock(struct omp_lock_t *lock) nogil - void omp_set_lock(struct omp_lock_t *lock) nogil - void omp_unset_lock(struct omp_lock_t *lock) nogil + void omp_init_lock(struct omp_lock_t*) nogil + void omp_destroy_lock(struct omp_lock_t*) nogil + void omp_set_lock(struct omp_lock_t*) nogil + void omp_unset_lock(struct omp_lock_t*) nogil cdef int _openmp_thread_num() noexcept nogil From fef52d4f6f8f6408a9dd9fa58c4f578535e95ebe Mon Sep 17 00:00:00 2001 From: jeremiedbb Date: Tue, 21 Feb 2023 18:28:23 +0100 Subject: [PATCH 09/21] iter [azure parallel] --- sklearn/utils/_openmp_helpers.pxd | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/sklearn/utils/_openmp_helpers.pxd b/sklearn/utils/_openmp_helpers.pxd index e657b457a354c..f2d1263247585 100644 --- a/sklearn/utils/_openmp_helpers.pxd +++ b/sklearn/utils/_openmp_helpers.pxd @@ -23,9 +23,9 @@ cdef extern from *: ctypedef struct omp_lock_t: pass - void omp_init_lock(struct omp_lock_t*) nogil - void omp_destroy_lock(struct omp_lock_t*) nogil - void omp_set_lock(struct omp_lock_t*) nogil - void omp_unset_lock(struct omp_lock_t*) nogil + void omp_init_lock(struct omp_lock_t) nogil + void omp_destroy_lock(struct omp_lock_t) nogil + void omp_set_lock(struct omp_lock_t) nogil + void omp_unset_lock(struct omp_lock_t) nogil cdef int _openmp_thread_num() noexcept nogil From 0f422b3e53657a9eee7837ff8f3b60748c8e1e3c Mon Sep 17 00:00:00 2001 From: jeremiedbb Date: Tue, 21 Feb 2023 18:51:55 +0100 Subject: [PATCH 10/21] iter [azure parallel] --- sklearn/utils/_openmp_helpers.pxd | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/sklearn/utils/_openmp_helpers.pxd b/sklearn/utils/_openmp_helpers.pxd index f2d1263247585..ade9366a0434a 100644 --- a/sklearn/utils/_openmp_helpers.pxd +++ b/sklearn/utils/_openmp_helpers.pxd @@ -10,22 +10,21 @@ cdef extern from *: #include #else #define USE_OPENMP 0 - struct omp_lock_t { - int dummy; - }; - void omp_init_lock(struct omp_lock_t *lock) {} - void omp_destroy_lock(struct omp_lock_t *lock) {} - void omp_set_lock(struct omp_lock_t *lock) {} - void omp_unset_lock(struct omp_lock_t *lock) {} + typedef void omp_lock_t; + + void omp_init_lock(omp_lock_t *lock) {} + void omp_destroy_lock(omp_lock_t *lock) {} + void omp_set_lock(omp_lock_t *lock) {} + void omp_unset_lock(omp_lock_t *lock) {} #endif """ bint USE_OPENMP ctypedef struct omp_lock_t: pass - - void omp_init_lock(struct omp_lock_t) nogil - void omp_destroy_lock(struct omp_lock_t) nogil - void omp_set_lock(struct omp_lock_t) nogil - void omp_unset_lock(struct omp_lock_t) nogil + + void omp_init_lock(omp_lock_t*) nogil + void omp_destroy_lock(omp_lock_t*) nogil + void omp_set_lock(omp_lock_t*) nogil + void omp_unset_lock(omp_lock_t*) nogil cdef int _openmp_thread_num() noexcept nogil From caed01dcda53410c821fac9ce9e2b744c28a25b0 Mon Sep 17 00:00:00 2001 From: jeremiedbb Date: Tue, 21 Feb 2023 19:11:04 +0100 Subject: [PATCH 11/21] iter [azure parallel] --- sklearn/utils/_openmp_helpers.pxd | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sklearn/utils/_openmp_helpers.pxd b/sklearn/utils/_openmp_helpers.pxd index ade9366a0434a..6f0005ea4d3cb 100644 --- a/sklearn/utils/_openmp_helpers.pxd +++ b/sklearn/utils/_openmp_helpers.pxd @@ -10,7 +10,7 @@ cdef extern from *: #include #else #define USE_OPENMP 0 - typedef void omp_lock_t; + typedef int omp_lock_t; void omp_init_lock(omp_lock_t *lock) {} void omp_destroy_lock(omp_lock_t *lock) {} From dc542a7e4b701a18d89c4011f8c6f2cbf304c167 Mon Sep 17 00:00:00 2001 From: jeremiedbb Date: Wed, 22 Feb 2023 09:12:57 +0100 Subject: [PATCH 12/21] wip --- .../_base.pyx.tp | 8 ++-- sklearn/utils/_fake_openmp.h | 13 ++++++ sklearn/utils/_openmp_helpers.pxd | 25 +++++------ sklearn/utils/_openmp_helpers.pyx | 41 +++++++------------ 4 files changed, 42 insertions(+), 45 deletions(-) create mode 100644 sklearn/utils/_fake_openmp.h diff --git a/sklearn/metrics/_pairwise_distances_reduction/_base.pyx.tp b/sklearn/metrics/_pairwise_distances_reduction/_base.pyx.tp index dec1e96dbbb9f..6a4d879667d3a 100644 --- a/sklearn/metrics/_pairwise_distances_reduction/_base.pyx.tp +++ b/sklearn/metrics/_pairwise_distances_reduction/_base.pyx.tp @@ -21,7 +21,7 @@ from cython.parallel cimport parallel, prange from libcpp.vector cimport vector from ...utils._cython_blas cimport _dot -from ...utils._openmp_helpers cimport _openmp_thread_num +from ...utils._openmp_helpers cimport omp_get_thread_num from ...utils._typedefs cimport ITYPE_t, DTYPE_t import numpy as np @@ -88,7 +88,7 @@ cdef DTYPE_t[::1] _sqeuclidean_row_norms32_dense( ) with nogil, parallel(num_threads=num_threads): - thread_num = _openmp_thread_num() + thread_num = omp_get_thread_num() for i in prange(n, schedule='static'): # Upcasting the i-th row of X from float32 to float64 @@ -245,7 +245,7 @@ cdef class BaseDistancesReduction{{name_suffix}}: ITYPE_t thread_num with nogil, parallel(num_threads=self.chunks_n_threads): - thread_num = _openmp_thread_num() + thread_num = omp_get_thread_num() # Allocating thread datastructures self._parallel_on_X_parallel_init(thread_num) @@ -324,7 +324,7 @@ cdef class BaseDistancesReduction{{name_suffix}}: X_end = X_start + self.X_n_samples_chunk with nogil, parallel(num_threads=self.chunks_n_threads): - thread_num = _openmp_thread_num() + thread_num = omp_get_thread_num() # Initializing datastructures used in this thread self._parallel_on_Y_parallel_init(thread_num, X_start, X_end) diff --git a/sklearn/utils/_fake_openmp.h b/sklearn/utils/_fake_openmp.h new file mode 100644 index 0000000000000..a13123471fc09 --- /dev/null +++ b/sklearn/utils/_fake_openmp.h @@ -0,0 +1,13 @@ +typedef int omp_lock_t; + +void omp_init_lock(omp_lock_t *lock) {} + +void omp_destroy_lock(omp_lock_t *lock) {} + +void omp_set_lock(omp_lock_t *lock) {} + +void omp_unset_lock(omp_lock_t *lock) {} + +int omp_get_thread_num() { return 0; } + +int omp_get_max_threads() { return 1; } diff --git a/sklearn/utils/_openmp_helpers.pxd b/sklearn/utils/_openmp_helpers.pxd index 6f0005ea4d3cb..610b6bd01dd7f 100644 --- a/sklearn/utils/_openmp_helpers.pxd +++ b/sklearn/utils/_openmp_helpers.pxd @@ -6,25 +6,22 @@ cdef extern from *: """ #ifdef _OPENMP - #define USE_OPENMP 1 #include + #define SKLEARN_OPENMP_PARALLELISM_ENABLED 1 #else - #define USE_OPENMP 0 - typedef int omp_lock_t; - - void omp_init_lock(omp_lock_t *lock) {} - void omp_destroy_lock(omp_lock_t *lock) {} - void omp_set_lock(omp_lock_t *lock) {} - void omp_unset_lock(omp_lock_t *lock) {} + #include <_fake_openmp.h> + #define SKLEARN_OPENMP_PARALLELISM_ENABLED 0 #endif """ - bint USE_OPENMP + bint SKLEARN_OPENMP_PARALLELISM_ENABLED + ctypedef struct omp_lock_t: pass - void omp_init_lock(omp_lock_t*) nogil - void omp_destroy_lock(omp_lock_t*) nogil - void omp_set_lock(omp_lock_t*) nogil - void omp_unset_lock(omp_lock_t*) nogil + void omp_init_lock(omp_lock_t*) noexcept nogil + void omp_destroy_lock(omp_lock_t*) noexcept nogil + void omp_set_lock(omp_lock_t*) noexcept nogil + void omp_unset_lock(omp_lock_t*) noexcept nogil -cdef int _openmp_thread_num() noexcept nogil + int omp_get_thread_num() noexcept nogil + int omp_get_max_threads() noexcept nogil diff --git a/sklearn/utils/_openmp_helpers.pyx b/sklearn/utils/_openmp_helpers.pyx index 25f57021c9fa9..f2006e9ed7ce1 100644 --- a/sklearn/utils/_openmp_helpers.pyx +++ b/sklearn/utils/_openmp_helpers.pyx @@ -1,7 +1,5 @@ -IF SKLEARN_OPENMP_PARALLELISM_ENABLED: - import os - cimport openmp - from joblib import cpu_count +import os +from joblib import cpu_count def _openmp_parallelism_enabled(): @@ -41,31 +39,20 @@ cpdef _openmp_effective_n_threads(n_threads=None): if n_threads == 0: raise ValueError("n_threads = 0 is invalid") - IF SKLEARN_OPENMP_PARALLELISM_ENABLED: - if os.getenv("OMP_NUM_THREADS"): - # Fall back to user provided number of threads making it possible - # to exceed the number of cpus. - max_n_threads = openmp.omp_get_max_threads() - else: - max_n_threads = min(openmp.omp_get_max_threads(), cpu_count()) - - if n_threads is None: - return max_n_threads - elif n_threads < 0: - return max(1, max_n_threads + n_threads + 1) - - return n_threads - ELSE: + if not SKLEARN_OPENMP_PARALLELISM_ENABLED: # OpenMP disabled at build-time => sequential mode return 1 + if os.getenv("OMP_NUM_THREADS"): + # Fall back to user provided number of threads making it possible + # to exceed the number of cpus. + max_n_threads = omp_get_max_threads() + else: + max_n_threads = min(omp_get_max_threads(), cpu_count()) -cdef inline int _openmp_thread_num() noexcept nogil: - """Return the number of the thread calling this function. + if n_threads is None: + return max_n_threads + elif n_threads < 0: + return max(1, max_n_threads + n_threads + 1) - If scikit-learn is built without OpenMP support, always return 0. - """ - IF SKLEARN_OPENMP_PARALLELISM_ENABLED: - return openmp.omp_get_thread_num() - ELSE: - return 0 + return n_threads From eecbf54814f06ed9a1ae5c6eed32e017393078bc Mon Sep 17 00:00:00 2001 From: jeremiedbb Date: Wed, 22 Feb 2023 13:14:46 +0100 Subject: [PATCH 13/21] finalize + update docs --- doc/developers/performance.rst | 23 +++++---------- sklearn/_build_utils/__init__.py | 11 ++------ sklearn/cluster/_k_means_elkan.pyx | 45 +++++++++++++----------------- sklearn/cluster/_k_means_lloyd.pyx | 11 ++++---- sklearn/utils/_fake_openmp.h | 13 --------- sklearn/utils/_openmp_helpers.pxd | 10 +++++-- sklearn/utils/_openmp_helpers.pyx | 5 ++-- 7 files changed, 44 insertions(+), 74 deletions(-) delete mode 100644 sklearn/utils/_fake_openmp.h diff --git a/doc/developers/performance.rst b/doc/developers/performance.rst index bf6a1a24efd8e..1ad1fbe910f83 100644 --- a/doc/developers/performance.rst +++ b/doc/developers/performance.rst @@ -344,27 +344,18 @@ Using OpenMP Since scikit-learn can be built without OpenMP, it's necessary to protect each direct call to OpenMP. -There are some helpers in +The _openmp_helpers module, available in `sklearn/utils/_openmp_helpers.pyx `_ -that you can reuse for the main useful functionalities and already have the -necessary protection to be built without OpenMP. +provides protected versions of the OpenMP routines. To use OpenMP routines, they +must be cimported from this module and not from the OpenMP library directly:: -If the helpers are not enough, you need to protect your OpenMP code using the -following syntax:: - - # importing OpenMP - IF SKLEARN_OPENMP_PARALLELISM_ENABLED: - cimport openmp - - # calling OpenMP - IF SKLEARN_OPENMP_PARALLELISM_ENABLED: - max_threads = openmp.omp_get_max_threads() - ELSE: - max_threads = 1 + from sklearn.utils._openmp_helpers cimport omp_get_max_threads + max_threads = omp_get_max_threads() .. note:: - Protecting the parallel loop, ``prange``, is already done by cython. + The parallel loop, `prange`, is already protected by cython and can be used directly + from `cython.parallel`. .. _profiling-compiled-extension: diff --git a/sklearn/_build_utils/__init__.py b/sklearn/_build_utils/__init__.py index 33af1a5128ad7..d539c0c06ecc1 100644 --- a/sklearn/_build_utils/__init__.py +++ b/sklearn/_build_utils/__init__.py @@ -52,11 +52,9 @@ def cythonize_extensions(extension): # compilers are properly configured to build with OpenMP. This is expensive # and we only want to call this function once. # The result of this check is cached as a private attribute on the sklearn - # module (only at build-time) to be used twice: - # - First to set the value of SKLEARN_OPENMP_PARALLELISM_ENABLED, the - # cython build-time variable passed to the cythonize() call. - # - Then in the build_ext subclass defined in the top-level setup.py file - # to actually build the compiled extensions with OpenMP flags if needed. + # module (only at build-time) to be used in the build_ext subclass defined + # in the top-level setup.py file to actually build the compiled extensions + # with OpenMP flags if needed. sklearn._OPENMP_SUPPORTED = check_openmp_support() n_jobs = 1 @@ -82,9 +80,6 @@ def cythonize_extensions(extension): return cythonize( extension, nthreads=n_jobs, - compile_time_env={ - "SKLEARN_OPENMP_PARALLELISM_ENABLED": sklearn._OPENMP_SUPPORTED - }, compiler_directives=compiler_directives, ) diff --git a/sklearn/cluster/_k_means_elkan.pyx b/sklearn/cluster/_k_means_elkan.pyx index abde900903c50..c2919ac1d0012 100644 --- a/sklearn/cluster/_k_means_elkan.pyx +++ b/sklearn/cluster/_k_means_elkan.pyx @@ -6,13 +6,16 @@ # fused types and when the array may be read-only (for instance when it's # provided by the user). This is fixed in cython > 0.3. -IF SKLEARN_OPENMP_PARALLELISM_ENABLED: - cimport openmp from cython cimport floating from cython.parallel import prange, parallel from libc.stdlib cimport calloc, free from libc.string cimport memset +from ..utils._openmp_helpers cimport omp_lock_t +from ..utils._openmp_helpers cimport omp_init_lock +from ..utils._openmp_helpers cimport omp_destroy_lock +from ..utils._openmp_helpers cimport omp_set_lock +from ..utils._openmp_helpers cimport omp_unset_lock from ..utils.extmath import row_norms from ._k_means_common import CHUNK_SIZE from ._k_means_common cimport _relocate_empty_clusters_dense @@ -274,8 +277,7 @@ def elkan_iter_chunked_dense( floating *centers_new_chunk floating *weight_in_clusters_chunk - IF SKLEARN_OPENMP_PARALLELISM_ENABLED: - openmp.omp_lock_t lock + omp_lock_t lock # count remainder chunk in total number of chunks n_chunks += n_samples != n_chunks * n_samples_chunk @@ -286,8 +288,7 @@ def elkan_iter_chunked_dense( if update_centers: memset(¢ers_new[0, 0], 0, n_clusters * n_features * sizeof(floating)) memset(&weight_in_clusters[0], 0, n_clusters * sizeof(floating)) - IF SKLEARN_OPENMP_PARALLELISM_ENABLED: - openmp.omp_init_lock(&lock) + omp_init_lock(&lock) with nogil, parallel(num_threads=n_threads): # thread local buffers @@ -316,23 +317,20 @@ def elkan_iter_chunked_dense( # reduction from local buffers. if update_centers: - IF SKLEARN_OPENMP_PARALLELISM_ENABLED: - # The lock is necessary to avoid race conditions when aggregating - # info from different thread-local buffers. - openmp.omp_set_lock(&lock) + # The lock is necessary to avoid race conditions when aggregating + # info from different thread-local buffers. + omp_set_lock(&lock) for j in range(n_clusters): weight_in_clusters[j] += weight_in_clusters_chunk[j] for k in range(n_features): centers_new[j, k] += centers_new_chunk[j * n_features + k] - IF SKLEARN_OPENMP_PARALLELISM_ENABLED: - openmp.omp_unset_lock(&lock) + omp_unset_lock(&lock) free(centers_new_chunk) free(weight_in_clusters_chunk) if update_centers: - IF SKLEARN_OPENMP_PARALLELISM_ENABLED: - openmp.omp_destroy_lock(&lock) + omp_destroy_lock(&lock) _relocate_empty_clusters_dense(X, sample_weight, centers_old, centers_new, weight_in_clusters, labels) @@ -516,8 +514,7 @@ def elkan_iter_chunked_sparse( floating *centers_new_chunk floating *weight_in_clusters_chunk - IF SKLEARN_OPENMP_PARALLELISM_ENABLED: - openmp.omp_lock_t lock + omp_lock_t lock # count remainder chunk in total number of chunks n_chunks += n_samples != n_chunks * n_samples_chunk @@ -528,8 +525,7 @@ def elkan_iter_chunked_sparse( if update_centers: memset(¢ers_new[0, 0], 0, n_clusters * n_features * sizeof(floating)) memset(&weight_in_clusters[0], 0, n_clusters * sizeof(floating)) - IF SKLEARN_OPENMP_PARALLELISM_ENABLED: - openmp.omp_init_lock(&lock) + omp_init_lock(&lock) with nogil, parallel(num_threads=n_threads): # thread local buffers @@ -561,23 +557,20 @@ def elkan_iter_chunked_sparse( # reduction from local buffers. if update_centers: - IF SKLEARN_OPENMP_PARALLELISM_ENABLED: - # The lock is necessary to avoid race conditions when aggregating - # info from different thread-local buffers. - openmp.omp_set_lock(&lock) + # The lock is necessary to avoid race conditions when aggregating + # info from different thread-local buffers. + omp_set_lock(&lock) for j in range(n_clusters): weight_in_clusters[j] += weight_in_clusters_chunk[j] for k in range(n_features): centers_new[j, k] += centers_new_chunk[j * n_features + k] - IF SKLEARN_OPENMP_PARALLELISM_ENABLED: - openmp.omp_unset_lock(&lock) + omp_unset_lock(&lock) free(centers_new_chunk) free(weight_in_clusters_chunk) if update_centers: - IF SKLEARN_OPENMP_PARALLELISM_ENABLED: - openmp.omp_destroy_lock(&lock) + omp_destroy_lock(&lock) _relocate_empty_clusters_sparse( X_data, X_indices, X_indptr, sample_weight, centers_old, centers_new, weight_in_clusters, labels) diff --git a/sklearn/cluster/_k_means_lloyd.pyx b/sklearn/cluster/_k_means_lloyd.pyx index 977275213520a..6ca50b2a1d0d9 100644 --- a/sklearn/cluster/_k_means_lloyd.pyx +++ b/sklearn/cluster/_k_means_lloyd.pyx @@ -4,18 +4,17 @@ # fused types and when the array may be read-only (for instance when it's # provided by the user). This is fixed in cython > 0.3. -from ..utils._openmp_helpers cimport omp_lock_t -from ..utils._openmp_helpers cimport omp_init_lock -from ..utils._openmp_helpers cimport omp_destroy_lock -from ..utils._openmp_helpers cimport omp_set_lock -from ..utils._openmp_helpers cimport omp_unset_lock - from cython cimport floating from cython.parallel import prange, parallel from libc.stdlib cimport malloc, calloc, free from libc.string cimport memset from libc.float cimport DBL_MAX, FLT_MAX +from ..utils._openmp_helpers cimport omp_lock_t +from ..utils._openmp_helpers cimport omp_init_lock +from ..utils._openmp_helpers cimport omp_destroy_lock +from ..utils._openmp_helpers cimport omp_set_lock +from ..utils._openmp_helpers cimport omp_unset_lock from ..utils.extmath import row_norms from ..utils._cython_blas cimport _gemm from ..utils._cython_blas cimport RowMajor, Trans, NoTrans diff --git a/sklearn/utils/_fake_openmp.h b/sklearn/utils/_fake_openmp.h deleted file mode 100644 index a13123471fc09..0000000000000 --- a/sklearn/utils/_fake_openmp.h +++ /dev/null @@ -1,13 +0,0 @@ -typedef int omp_lock_t; - -void omp_init_lock(omp_lock_t *lock) {} - -void omp_destroy_lock(omp_lock_t *lock) {} - -void omp_set_lock(omp_lock_t *lock) {} - -void omp_unset_lock(omp_lock_t *lock) {} - -int omp_get_thread_num() { return 0; } - -int omp_get_max_threads() { return 1; } diff --git a/sklearn/utils/_openmp_helpers.pxd b/sklearn/utils/_openmp_helpers.pxd index 610b6bd01dd7f..cc63532c1553e 100644 --- a/sklearn/utils/_openmp_helpers.pxd +++ b/sklearn/utils/_openmp_helpers.pxd @@ -3,14 +3,21 @@ # Those interfaces act as indirections which allows the non-support of OpenMP # for implementations which have been written for it. + cdef extern from *: """ #ifdef _OPENMP #include #define SKLEARN_OPENMP_PARALLELISM_ENABLED 1 #else - #include <_fake_openmp.h> #define SKLEARN_OPENMP_PARALLELISM_ENABLED 0 + typedef int omp_lock_t; + void omp_init_lock(omp_lock_t *lock) {} + void omp_destroy_lock(omp_lock_t *lock) {} + void omp_set_lock(omp_lock_t *lock) {} + void omp_unset_lock(omp_lock_t *lock) {} + int omp_get_thread_num() { return 0; } + int omp_get_max_threads() { return 1; } #endif """ bint SKLEARN_OPENMP_PARALLELISM_ENABLED @@ -22,6 +29,5 @@ cdef extern from *: void omp_destroy_lock(omp_lock_t*) noexcept nogil void omp_set_lock(omp_lock_t*) noexcept nogil void omp_unset_lock(omp_lock_t*) noexcept nogil - int omp_get_thread_num() noexcept nogil int omp_get_max_threads() noexcept nogil diff --git a/sklearn/utils/_openmp_helpers.pyx b/sklearn/utils/_openmp_helpers.pyx index f2006e9ed7ce1..f2b2a421e4fae 100644 --- a/sklearn/utils/_openmp_helpers.pyx +++ b/sklearn/utils/_openmp_helpers.pyx @@ -7,9 +7,8 @@ def _openmp_parallelism_enabled(): It allows to retrieve at runtime the information gathered at compile time. """ - # SKLEARN_OPENMP_PARALLELISM_ENABLED is resolved at compile time during - # cythonization. It is defined via the `compile_time_env` kwarg of the - # `cythonize` call and behaves like the `-D` option of the C preprocessor. + # SKLEARN_OPENMP_PARALLELISM_ENABLED is resolved at compile time and defined + # in _openmp_helpers.pxd as a boolean. This function exposes it to Python. return SKLEARN_OPENMP_PARALLELISM_ENABLED From 8fe4c07fd45ab9e61f52cc5bb003d1d92b951535 Mon Sep 17 00:00:00 2001 From: jeremiedbb Date: Wed, 22 Feb 2023 13:53:37 +0100 Subject: [PATCH 14/21] remove DEF as well --- sklearn/linear_model/_sgd_fast.pyx | 41 ++++++++++++++++++++---------- 1 file changed, 27 insertions(+), 14 deletions(-) diff --git a/sklearn/linear_model/_sgd_fast.pyx b/sklearn/linear_model/_sgd_fast.pyx index 182635bfb5136..ceaa3a6a3ed73 100644 --- a/sklearn/linear_model/_sgd_fast.pyx +++ b/sklearn/linear_model/_sgd_fast.pyx @@ -20,20 +20,33 @@ from ..utils._seq_dataset cimport SequentialDataset64 as SequentialDataset cnp.import_array() -# Penalty constants -DEF NO_PENALTY = 0 -DEF L1 = 1 -DEF L2 = 2 -DEF ELASTICNET = 3 - -# Learning rate constants -DEF CONSTANT = 1 -DEF OPTIMAL = 2 -DEF INVSCALING = 3 -DEF ADAPTIVE = 4 -DEF PA1 = 5 -DEF PA2 = 6 - +cdef extern from *: + """ + /* Penalty constants */ + #define NO_PENALTY 0 + #define L1 1 + #define L2 2 + #define ELASTICNET 3 + + /* Learning rate constants */ + #define CONSTANT 1 + #define OPTIMAL 2 + #define INVSCALING 3 + #define ADAPTIVE 4 + #define PA1 5 + #define PA2 6 + """ + const int NO_PENALTY = 0 + const int L1 = 1 + const int L2 = 2 + const int ELASTICNET = 3 + + const int CONSTANT = 1 + const int OPTIMAL = 2 + const int INVSCALING = 3 + const int ADAPTIVE = 4 + const int PA1 = 5 + const int PA2 = 6 # ---------------------------------------- From b74d503dba8ade473cb1884da4253cced5277ce4 Mon Sep 17 00:00:00 2001 From: jeremiedbb Date: Wed, 22 Feb 2023 15:24:04 +0100 Subject: [PATCH 15/21] even better noops: use full macros --- sklearn/utils/_openmp_helpers.pxd | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/sklearn/utils/_openmp_helpers.pxd b/sklearn/utils/_openmp_helpers.pxd index cc63532c1553e..a7694d0be2d93 100644 --- a/sklearn/utils/_openmp_helpers.pxd +++ b/sklearn/utils/_openmp_helpers.pxd @@ -1,8 +1,8 @@ -# Helpers to access OpenMP threads information +# Helpers to safely access OpenMP routines # -# Those interfaces act as indirections which allows the non-support of OpenMP -# for implementations which have been written for it. - +# no-op implementations are provided for the case where OpenMP is not available. +# +# All calls to OpenMP routines should be cimported from this module. cdef extern from *: """ @@ -11,13 +11,13 @@ cdef extern from *: #define SKLEARN_OPENMP_PARALLELISM_ENABLED 1 #else #define SKLEARN_OPENMP_PARALLELISM_ENABLED 0 - typedef int omp_lock_t; - void omp_init_lock(omp_lock_t *lock) {} - void omp_destroy_lock(omp_lock_t *lock) {} - void omp_set_lock(omp_lock_t *lock) {} - void omp_unset_lock(omp_lock_t *lock) {} - int omp_get_thread_num() { return 0; } - int omp_get_max_threads() { return 1; } + #define omp_lock_t int + #define omp_init_lock(l) (void)0 + #define omp_destroy_lock(l) (void)0 + #define omp_set_lock(l) (void)0 + #define omp_unset_lock(l) (void)0 + #define omp_get_thread_num() 0 + #define omp_get_max_threads() 1 #endif """ bint SKLEARN_OPENMP_PARALLELISM_ENABLED From 714ae33bdef4d6247474feb610bb32842214948e Mon Sep 17 00:00:00 2001 From: jeremiedbb Date: Thu, 23 Feb 2023 11:55:18 +0100 Subject: [PATCH 16/21] [cd build] From 815052e605719274146aa04c35b538178374162b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=A9mie=20du=20Boisberranger?= <34657725+jeremiedbb@users.noreply.github.com> Date: Fri, 24 Feb 2023 13:03:42 +0100 Subject: [PATCH 17/21] Update doc/developers/performance.rst Co-authored-by: Guillaume Lemaitre --- doc/developers/performance.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/developers/performance.rst b/doc/developers/performance.rst index 1ad1fbe910f83..6b9f4389a354d 100644 --- a/doc/developers/performance.rst +++ b/doc/developers/performance.rst @@ -344,7 +344,7 @@ Using OpenMP Since scikit-learn can be built without OpenMP, it's necessary to protect each direct call to OpenMP. -The _openmp_helpers module, available in +The `_openmp_helpers` module, available in `sklearn/utils/_openmp_helpers.pyx `_ provides protected versions of the OpenMP routines. To use OpenMP routines, they must be cimported from this module and not from the OpenMP library directly:: From 1daa4222a3f77fdc7043fa50068d6083616bbb18 Mon Sep 17 00:00:00 2001 From: jeremie du boisberranger Date: Fri, 24 Feb 2023 13:13:09 +0100 Subject: [PATCH 18/21] [scipy-dev] From 4d1b27b60b47ac817ec9c88b027ec6028a880966 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=A9mie=20du=20Boisberranger?= <34657725+jeremiedbb@users.noreply.github.com> Date: Fri, 24 Feb 2023 13:29:51 +0100 Subject: [PATCH 19/21] Update sklearn/linear_model/_sgd_fast.pyx --- sklearn/linear_model/_sgd_fast.pyx | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/sklearn/linear_model/_sgd_fast.pyx b/sklearn/linear_model/_sgd_fast.pyx index ceaa3a6a3ed73..96f13733c8892 100644 --- a/sklearn/linear_model/_sgd_fast.pyx +++ b/sklearn/linear_model/_sgd_fast.pyx @@ -36,17 +36,17 @@ cdef extern from *: #define PA1 5 #define PA2 6 """ - const int NO_PENALTY = 0 - const int L1 = 1 - const int L2 = 2 - const int ELASTICNET = 3 - - const int CONSTANT = 1 - const int OPTIMAL = 2 - const int INVSCALING = 3 - const int ADAPTIVE = 4 - const int PA1 = 5 - const int PA2 = 6 + int NO_PENALTY = 0 + int L1 = 1 + int L2 = 2 + int ELASTICNET = 3 + + int CONSTANT = 1 + int OPTIMAL = 2 + int INVSCALING = 3 + int ADAPTIVE = 4 + int PA1 = 5 + int PA2 = 6 # ---------------------------------------- From 2364042d8cf3ed3ef6893493972b31fc571a7f0b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=A9mie=20du=20Boisberranger?= <34657725+jeremiedbb@users.noreply.github.com> Date: Fri, 24 Feb 2023 13:30:24 +0100 Subject: [PATCH 20/21] Update sklearn/linear_model/_sgd_fast.pyx --- sklearn/linear_model/_sgd_fast.pyx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sklearn/linear_model/_sgd_fast.pyx b/sklearn/linear_model/_sgd_fast.pyx index 96f13733c8892..42cb9e8ee40d0 100644 --- a/sklearn/linear_model/_sgd_fast.pyx +++ b/sklearn/linear_model/_sgd_fast.pyx @@ -40,7 +40,7 @@ cdef extern from *: int L1 = 1 int L2 = 2 int ELASTICNET = 3 - + int CONSTANT = 1 int OPTIMAL = 2 int INVSCALING = 3 From 3d9e5f1266e82d7b3fae3e28f63f626bc2d4b2e3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=A9mie=20du=20Boisberranger?= <34657725+jeremiedbb@users.noreply.github.com> Date: Fri, 24 Feb 2023 13:30:54 +0100 Subject: [PATCH 21/21] [scipy-dev] --- sklearn/linear_model/_sgd_fast.pyx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sklearn/linear_model/_sgd_fast.pyx b/sklearn/linear_model/_sgd_fast.pyx index 42cb9e8ee40d0..96f13733c8892 100644 --- a/sklearn/linear_model/_sgd_fast.pyx +++ b/sklearn/linear_model/_sgd_fast.pyx @@ -40,7 +40,7 @@ cdef extern from *: int L1 = 1 int L2 = 2 int ELASTICNET = 3 - + int CONSTANT = 1 int OPTIMAL = 2 int INVSCALING = 3