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(CalcHarmonicBondForceKernel::Name(), factory);
68
    registerKernelFactory(CalcCustomBondForceKernel::Name(), factory);
69
    registerKernelFactory(CalcHarmonicAngleForceKernel::Name(), factory);
70
    registerKernelFactory(CalcCustomAngleForceKernel::Name(), factory);
71
    registerKernelFactory(CalcPeriodicTorsionForceKernel::Name(), factory);
72
    registerKernelFactory(CalcRBTorsionForceKernel::Name(), factory);
73
    registerKernelFactory(CalcCMAPTorsionForceKernel::Name(), factory);
74
    registerKernelFactory(CalcCustomTorsionForceKernel::Name(), factory);
75
    registerKernelFactory(CalcNonbondedForceKernel::Name(), factory);
76
    registerKernelFactory(CalcConstantPotentialForceKernel::Name(), factory);
77
    registerKernelFactory(CalcCustomNonbondedForceKernel::Name(), factory);
78
    registerKernelFactory(CalcGBSAOBCForceKernel::Name(), factory);
79
    registerKernelFactory(CalcCustomGBForceKernel::Name(), factory);
80
    registerKernelFactory(CalcCustomExternalForceKernel::Name(), factory);
81
    registerKernelFactory(CalcCustomHbondForceKernel::Name(), factory);
82
    registerKernelFactory(CalcCustomCentroidBondForceKernel::Name(), factory);
83
    registerKernelFactory(CalcCustomCompoundBondForceKernel::Name(), factory);
84
    registerKernelFactory(CalcCustomCPPForceKernel::Name(), factory);
85
    registerKernelFactory(CalcCustomCVForceKernel::Name(), factory);
86
    registerKernelFactory(CalcATMForceKernel::Name(), factory);
87
    registerKernelFactory(CalcOrientationRestraintForceKernel::Name(), factory);
Peter Eastman's avatar
Peter Eastman committed
88
    registerKernelFactory(CalcPythonForceKernel::Name(), factory);
89
    registerKernelFactory(CalcRGForceKernel::Name(), factory);
peastman's avatar
peastman committed
90
    registerKernelFactory(CalcRMSDForceKernel::Name(), factory);
91
    registerKernelFactory(CalcCustomManyParticleForceKernel::Name(), factory);
92
    registerKernelFactory(CalcGayBerneForceKernel::Name(), factory);
Evan Pretti's avatar
Evan Pretti committed
93
    registerKernelFactory(CalcLCPOForceKernel::Name(), factory);
94
    registerKernelFactory(IntegrateVerletStepKernel::Name(), factory);
95
    registerKernelFactory(IntegrateNoseHooverStepKernel::Name(), factory);
96
    registerKernelFactory(IntegrateLangevinMiddleStepKernel::Name(), factory);
97
    registerKernelFactory(IntegrateBrownianStepKernel::Name(), factory);
98
99
    registerKernelFactory(IntegrateVariableVerletStepKernel::Name(), factory);
    registerKernelFactory(IntegrateVariableLangevinStepKernel::Name(), factory);
100
    registerKernelFactory(IntegrateCustomStepKernel::Name(), factory);
Peter Eastman's avatar
Peter Eastman committed
101
    registerKernelFactory(IntegrateDPDStepKernel::Name(), factory);
102
    registerKernelFactory(IntegrateQTBStepKernel::Name(), factory);
103
    registerKernelFactory(ApplyAndersenThermostatKernel::Name(), factory);
104
    registerKernelFactory(ApplyMonteCarloBarostatKernel::Name(), factory);
105
    registerKernelFactory(RemoveCMMotionKernel::Name(), factory);
106
    platformProperties.push_back(OpenCLDeviceIndex());
107
    platformProperties.push_back(OpenCLDeviceName());
108
    platformProperties.push_back(OpenCLPlatformIndex());
109
    platformProperties.push_back(OpenCLPlatformName());
110
    platformProperties.push_back(OpenCLPrecision());
111
    platformProperties.push_back(OpenCLUseCpuPme());
112
    platformProperties.push_back(OpenCLDisablePmeStream());
113
    setPropertyDefaultValue(OpenCLDeviceIndex(), "");
114
    setPropertyDefaultValue(OpenCLDeviceName(), "");
115
    setPropertyDefaultValue(OpenCLPlatformIndex(), "");
116
    setPropertyDefaultValue(OpenCLPlatformName(), "");
117
    setPropertyDefaultValue(OpenCLPrecision(), "single");
118
    setPropertyDefaultValue(OpenCLUseCpuPme(), "false");
119
    setPropertyDefaultValue(OpenCLDisablePmeStream(), "false");
120
121
}

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

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

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

#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;

145
146
    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
147
        // contained a number of serious bugs in the Apple OpenCL libraries.
Evan Pretti's avatar
Evan Pretti committed
148
        // (See https://github.com/openmm/openmm/issues/395 for example.)
149
150
151
        return false;
#endif

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

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

166
167
168
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());
169
170
171
172
    string propertyName = property;
    if (deprecatedPropertyReplacements.find(property) != deprecatedPropertyReplacements.end())
        propertyName = deprecatedPropertyReplacements.find(property)->second;
    map<string, string>::const_iterator value = data->propertyValues.find(propertyName);
173
174
175
176
177
178
179
180
    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 {
}

181
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
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;
}

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

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

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

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

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

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

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