OpenCLPlatform.cpp 18.2 KB
Newer Older
1
2
3
/* -------------------------------------------------------------------------- *
 *                                   OpenMM                                   *
 * -------------------------------------------------------------------------- *
Evan Pretti's avatar
Evan Pretti committed
4
5
 * This is part of the OpenMM molecular simulation toolkit.                   *
 * See https://openmm.org/development.                                        *
6
 *                                                                            *
7
 * Portions copyright (c) 2008-2026 Stanford University and the Authors.      *
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
 * Authors: Peter Eastman                                                     *
 * Contributors:                                                              *
 *                                                                            *
 * This program is free software: you can redistribute it and/or modify       *
 * it under the terms of the GNU Lesser General Public License as published   *
 * by the Free Software Foundation, either version 3 of the License, or       *
 * (at your option) any later version.                                        *
 *                                                                            *
 * This program is distributed in the hope that it will be useful,            *
 * but WITHOUT ANY WARRANTY; without even the implied warranty of             *
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the              *
 * GNU Lesser General Public License for more details.                        *
 *                                                                            *
 * You should have received a copy of the GNU Lesser General Public License   *
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.      *
 * -------------------------------------------------------------------------- */

#include "OpenCLContext.h"
#include "OpenCLPlatform.h"
#include "OpenCLKernelFactory.h"
#include "OpenCLKernels.h"
#include "openmm/Context.h"
#include "openmm/System.h"
31
32
#include "openmm/internal/ContextImpl.h"
#include "openmm/internal/hardware.h"
33
#include <algorithm>
34
#include <cctype>
35
#include <sstream>
36
37
38
39
#ifdef __APPLE__
#include "sys/sysctl.h"
#endif

40
41

using namespace OpenMM;
42
using namespace std;
43

44
#ifdef OPENMM_COMMON_BUILDING_STATIC_LIBRARY
45
extern "C" void registerOpenCLPlatform() {
46
47
    if (OpenCLPlatform::isPlatformSupported())
        Platform::registerPlatform(new OpenCLPlatform());
48
49
}
#else
50
extern "C" OPENMM_EXPORT_COMMON void registerPlatforms() {
51
52
    if (OpenCLPlatform::isPlatformSupported())
        Platform::registerPlatform(new OpenCLPlatform());
53
}
54
#endif
55
56

