|
| 1 | +/* -*- c -*- */ |
| 2 | + |
| 3 | +/* |
| 4 | + * This is a dummy module whose purpose is to get distutils to generate the |
| 5 | + * configuration files before the libraries are made. |
| 6 | + */ |
| 7 | + |
| 8 | +#if (defined(USING_MKL_RT) && defined(__linux__)) |
| 9 | +#define FORCE_PRELOADING 1 |
| 10 | +#define _GNU_SOURCE 1 |
| 11 | +#include <dlfcn.h> |
| 12 | +#include <string.h> |
| 13 | +#undef _GNU_SOURCE |
| 14 | +#endif |
| 15 | + |
| 16 | +#include <Python.h> |
| 17 | +#if PY_MAJOR_VERSION >= 3 |
| 18 | +#define IS_PY3K |
| 19 | +#endif |
| 20 | +#include "mkl.h" |
| 21 | + |
| 22 | +static struct PyMethodDef methods[] = { |
| 23 | + {NULL, NULL, 0, NULL} |
| 24 | +}; |
| 25 | + |
| 26 | +static inline void _set_mkl_ilp64() { |
| 27 | +#ifdef USING_MKL_RT |
| 28 | + int i = mkl_set_interface_layer(MKL_INTERFACE_ILP64); |
| 29 | +#endif |
| 30 | + return; |
| 31 | +} |
| 32 | + |
| 33 | +static inline void _set_mkl_lp64() { |
| 34 | +#ifdef USING_MKL_RT |
| 35 | + int i = mkl_set_interface_layer(MKL_INTERFACE_LP64); |
| 36 | +#endif |
| 37 | + return; |
| 38 | +} |
| 39 | + |
| 40 | +static void _preload_threading_layer() { |
| 41 | +#if FORCE_PRELOADING |
| 42 | +#define VERBOSE(...) if(verbose) printf("mkl-service + Intel(R) MKL: " __VA_ARGS__) |
| 43 | +#define SET_MTLAYER(L) do { \ |
| 44 | + VERBOSE("setting Intel(R) MKL to use " #L " OpenMP runtime\n"); \ |
| 45 | + mkl_set_threading_layer(MKL_THREADING_##L); \ |
| 46 | + setenv("MKL_THREADING_LAYER", #L, 0); \ |
| 47 | + } while(0) |
| 48 | +#define PRELOAD(lib) do { \ |
| 49 | + VERBOSE("preloading %s runtime\n", lib); \ |
| 50 | + dlopen(lib, RTLD_LAZY|RTLD_GLOBAL); \ |
| 51 | + } while(0) |
| 52 | + /* |
| 53 | + * The following is the pseudo-code skeleton for reinterpreting unset MKL_THREADING_LAYER |
| 54 | + * |
| 55 | + * if MKL_THREADING_LAYER is empty |
| 56 | + * if kmp_calloc (or a suitable symbol identified by Terry) is loaded, |
| 57 | + * we are using Intel (R) OpenMP, i.e. reinterpret as implicit value of INTEL |
| 58 | + * otherwise check if other Open MP is loaded by checking get_omp_num_threads symbol |
| 59 | + * if not loaded: |
| 60 | + * assume INTEL, and force loading of IOMP5 |
| 61 | + * if loaded: |
| 62 | + * if Gnu OMP, set MKL_THREADING_LAYER=GNU, and call set_mkl_threading_layer(MKL_THREADING_GNU) |
| 63 | + * if other vendors? |
| 64 | + * if MKL_THREADING_LAYER is INTEL |
| 65 | + * force loading of iomp, to preempt possibility of other modules loading other OMP library before MKL is actually used |
| 66 | + * |
| 67 | + * should we treat other possible values of MKL_THREADING_LAYER specially? |
| 68 | + * |
| 69 | + */ |
| 70 | + |
| 71 | + const char *libiomp = "libiomp5.so"; |
| 72 | + const char *verbose = getenv("MKL_VERBOSE"); |
| 73 | + const char *mtlayer = getenv("MKL_THREADING_LAYER"); |
| 74 | + void *omp = dlsym(RTLD_DEFAULT, "omp_get_num_threads"); |
| 75 | + const char *omp_name = "(unidentified)"; |
| 76 | + const char *iomp = NULL; /* non-zero indicates Intel OpenMP is loaded */ |
| 77 | + Dl_info omp_info; |
| 78 | + |
| 79 | + if(verbose && (verbose[0] == 0 || atoi(verbose) == 0)) |
| 80 | + verbose = NULL; |
| 81 | + |
| 82 | + VERBOSE("THREADING LAYER: %s\n", mtlayer); |
| 83 | + |
| 84 | + if(omp) { |
| 85 | + if(dladdr(omp, &omp_info)) { |
| 86 | + omp_name = basename(omp_info.dli_fname); /* GNU version doesn't modify argument */ |
| 87 | + iomp = strstr(omp_name, libiomp); |
| 88 | + } |
| 89 | + VERBOSE("%s OpenMP runtime %s is already loaded\n", iomp?"Intel(R)":"Other vendor", omp_name); |
| 90 | + } |
| 91 | + if(!mtlayer || mtlayer[0] == 0) { /* unset or empty */ |
| 92 | + if(omp) { /* if OpenMP runtime is loaded */ |
| 93 | + if(iomp) /* if Intel runtime is loaded */ |
| 94 | + SET_MTLAYER(INTEL); |
| 95 | + else /* otherwise, assume it is GNU OpenMP */ |
| 96 | + SET_MTLAYER(GNU); |
| 97 | + } else { /* nothing is loaded */ |
| 98 | + SET_MTLAYER(INTEL); |
| 99 | + PRELOAD(libiomp); |
| 100 | + } |
| 101 | + } else if(strcasecmp(mtlayer, "intel") == 0) { /* Intel runtime is requested */ |
| 102 | + if(omp && !iomp) { |
| 103 | + fprintf(stderr, "Error: mkl-service + Intel(R) MKL: MKL_THREADING_LAYER=INTEL is incompatible with %s library." |
| 104 | + "\n\tTry to import numpy first or set the threading layer accordingly. " |
| 105 | + "Set MKL_SERVICE_FORCE_INTEL to force it.\n", omp_name); |
| 106 | + if(!getenv("MKL_SERVICE_FORCE_INTEL")) |
| 107 | + exit(1); |
| 108 | + } else |
| 109 | + PRELOAD(libiomp); |
| 110 | + } |
| 111 | +#endif |
| 112 | + return; |
| 113 | +} |
| 114 | + |
| 115 | +static inline void _set_mkl_interface() { |
| 116 | + _set_mkl_lp64(); |
| 117 | + _preload_threading_layer(); |
| 118 | +} |
| 119 | + |
| 120 | +#if defined(IS_PY3K) |
| 121 | +static struct PyModuleDef moduledef = { |
| 122 | + PyModuleDef_HEAD_INIT, |
| 123 | + "mklinit", |
| 124 | + NULL, |
| 125 | + -1, |
| 126 | + methods, |
| 127 | + NULL, |
| 128 | + NULL, |
| 129 | + NULL, |
| 130 | + NULL |
| 131 | +}; |
| 132 | +#endif |
| 133 | + |
| 134 | +/* Initialization function for the module */ |
| 135 | +#if defined(IS_PY3K) |
| 136 | +PyMODINIT_FUNC PyInit__mklinit(void) { |
| 137 | + PyObject *m; |
| 138 | + |
| 139 | + _set_mkl_interface(); |
| 140 | + m = PyModule_Create(&moduledef); |
| 141 | + if (!m) { |
| 142 | + return NULL; |
| 143 | + } |
| 144 | + |
| 145 | + return m; |
| 146 | +} |
| 147 | +#else |
| 148 | +PyMODINIT_FUNC |
| 149 | +init_mklinit(void) { |
| 150 | + _set_mkl_interface(); |
| 151 | + Py_InitModule("_mklinit", methods); |
| 152 | +} |
| 153 | +#endif |
0 commit comments