OpenCLPlatform::OpenCLPlatform() {
Peter Eastman's avatar
Peter Eastman committed
57
58
59
60
61
    deprecatedPropertyReplacements["OpenCLDeviceIndex"] = OpenCLDeviceIndex();
    deprecatedPropertyReplacements["OpenCLDeviceName"] = OpenCLDeviceName();
    deprecatedPropertyReplacements["OpenCLPrecision"] = OpenCLPrecision();
    deprecatedPropertyReplacements["OpenCLUseCpuPme"] = OpenCLUseCpuPme();
    deprecatedPropertyReplacements["OpenCLDisablePmeStream"] = OpenCLDisablePmeStream();
62
    OpenCLKernelFactory* factory = new OpenCLKernelFactory();
63
    registerKernelFactory(CalcForcesAndEnergyKernel::Name(), factory);
64
    registerKernelFactory(UpdateStateDataKernel::Name(), factory);
65
    registerKernelFactory(ApplyConstraintsKernel::Name(), factory);
66
    registerKernelFactory(VirtualSitesKernel::Name(), factory);
67
    registerKernelFactory(MinimizeKernel::Name(), factory);
68
    registerKernelFactory(CalcHarmonicBondForceKernel::Name(), factory);
69
    registerKernelFactory(CalcCustomBondForceKernel::Name(), factory);
70
    registerKernelFactory(CalcHarmonicAngleForceKernel::Name(), factory);
71
    registerKernelFactory(CalcCustomAngleForceKernel::Name(), factory);
72
    registerKernelFactory(CalcPeriodicTorsionForceKernel::Name(), factory);
73
    registerKernelFactory(CalcRBTorsionForceKernel::Name(), factory);
74
    registerKernelFactory(CalcCMAPTorsionForceKernel::Name(), factory);
75
    registerKernelFactory(CalcCustomTorsionForceKernel::Name(), factory);
76
    registerKernelFactory(CalcNonbondedForceKernel::Name(), factory);
77
    registerKernelFactory(CalcConstantPotentialForceKernel::Name(), factory);
78
    registerKernelFactory(CalcCustomNonbondedForceKernel::Name(), factory);
79
    registerKernelFactory(CalcGBSAOBCForceKernel::Name(), factory);
80
    registerKernelFactory(CalcCustomGBForceKernel::Name(), factory);
81
    registerKernelFactory(CalcCustomExternalForceKernel::Name(), factory);
82
    registerKernelFactory(CalcCustomHbondForceKernel::Name(), factory);
83
    registerKernelFactory(CalcCustomCentroidBondForceKernel::Name(), factory);
84
    registerKernelFactory(CalcCustomCompoundBondForceKernel::Name(), factory);
85
    registerKernelFactory(CalcCustomCPPForceKernel::Name(), factory);
86
    registerKernelFactory(CalcCustomCVForceKernel::Name(), factory);
87
    registerKernelFactory(CalcATMForceKernel::Name(), factory);
88
    registerKernelFactory(CalcOrientationRestraintForceKernel::Name(), factory);
Peter Eastman's avatar
Peter Eastman committed
89
    registerKernelFactory(CalcPythonForceKernel::Name(), factory);
90
    registerKernelFactory(CalcRGForceKernel::Name(), factory);
peastman's avatar
peastman committed
91
    registerKernelFactory(CalcRMSDForceKernel::Name(), factory);
92
    registerKernelFactory(CalcCustomManyParticleForceKernel::Name(), factory);
93
    registerKernelFactory(CalcGayBerneForceKernel::Name(), factory);
Evan Pretti's avatar
Evan Pretti committed
94
    registerKernelFactory(CalcLCPOForceKernel::Name(), factory);
95
    registerKernelFactory(IntegrateVerletStepKernel::Name(), factory);
96
    registerKernelFactory(IntegrateNoseHooverStepKernel::Name(), factory);
97
    registerKernelFactory(IntegrateLangevinMiddleStepKernel::Name(), factory);
98
    registerKernelFactory(IntegrateBrownianStepKernel::Name(), factory);
99
100
    registerKernelFactory(IntegrateVariableVerletStepKernel::Name(), factory);
    registerKernelFactory(IntegrateVariableLangevinStepKernel::Name(), factory);
101
    registerKernelFactory(IntegrateCustomStepKernel::Name(), factory);
Peter Eastman's avatar
Peter Eastman committed
102
    registerKernelFactory(IntegrateDPDStepKernel::Name(), factory);
103
    registerKernelFactory(IntegrateQTBStepKernel::Name(), factory);
104
    registerKernelFactory(ApplyAndersenThermostatKernel::Name(), factory);
105
    registerKernelFactory(ApplyMonteCarloBarostatKernel::Name(), factory);
106
    registerKernelFactory(RemoveCMMotionKernel::Name(), factory);
107
    platformProperties.push_back(OpenCLDeviceIndex());
108
    platformProperties.push_back(OpenCLDeviceName());
109
    platformProperties.push_back(OpenCLPlatformIndex());
110
    platformProperties.push_back(OpenCLPlatformName());
111
    platformProperties.push_back(OpenCLPrecision());
112
    platformProperties.push_back(OpenCLUseCpuPme());
113
    platformProperties.push_back(OpenCLDisablePmeStream());
114
    setPropertyDefaultValue(OpenCLDeviceIndex(), "");
115
    setPropertyDefaultValue(OpenCLDeviceName(), "");
116
    setPropertyDefaultValue(OpenCLPlatformIndex(), "");
117
    setPropertyDefaultValue(OpenCLPlatformName(), "");
118
    setPropertyDefaultValue(OpenCLPrecision(), "single");
119
    setPropertyDefaultValue(OpenCLUseCpuPme(), "false");
120
    setPropertyDefaultValue(OpenCLDisablePmeStream(), "false");
121
122
}

123
124
125
126
double OpenCLPlatform::getSpeed() const {
    return 50;
}

127
bool OpenCLPlatform::supportsDoublePrecision() const {
Peter Eastman's avatar
Peter Eastman committed
128
    return true;
129
130
}

131
132
bool OpenCLPlatform::isPlatformSupported() {
    // Return false for OpenCL implementations that are known
133
    // to be buggy (Apple OS X prior to 10.10).
134
135
136
137
138
139
140
141
142
143
144
145

#ifdef __APPLE__
    char str[256];
    size_t size = sizeof(str);
    int ret = sysctlbyname("kern.osrelease", str, &size, NULL, 0);
    if (ret != 0)
        return false;

    int major, minor, micro;
    if (sscanf(str, "%d.%d.%d", &major, &minor, &micro) != 3)
        return false;

146
147
    if (major < 14 || (major == 14 && minor < 3))
        // 14.3.0 is the darwin release corresponding to OS X 10.10.3. Versions prior to that
148
        // contained a number of serious bugs in the Apple OpenCL libraries.
Evan Pretti's avatar
Evan Pretti committed
149
        // (See https://github.com/openmm/openmm/issues/395 for example.)
150
151
152
        return false;
#endif

153
154
155
    // Make sure at least one OpenCL implementation is installed.

    std::vector<cl::Platform> platforms;
156
157
158
159
160
161
    try {
        cl::Platform::get(&platforms);
        if (platforms.size() == 0)
            return false;
    }
    catch (...) {
162
        return false;
163
    }
164
165
166
    return true;
}

167
168
169
const string& OpenCLPlatform::getPropertyValue(const Context& context, const string& property) const {
    const ContextImpl& impl = getContextImpl(context);
    const PlatformData* data = reinterpret_cast<const PlatformData*>(impl.getPlatformData());
170
171
172
173
    string propertyName = property;
    if (deprecatedPropertyReplacements.find(property) != deprecatedPropertyReplacements.end())
        propertyName = deprecatedPropertyReplacements.find(property)->second;
    map<string, string>::const_iterator value = data->propertyValues.find(propertyName);
174
175
176
177
178
179
180
181
    if (value != data->propertyValues.end())
        return value->second;
    return Platform::getPropertyValue(context, property);
}

void OpenCLPlatform::setPropertyValue(Context& context, const string& property, const string& value) const {
}

182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
vector<map<string, string> > OpenCLPlatform::getDevices(const map<string, string>& filters) const {
    // Check for properties that might act as filters.

    int platformIndex = -1;
    if (filters.find(OpenCLPlatformIndex()) != filters.end())
        stringstream(filters.at(OpenCLPlatformIndex())) >> platformIndex;
    string platformName = (filters.find(OpenCLPlatformName()) == filters.end() ? "" : filters.at(OpenCLPlatformName()));
    int deviceIndex = -1;
    if (filters.find(OpenCLDeviceIndex()) != filters.end())
        stringstream(filters.at(OpenCLDeviceIndex())) >> deviceIndex;
    string deviceName = (filters.find(OpenCLDeviceName()) == filters.end() ? "" : filters.at(OpenCLDeviceName()));
    bool needsDouble = false;
    if (filters.find(OpenCLPrecision()) != filters.end()) {
        string precision = filters.at(OpenCLPrecision());
        transform(precision.begin(), precision.end(), precision.begin(), ::tolower);
        needsDouble = (precision != "single");
    }

    // Loop over platforms.

    vector<map<string, string> > results;
    vector<cl::Platform> platforms;
    cl::Platform::get(&platforms);
    for (int i = 0; i < platforms.size(); i++) {
        if (platformIndex != -1 && platformIndex != i)
            continue;
        if (platformName.size() > 0 && platformName != platforms[i].getInfo<CL_PLATFORM_NAME>())
            continue;

        // Loop over devices for the platform.

        vector<cl::Device> devices;
        try {
            platforms[i].getDevices(CL_DEVICE_TYPE_GPU | CL_DEVICE_TYPE_CPU, &devices);
        }
        catch (...) {
            // There are no devices available for this platform.
            continue;
        }
        for (int j = 0; j < devices.size(); j++) {
            if (deviceIndex != -1 && deviceIndex != j)
                continue;
            if (deviceName.size() > 0 && deviceName != devices[j].getInfo<CL_DEVICE_NAME>())
                continue;
            bool supportsDouble = (devices[j].getInfo<CL_DEVICE_EXTENSIONS>().find("cl_khr_fp64") != string::npos);
            if (needsDouble && !supportsDouble)
                continue;
            stringstream platformIndexStr, deviceIndexStr;
            platformIndexStr << i;
            deviceIndexStr << j;
            map<string, string> properties = {{OpenCLPlatformIndex(), platformIndexStr.str()},
                                              {OpenCLPlatformName(), platforms[i].getInfo<CL_PLATFORM_NAME>()},
                                              {OpenCLDeviceIndex(), deviceIndexStr.str()},
                                              {OpenCLDeviceName(), devices[j].getInfo<CL_DEVICE_NAME>()}};
            results.push_back(properties);
        }
    }
    return results;
}

242
void OpenCLPlatform::contextCreated(ContextImpl& context, const map<string, string>& properties) const {
243
244
    const string& platformPropValue = (properties.find(OpenCLPlatformIndex()) == properties.end() ?
            getPropertyDefaultValue(OpenCLPlatformIndex()) : properties.find(OpenCLPlatformIndex())->second);
245
246
    const string& devicePropValue = (properties.find(OpenCLDeviceIndex()) == properties.end() ?
            getPropertyDefaultValue(OpenCLDeviceIndex()) : properties.find(OpenCLDeviceIndex())->second);
247
248
    string precisionPropValue = (properties.find(OpenCLPrecision()) == properties.end() ?
            getPropertyDefaultValue(OpenCLPrecision()) : properties.find(OpenCLPrecision())->second);
249
250
    string cpuPmePropValue = (properties.find(OpenCLUseCpuPme()) == properties.end() ?
            getPropertyDefaultValue(OpenCLUseCpuPme()) : properties.find(OpenCLUseCpuPme())->second);
251
252
    string pmeStreamPropValue = (properties.find(OpenCLDisablePmeStream()) == properties.end() ?
            getPropertyDefaultValue(OpenCLDisablePmeStream()) : properties.find(OpenCLDisablePmeStream())->second);
253
254
    transform(precisionPropValue.begin(), precisionPropValue.end(), precisionPropValue.begin(), ::tolower);
    transform(cpuPmePropValue.begin(), cpuPmePropValue.end(), cpuPmePropValue.begin(), ::tolower);
255
    transform(pmeStreamPropValue.begin(), pmeStreamPropValue.end(), pmeStreamPropValue.begin(), ::tolower);
256
257
258
259
    vector<string> pmeKernelName;
    pmeKernelName.push_back(CalcPmeReciprocalForceKernel::Name());
    if (!supportsKernels(pmeKernelName))
        cpuPmePropValue = "false";
260
261
262
263
    int threads = getNumProcessors();
    char* threadsEnv = getenv("OPENMM_CPU_THREADS");
    if (threadsEnv != NULL)
        stringstream(threadsEnv) >> threads;
264
    context.setPlatformData(new PlatformData(context.getSystem(), &context, platformPropValue, devicePropValue, precisionPropValue, cpuPmePropValue,
265
266
267
268
269
270
271
272
273
274
275
            pmeStreamPropValue, threads, NULL));
}

void OpenCLPlatform::linkedContextCreated(ContextImpl& context, ContextImpl& originalContext) const {
    Platform& platform = originalContext.getPlatform();
    string platformPropValue = platform.getPropertyValue(originalContext.getOwner(), OpenCLPlatformIndex());
    string devicePropValue = platform.getPropertyValue(originalContext.getOwner(), OpenCLDeviceIndex());
    string precisionPropValue = platform.getPropertyValue(originalContext.getOwner(), OpenCLPrecision());
    string cpuPmePropValue = platform.getPropertyValue(originalContext.getOwner(), OpenCLUseCpuPme());
    string pmeStreamPropValue = platform.getPropertyValue(originalContext.getOwner(), OpenCLDisablePmeStream());
    int threads = reinterpret_cast<PlatformData*>(originalContext.getPlatformData())->threads.getNumThreads();
276
    context.setPlatformData(new PlatformData(context.getSystem(), &context, platformPropValue, devicePropValue, precisionPropValue, cpuPmePropValue,
277
            pmeStreamPropValue, threads, &originalContext));
278
279
280
281
282
283
284
}

void OpenCLPlatform::contextDestroyed(ContextImpl& context) const {
    PlatformData* data = reinterpret_cast<PlatformData*>(context.getPlatformData());
    delete data;
}

285
OpenCLPlatform::PlatformData::PlatformData(const System& system, ContextImpl* context, const string& platformPropValue, const string& deviceIndexProperty,
286
        const string& precisionProperty, const string& cpuPmeProperty, const string& pmeStreamProperty, int numThreads, ContextImpl* originalContext) :
287
            context(context), removeCM(false), stepCount(0), computeForceCount(0), time(0.0), hasInitializedContexts(false), threads(numThreads)  {
Robert McGibbon's avatar
Robert McGibbon committed
288
    int platformIndex = -1;
289
290
    if (platformPropValue.length() > 0)
        stringstream(platformPropValue) >> platformIndex;
291
292
    vector<string> devices;
    size_t searchPos = 0, nextPos;
293
    while ((nextPos = deviceIndexProperty.find_first_of(", ", searchPos)) != string::npos) {
294
295
296
297
        devices.push_back(deviceIndexProperty.substr(searchPos, nextPos-searchPos));
        searchPos = nextPos+1;
    }
    devices.push_back(deviceIndexProperty.substr(searchPos));
298
299
300
    PlatformData* originalData = NULL;
    if (originalContext != NULL)
        originalData = reinterpret_cast<PlatformData*>(originalContext->getPlatformData());
301
302
303
    try {
        for (int i = 0; i < (int) devices.size(); i++) {
            if (devices[i].length() > 0) {
peastman's avatar
peastman committed
304
                int deviceIndex;
305
                stringstream(devices[i]) >> deviceIndex;
306
                contexts.push_back(new OpenCLContext(system, platformIndex, deviceIndex, precisionProperty, *this, (originalData == NULL ? NULL : originalData->contexts[i])));
307
            }
308
        }
309
        if (contexts.size() == 0)
310
            contexts.push_back(new OpenCLContext(system, platformIndex, -1, precisionProperty, *this, (originalData == NULL ? NULL : originalData->contexts[0])));
311
312
313
314
315
316
317
    }
    catch (...) {
        // If an exception was thrown, do our best to clean up memory.
        
        for (int i = 0; i < (int) contexts.size(); i++)
            delete contexts[i];
        throw;
318
    }
319
    stringstream deviceIndex, deviceName;
320
    for (int i = 0; i < (int) contexts.size(); i++) {
321
322
323
324
325
326
        if (i > 0) {
            deviceIndex << ',';
            deviceName << ',';
        }
        deviceIndex << contexts[i]->getDeviceIndex();
        deviceName << contexts[i]->getDevice().getInfo<CL_DEVICE_NAME>();
327
    }
Robert McGibbon's avatar
Robert McGibbon committed
328
329
    platformIndex = contexts[0]->getPlatformIndex();

330
    useCpuPme = (cpuPmeProperty == "true" && !contexts[0]->getUseDoublePrecision());
331
    disablePmeStream = (pmeStreamProperty == "true");
332
333
    propertyValues[OpenCLPlatform::OpenCLDeviceIndex()] = deviceIndex.str();
    propertyValues[OpenCLPlatform::OpenCLDeviceName()] = deviceName.str();
334
    propertyValues[OpenCLPlatform::OpenCLPlatformIndex()] = contexts[0]->intToString(platformIndex);
335
336
337
    std::vector<cl::Platform> platforms;
    cl::Platform::get(&platforms);
    propertyValues[OpenCLPlatform::OpenCLPlatformName()] = platforms[platformIndex].getInfo<CL_PLATFORM_NAME>();
338
    propertyValues[OpenCLPlatform::OpenCLPrecision()] = precisionProperty;
339
    propertyValues[OpenCLPlatform::OpenCLUseCpuPme()] = useCpuPme ? "true" : "false";
340
    propertyValues[OpenCLPlatform::OpenCLDisablePmeStream()] = disablePmeStream ? "true" : "false";
341
    contextEnergy.resize(contexts.size());
342
}
343
344

OpenCLPlatform::PlatformData::~PlatformData() {
345
346
347
348
349
    for (int i = 0; i < (int) contexts.size(); i++)
        delete contexts[i];
}

void OpenCLPlatform::PlatformData::initializeContexts(const System& system) {
350
351
    if (hasInitializedContexts)
        return;
352
    for (int i = 0; i < (int) contexts.size(); i++)
353
        contexts[i]->initialize();
354
    hasInitializedContexts = true;
355
}
356
357
358
359
360

void OpenCLPlatform::PlatformData::syncContexts() {
    for (int i = 0; i < (int) contexts.size(); i++)
        contexts[i]->getWorkThread().flush();
}