~ubuntu-branches/ubuntu/precise/boinc/precise

« back to all changes in this revision

Viewing changes to client/coproc_detect.cpp

Tags: 6.12.8+dfsg-1
* New upstream release.
* Simplified debian/rules

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
// This file is part of BOINC.
 
2
// http://boinc.berkeley.edu
 
3
// Copyright (C) 2009 University of California
 
4
//
 
5
// BOINC is free software; you can redistribute it and/or modify it
 
6
// under the terms of the GNU Lesser General Public License
 
7
// as published by the Free Software Foundation,
 
8
// either version 3 of the License, or (at your option) any later version.
 
9
//
 
10
// BOINC is distributed in the hope that it will be useful,
 
11
// but WITHOUT ANY WARRANTY; without even the implied warranty of
 
12
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 
13
// See the GNU Lesser General Public License for more details.
 
14
//
 
15
// You should have received a copy of the GNU Lesser General Public License
 
16
// along with BOINC.  If not, see <http://www.gnu.org/licenses/>.
 
17
 
 
18
 
 
19
// client-specific GPU code.  Mostly GPU detection
 
20
 
 
21
#include "cpp.h"
 
22
 
 
23
#ifdef _WIN32
 
24
#include "boinc_win.h"
 
25
#ifndef SIM
 
26
#include <nvapi.h>
 
27
#endif
 
28
#else
 
29
#ifdef __APPLE__
 
30
// Suppress obsolete warning when building for OS 10.3.9
 
31
#define DLOPEN_NO_WARN
 
32
#endif
 
33
#include "config.h"
 
34
#include <dlfcn.h>
 
35
#include <setjmp.h>
 
36
#include <signal.h>
 
37
#endif
 
38
 
 
39
#include "coproc.h"
 
40
#include "str_util.h"
 
41
#include "util.h"
 
42
 
 
43
#include "client_state.h"
 
44
#include "client_msgs.h"
 
45
 
 
46
using std::string;
 
47
using std::vector;
 
48
 
 
49
//#define MEASURE_AVAILABLE_RAM
 
50
 
 
51
static bool in_vector(int n, vector<int>& v) {
 
52
    for (unsigned int i=0; i<v.size(); i++) {
 
53
        if (v[i] == n) return true;
 
54
    }
 
55
    return false;
 
56
}
 
57
 
 
58
#ifndef _WIN32
 
59
jmp_buf resume;
 
60
 
 
61
void segv_handler(int) {
 
62
    longjmp(resume, 1);
 
63
}
 
64
#endif
 
65
 
 
66
void COPROC::print_available_ram() {
 
67
#ifdef MEASURE_AVAILABLE_RAM
 
68
    if (gstate.now - last_print_time < 60) return;
 
69
    last_print_time = gstate.now;
 
70
 
 
71
    for (int i=0; i<count; i++) {
 
72
        if (available_ram_unknown[i]) {
 
73
            if (log_flags.coproc_debug) {
 
74
                msg_printf(0, MSG_INFO,
 
75
                    "[coproc] %s device %d: available RAM unknown",
 
76
                    type, device_nums[i]
 
77
                );
 
78
            }
 
79
        } else {
 
80
            if (log_flags.coproc_debug) {
 
81
                msg_printf(0, MSG_INFO,
 
82
                    "[coproc] %s device %d: available RAM %d MB",
 
83
                    type, device_nums[i],
 
84
                    (int)(available_ram[i]/MEGA)
 
85
                );
 
86
            }
 
87
        }
 
88
    }
 
89
#endif
 
90
}
 
91
 
 
92
void COPROCS::get(
 
93
    bool use_all, vector<string>&descs, vector<string>&warnings,
 
94
    vector<int>& ignore_cuda_dev,
 
95
    vector<int>& ignore_ati_dev
 
96
) {
 
97
 
 
98
#ifdef _WIN32
 
99
    try {
 
100
        cuda.get(use_all, descs, warnings, ignore_cuda_dev);
 
101
    }
 
102
    catch (...) {
 
103
        warnings.push_back("Caught SIGSEGV in NVIDIA GPU detection");
 
104
    }
 
105
    try {
 
106
        ati.get(use_all, descs, warnings, ignore_ati_dev);
 
107
    } 
 
108
    catch (...) {
 
109
        warnings.push_back("Caught SIGSEGV in ATI GPU detection");
 
110
    }
 
111
#else
 
112
    void (*old_sig)(int) = signal(SIGSEGV, segv_handler);
 
113
    if (setjmp(resume)) {
 
114
        warnings.push_back("Caught SIGSEGV in NVIDIA GPU detection");
 
115
    } else {
 
116
        cuda.get(use_all, descs, warnings, ignore_cuda_dev);
 
117
    }
 
118
#ifndef __APPLE__       // ATI does not yet support CAL on Macs
 
119
    if (setjmp(resume)) {
 
120
        warnings.push_back("Caught SIGSEGV in ATI GPU detection");
 
121
    } else {
 
122
        ati.get(use_all, descs, warnings, ignore_ati_dev);
 
123
    }
 
124
#endif
 
125
    signal(SIGSEGV, old_sig);
 
126
#endif
 
127
}
 
128
 
 
129
// return 1/-1/0 if device 1 is more/less/same capable than device 2.
 
130
// factors (decreasing priority):
 
131
// - compute capability
 
132
// - software version
 
133
// - memory
 
134
// - speed
 
135
//
 
136
// If "loose", ignore FLOPS and tolerate small memory diff
 
137
//
 
138
int cuda_compare(COPROC_CUDA& c1, COPROC_CUDA& c2, bool loose) {
 
139
    if (c1.prop.major > c2.prop.major) return 1;
 
140
    if (c1.prop.major < c2.prop.major) return -1;
 
141
    if (c1.prop.minor > c2.prop.minor) return 1;
 
142
    if (c1.prop.minor < c2.prop.minor) return -1;
 
143
    if (c1.cuda_version > c2.cuda_version) return 1;
 
144
    if (c1.cuda_version < c2.cuda_version) return -1;
 
145
    if (loose) {
 
146
        if (c1.prop.totalGlobalMem > 1.4*c2.prop.totalGlobalMem) return 1;
 
147
        if (c1.prop.totalGlobalMem < .7* c2.prop.totalGlobalMem) return -1;
 
148
        return 0;
 
149
    }
 
150
    if (c1.prop.totalGlobalMem > c2.prop.totalGlobalMem) return 1;
 
151
    if (c1.prop.totalGlobalMem < c2.prop.totalGlobalMem) return -1;
 
152
    double s1 = c1.peak_flops();
 
153
    double s2 = c2.peak_flops();
 
154
    if (s1 > s2) return 1;
 
155
    if (s1 < s2) return -1;
 
156
    return 0;
 
157
}
 
158
 
 
159
enum CUdevice_attribute_enum {
 
160
  CU_DEVICE_ATTRIBUTE_MAX_THREADS_PER_BLOCK = 1,
 
161
  CU_DEVICE_ATTRIBUTE_MAX_BLOCK_DIM_X = 2,
 
162
  CU_DEVICE_ATTRIBUTE_MAX_BLOCK_DIM_Y = 3,
 
163
  CU_DEVICE_ATTRIBUTE_MAX_BLOCK_DIM_Z = 4,
 
164
  CU_DEVICE_ATTRIBUTE_MAX_GRID_DIM_X = 5,
 
165
  CU_DEVICE_ATTRIBUTE_MAX_GRID_DIM_Y = 6,
 
166
  CU_DEVICE_ATTRIBUTE_MAX_GRID_DIM_Z = 7,
 
167
  CU_DEVICE_ATTRIBUTE_SHARED_MEMORY_PER_BLOCK = 8,
 
168
  CU_DEVICE_ATTRIBUTE_TOTAL_CONSTANT_MEMORY = 9,
 
169
  CU_DEVICE_ATTRIBUTE_WARP_SIZE = 10,
 
170
  CU_DEVICE_ATTRIBUTE_MAX_PITCH = 11,
 
171
  CU_DEVICE_ATTRIBUTE_REGISTERS_PER_BLOCK = 12,
 
172
  CU_DEVICE_ATTRIBUTE_CLOCK_RATE = 13,
 
173
  CU_DEVICE_ATTRIBUTE_TEXTURE_ALIGNMENT = 14,
 
174
  CU_DEVICE_ATTRIBUTE_GPU_OVERLAP = 15,
 
175
  CU_DEVICE_ATTRIBUTE_MULTIPROCESSOR_COUNT = 16,
 
176
  CU_DEVICE_ATTRIBUTE_KERNEL_EXEC_TIMEOUT = 17,
 
177
  CU_DEVICE_ATTRIBUTE_INTEGRATED = 18,
 
178
  CU_DEVICE_ATTRIBUTE_CAN_MAP_HOST_MEMORY = 19,
 
179
  CU_DEVICE_ATTRIBUTE_COMPUTE_MODE = 20
 
180
};
 
181
 
 
182
#ifdef _WIN32
 
183
typedef int (__stdcall *CUDA_GDC)(int *count);
 
184
typedef int (__stdcall *CUDA_GDV)(int* version);
 
185
typedef int (__stdcall *CUDA_GDI)(int);
 
186
typedef int (__stdcall *CUDA_GDG)(int*, int);
 
187
typedef int (__stdcall *CUDA_GDA)(int*, int, int);
 
188
typedef int (__stdcall *CUDA_GDN)(char*, int, int);
 
189
typedef int (__stdcall *CUDA_GDM)(unsigned int*, int);
 
190
typedef int (__stdcall *CUDA_GDCC)(int*, int*, int);
 
191
typedef int (__stdcall *CUDA_CC)(unsigned int*, unsigned int, unsigned int);
 
192
typedef int (__stdcall *CUDA_CD)(unsigned int);
 
193
typedef int (__stdcall *CUDA_MA)(unsigned int*, unsigned int);
 
194
typedef int (__stdcall *CUDA_MF)(unsigned int);
 
195
typedef int (__stdcall *CUDA_MGI)(unsigned int*, unsigned int*);
 
196
 
 
197
CUDA_GDC __cuDeviceGetCount = NULL;
 
198
CUDA_GDV __cuDriverGetVersion = NULL;
 
199
CUDA_GDI __cuInit = NULL;
 
200
CUDA_GDG __cuDeviceGet = NULL;
 
201
CUDA_GDA __cuDeviceGetAttribute = NULL;
 
202
CUDA_GDN __cuDeviceGetName = NULL;
 
203
CUDA_GDM __cuDeviceTotalMem = NULL;
 
204
CUDA_GDCC __cuDeviceComputeCapability = NULL;
 
205
CUDA_CC __cuCtxCreate = NULL;
 
206
CUDA_CD __cuCtxDestroy = NULL;
 
207
CUDA_MA __cuMemAlloc = NULL;
 
208
CUDA_MF __cuMemFree = NULL;
 
209
CUDA_MGI __cuMemGetInfo = NULL;
 
210
#else
 
211
void* cudalib;
 
212
int (*__cuInit)(int);
 
213
int (*__cuDeviceGetCount)(int*);
 
214
int (*__cuDriverGetVersion)(int*);
 
215
int (*__cuDeviceGet)(int*, int);
 
216
int (*__cuDeviceGetAttribute)(int*, int, int);
 
217
int (*__cuDeviceGetName)(char*, int, int);
 
218
int (*__cuDeviceTotalMem)(unsigned int*, int);
 
219
int (*__cuDeviceComputeCapability)(int*, int*, int);
 
220
int (*__cuCtxCreate)(unsigned int*, unsigned int, unsigned int);
 
221
int (*__cuCtxDestroy)(unsigned int);
 
222
int (*__cuMemAlloc)(unsigned int*, unsigned int);
 
223
int (*__cuMemFree)(unsigned int);
 
224
int (*__cuMemGetInfo)(unsigned int*, unsigned int*);
 
225
#endif
 
226
 
 
227
// NVIDIA interfaces are documented here:
 
228
// http://developer.download.nvidia.com/compute/cuda/2_3/toolkit/docs/online/index.html
 
229
 
 
230
void COPROC_CUDA::get(
 
231
    bool use_all,    // if false, use only those equivalent to most capable
 
232
    vector<string>& descs,
 
233
    vector<string>& warnings,
 
234
    vector<int>& ignore_devs
 
235
) {
 
236
    int count, retval;
 
237
    char buf[256];
 
238
 
 
239
#ifdef _WIN32
 
240
    HMODULE cudalib = LoadLibrary("nvcuda.dll");
 
241
    if (!cudalib) {
 
242
        warnings.push_back("No NVIDIA library found");
 
243
        return;
 
244
    }
 
245
    __cuDeviceGetCount = (CUDA_GDC)GetProcAddress( cudalib, "cuDeviceGetCount" );
 
246
    __cuDriverGetVersion = (CUDA_GDV)GetProcAddress( cudalib, "cuDriverGetVersion" );
 
247
    __cuInit = (CUDA_GDI)GetProcAddress( cudalib, "cuInit" );
 
248
    __cuDeviceGet = (CUDA_GDG)GetProcAddress( cudalib, "cuDeviceGet" );
 
249
    __cuDeviceGetAttribute = (CUDA_GDA)GetProcAddress( cudalib, "cuDeviceGetAttribute" );
 
250
    __cuDeviceGetName = (CUDA_GDN)GetProcAddress( cudalib, "cuDeviceGetName" );
 
251
    __cuDeviceTotalMem = (CUDA_GDM)GetProcAddress( cudalib, "cuDeviceTotalMem" );
 
252
    __cuDeviceComputeCapability = (CUDA_GDCC)GetProcAddress( cudalib, "cuDeviceComputeCapability" );
 
253
    __cuCtxCreate = (CUDA_CC)GetProcAddress( cudalib, "cuCtxCreate" );
 
254
    __cuCtxDestroy = (CUDA_CD)GetProcAddress( cudalib, "cuCtxDestroy" );
 
255
    __cuMemAlloc = (CUDA_MA)GetProcAddress( cudalib, "cuMemAlloc" );
 
256
    __cuMemFree = (CUDA_MF)GetProcAddress( cudalib, "cuMemFree" );
 
257
    __cuMemGetInfo = (CUDA_MGI)GetProcAddress( cudalib, "cuMemGetInfo" );
 
258
 
 
259
#ifndef SIM
 
260
    NvAPI_Status nvapiStatus;
 
261
    NvDisplayHandle hDisplay;
 
262
    NV_DISPLAY_DRIVER_VERSION Version;
 
263
    memset(&Version, 0, sizeof(Version));
 
264
    Version.version = NV_DISPLAY_DRIVER_VERSION_VER;
 
265
 
 
266
    NvAPI_Initialize();
 
267
    for (int i=0; ; i++) {
 
268
        nvapiStatus = NvAPI_EnumNvidiaDisplayHandle(i, &hDisplay);
 
269
        if (nvapiStatus != NVAPI_OK) break;
 
270
        nvapiStatus = NvAPI_GetDisplayDriverVersion(hDisplay, &Version);
 
271
        if (nvapiStatus == NVAPI_OK) break;
 
272
    }
 
273
#endif
 
274
#else
 
275
 
 
276
#ifdef __APPLE__
 
277
    cudalib = dlopen("/usr/local/cuda/lib/libcuda.dylib", RTLD_NOW);
 
278
#else
 
279
    cudalib = dlopen("libcuda.so", RTLD_NOW);
 
280
#endif
 
281
    if (!cudalib) {
 
282
        warnings.push_back("No NVIDIA library found");
 
283
        return;
 
284
    }
 
285
    __cuDeviceGetCount = (int(*)(int*)) dlsym(cudalib, "cuDeviceGetCount");
 
286
    __cuDriverGetVersion = (int(*)(int*)) dlsym( cudalib, "cuDriverGetVersion" );
 
287
    __cuInit = (int(*)(int)) dlsym( cudalib, "cuInit" );
 
288
    __cuDeviceGet = (int(*)(int*, int)) dlsym( cudalib, "cuDeviceGet" );
 
289
    __cuDeviceGetAttribute = (int(*)(int*, int, int)) dlsym( cudalib, "cuDeviceGetAttribute" );
 
290
    __cuDeviceGetName = (int(*)(char*, int, int)) dlsym( cudalib, "cuDeviceGetName" );
 
291
    __cuDeviceTotalMem = (int(*)(unsigned int*, int)) dlsym( cudalib, "cuDeviceTotalMem" );
 
292
    __cuDeviceComputeCapability = (int(*)(int*, int*, int)) dlsym( cudalib, "cuDeviceComputeCapability" );
 
293
    __cuCtxCreate = (int(*)(unsigned int*, unsigned int, unsigned int)) dlsym( cudalib, "cuCtxCreate" );
 
294
    __cuCtxDestroy = (int(*)(unsigned int)) dlsym( cudalib, "cuCtxDestroy" );
 
295
    __cuMemAlloc = (int(*)(unsigned int*, unsigned int)) dlsym( cudalib, "cuMemAlloc" );
 
296
    __cuMemFree = (int(*)(unsigned int)) dlsym( cudalib, "cuMemFree" );
 
297
    __cuMemGetInfo = (int(*)(unsigned int*, unsigned int*)) dlsym( cudalib, "cuMemGetInfo" );
 
298
#endif
 
299
 
 
300
    if (!__cuDriverGetVersion) {
 
301
        warnings.push_back("cuDriverGetVersion() missing from NVIDIA library");
 
302
        return;
 
303
    }
 
304
    if (!__cuInit) {
 
305
        warnings.push_back("cuInit() missing from NVIDIA library");
 
306
        return;
 
307
    }
 
308
    if (!__cuDeviceGetCount) {
 
309
        warnings.push_back("cuDeviceGetCount() missing from NVIDIA library");
 
310
        return;
 
311
    }
 
312
    if (!__cuDeviceGet) {
 
313
        warnings.push_back("cuDeviceGet() missing from NVIDIA library");
 
314
        return;
 
315
    }
 
316
    if (!__cuDeviceGetAttribute) {
 
317
        warnings.push_back("cuDeviceGetAttribute() missing from NVIDIA library");
 
318
        return;
 
319
    }
 
320
    if (!__cuDeviceTotalMem) {
 
321
        warnings.push_back("cuDeviceTotalMem() missing from NVIDIA library");
 
322
        return;
 
323
    }
 
324
    if (!__cuDeviceComputeCapability) {
 
325
        warnings.push_back("cuDeviceComputeCapability() missing from NVIDIA library");
 
326
        return;
 
327
    }
 
328
    if (!__cuCtxCreate) {
 
329
        warnings.push_back("cuCtxCreate() missing from NVIDIA library");
 
330
        return;
 
331
    }
 
332
    if (!__cuCtxDestroy) {
 
333
        warnings.push_back("cuCtxDestroy() missing from NVIDIA library");
 
334
        return;
 
335
    }
 
336
    if (!__cuMemAlloc) {
 
337
        warnings.push_back("cuMemAlloc() missing from NVIDIA library");
 
338
        return;
 
339
    }
 
340
    if (!__cuMemFree) {
 
341
        warnings.push_back("cuMemFree() missing from NVIDIA library");
 
342
        return;
 
343
    }
 
344
    if (!__cuMemGetInfo) {
 
345
        warnings.push_back("cuMemGetInfo() missing from NVIDIA library");
 
346
        return;
 
347
    }
 
348
 
 
349
    retval = (*__cuInit)(0);
 
350
    if (retval) {
 
351
        sprintf(buf, "NVIDIA drivers present but no GPUs found");
 
352
        warnings.push_back(buf);
 
353
        return;
 
354
    }
 
355
 
 
356
    int cuda_version;
 
357
    retval = (*__cuDriverGetVersion)(&cuda_version);
 
358
    if (retval) {
 
359
        sprintf(buf, "cuDriverGetVersion() returned %d", retval);
 
360
        warnings.push_back(buf);
 
361
        return;
 
362
    }
 
363
 
 
364
    vector<COPROC_CUDA> gpus;
 
365
    retval = (*__cuDeviceGetCount)(&count);
 
366
    if (retval) {
 
367
        sprintf(buf, "cuDeviceGetCount() returned %d", retval);
 
368
        warnings.push_back(buf);
 
369
        return;
 
370
    }
 
371
    sprintf(buf, "NVIDIA library reports %d GPU%s", count, (count==1)?"":"s");
 
372
    warnings.push_back(buf);
 
373
 
 
374
    int j;
 
375
    unsigned int i;
 
376
    COPROC_CUDA cc;
 
377
    string s;
 
378
    for (j=0; j<count; j++) {
 
379
        memset(&cc.prop, 0, sizeof(cc.prop));
 
380
        int device;
 
381
        retval = (*__cuDeviceGet)(&device, j);
 
382
        if (retval) {
 
383
            sprintf(buf, "cuDeviceGet(%d) returned %d", j, retval);
 
384
            warnings.push_back(buf);
 
385
            return;
 
386
        }
 
387
        (*__cuDeviceGetName)(cc.prop.name, 256, device);
 
388
        if (retval) {
 
389
            sprintf(buf, "cuDeviceGetName(%d) returned %d", j, retval);
 
390
            warnings.push_back(buf);
 
391
            return;
 
392
        }
 
393
        (*__cuDeviceComputeCapability)(&cc.prop.major, &cc.prop.minor, device);
 
394
        (*__cuDeviceTotalMem)(&cc.prop.totalGlobalMem, device);
 
395
        (*__cuDeviceGetAttribute)(&cc.prop.sharedMemPerBlock, CU_DEVICE_ATTRIBUTE_SHARED_MEMORY_PER_BLOCK, device);
 
396
        (*__cuDeviceGetAttribute)(&cc.prop.regsPerBlock, CU_DEVICE_ATTRIBUTE_REGISTERS_PER_BLOCK, device);
 
397
        (*__cuDeviceGetAttribute)(&cc.prop.warpSize, CU_DEVICE_ATTRIBUTE_WARP_SIZE, device);
 
398
        (*__cuDeviceGetAttribute)(&cc.prop.memPitch, CU_DEVICE_ATTRIBUTE_MAX_PITCH, device);
 
399
        retval = (*__cuDeviceGetAttribute)(&cc.prop.maxThreadsPerBlock, CU_DEVICE_ATTRIBUTE_MAX_THREADS_PER_BLOCK, device);
 
400
        retval = (*__cuDeviceGetAttribute)(&cc.prop.maxThreadsDim[0], CU_DEVICE_ATTRIBUTE_MAX_BLOCK_DIM_X, device);
 
401
        (*__cuDeviceGetAttribute)(&cc.prop.maxThreadsDim[1], CU_DEVICE_ATTRIBUTE_MAX_BLOCK_DIM_Y, device);
 
402
        (*__cuDeviceGetAttribute)(&cc.prop.maxThreadsDim[2], CU_DEVICE_ATTRIBUTE_MAX_BLOCK_DIM_Z, device);
 
403
        (*__cuDeviceGetAttribute)(&cc.prop.maxGridSize[0], CU_DEVICE_ATTRIBUTE_MAX_GRID_DIM_X, device);
 
404
        (*__cuDeviceGetAttribute)(&cc.prop.maxGridSize[1], CU_DEVICE_ATTRIBUTE_MAX_GRID_DIM_Y, device);
 
405
        (*__cuDeviceGetAttribute)(&cc.prop.maxGridSize[2], CU_DEVICE_ATTRIBUTE_MAX_GRID_DIM_Z, device);
 
406
        (*__cuDeviceGetAttribute)(&cc.prop.clockRate, CU_DEVICE_ATTRIBUTE_CLOCK_RATE, device);
 
407
        (*__cuDeviceGetAttribute)(&cc.prop.totalConstMem, CU_DEVICE_ATTRIBUTE_TOTAL_CONSTANT_MEMORY, device);
 
408
        (*__cuDeviceGetAttribute)(&cc.prop.textureAlignment, CU_DEVICE_ATTRIBUTE_TEXTURE_ALIGNMENT, device);
 
409
        (*__cuDeviceGetAttribute)(&cc.prop.deviceOverlap, CU_DEVICE_ATTRIBUTE_GPU_OVERLAP, device);
 
410
        retval = (*__cuDeviceGetAttribute)(&cc.prop.multiProcessorCount, CU_DEVICE_ATTRIBUTE_MULTIPROCESSOR_COUNT, device);
 
411
        //retval = (*__cuDeviceGetProperties)(&cc.prop, device);
 
412
        if (cc.prop.major <= 0) continue;  // major == 0 means emulation
 
413
        if (cc.prop.major > 100) continue;  // e.g. 9999 is an error
 
414
#if defined(_WIN32) && !defined(SIM)
 
415
        cc.display_driver_version = Version.drvVersion;
 
416
#else
 
417
        cc.display_driver_version = 0;
 
418
#endif
 
419
        cc.cuda_version = cuda_version;
 
420
        cc.device_num = j;
 
421
        gpus.push_back(cc);
 
422
    }
 
423
 
 
424
    if (!gpus.size()) {
 
425
        warnings.push_back("No CUDA-capable NVIDIA GPUs found");
 
426
        return;
 
427
    }
 
428
 
 
429
    // identify the most capable non-ignored instance
 
430
    //
 
431
    COPROC_CUDA best;
 
432
    bool first = true;
 
433
    for (i=0; i<gpus.size(); i++) {
 
434
        if (in_vector(gpus[i].device_num, ignore_devs)) continue;
 
435
        if (first) {
 
436
            best = gpus[i];
 
437
            first = false;
 
438
        } else if (cuda_compare(gpus[i], best, false) > 0) {
 
439
            best = gpus[i];
 
440
        }
 
441
    }
 
442
 
 
443
    // see which other instances are equivalent,
 
444
    // and set the "count" and "device_nums" fields
 
445
    //
 
446
    best.count = 0;
 
447
    for (i=0; i<gpus.size(); i++) {
 
448
        char buf2[256];
 
449
        gpus[i].description(buf);
 
450
        if (in_vector(gpus[i].device_num, ignore_devs)) {
 
451
            sprintf(buf2, "NVIDIA GPU %d (ignored by config): %s", gpus[i].device_num, buf);
 
452
        } else if (use_all || !cuda_compare(gpus[i], best, true)) {
 
453
            best.device_nums[best.count] = gpus[i].device_num;
 
454
            best.count++;
 
455
            sprintf(buf2, "NVIDIA GPU %d: %s", gpus[i].device_num, buf);
 
456
        } else {
 
457
            sprintf(buf2, "NVIDIA GPU %d (not used): %s", gpus[i].device_num, buf);
 
458
        }
 
459
        descs.push_back(string(buf2));
 
460
    }
 
461
 
 
462
    if (best.count) {
 
463
        *this = best;
 
464
    }
 
465
}
 
466
 
 
467
// fake a NVIDIA GPU (for debugging)
 
468
//
 
469
void COPROC_CUDA::fake(int driver_version, double ram, int n) {
 
470
   strcpy(type, "CUDA");
 
471
   count = n;
 
472
   for (int i=0; i<count; i++) {
 
473
       device_nums[i] = i;
 
474
   }
 
475
   display_driver_version = driver_version;
 
476
   cuda_version = 2020;
 
477
   strcpy(prop.name, "Fake NVIDIA GPU");
 
478
   prop.totalGlobalMem = (unsigned int)ram;
 
479
   prop.sharedMemPerBlock = 100;
 
480
   prop.regsPerBlock = 8;
 
481
   prop.warpSize = 10;
 
482
   prop.memPitch = 10;
 
483
   prop.maxThreadsPerBlock = 20;
 
484
   prop.maxThreadsDim[0] = 2;
 
485
   prop.maxThreadsDim[1] = 2;
 
486
   prop.maxThreadsDim[2] = 2;
 
487
   prop.maxGridSize[0] = 10;
 
488
   prop.maxGridSize[1] = 10;
 
489
   prop.maxGridSize[2] = 10;
 
490
   prop.totalConstMem = 10;
 
491
   prop.major = 1;
 
492
   prop.minor = 2;
 
493
   prop.clockRate = 1250000;
 
494
   prop.textureAlignment = 1000;
 
495
   prop.multiProcessorCount = 14;
 
496
}
 
497
 
 
498
// See how much RAM is available on each GPU.
 
499
// If this fails, set "available_ram_unknown"
 
500
//
 
501
void COPROC_CUDA::get_available_ram() {
 
502
#ifdef MEASURE_AVAILABLE_RAM
 
503
    int device, i, retval;
 
504
    unsigned int memfree, memtotal;
 
505
    unsigned int ctx;
 
506
    
 
507
    // avoid crash if faked GPU
 
508
    //
 
509
    if (!__cuDeviceGet) {
 
510
        for (i=0; i<count; i++) {
 
511
            available_ram[i] = available_ram_fake[i];
 
512
            available_ram_unknown[i] = false;
 
513
        }
 
514
        return;
 
515
    }
 
516
    for (i=0; i<count; i++) {
 
517
        int devnum = device_nums[i];
 
518
        available_ram[i] = 0;
 
519
        available_ram_unknown[i] = true;
 
520
        retval = (*__cuDeviceGet)(&device, devnum);
 
521
        if (retval) {
 
522
            if (log_flags.coproc_debug) {
 
523
                msg_printf(0, MSG_INFO,
 
524
                    "[coproc] cuDeviceGet(%d) returned %d", devnum, retval
 
525
                );
 
526
            }
 
527
            continue;
 
528
        }
 
529
        retval = (*__cuCtxCreate)(&ctx, 0, device);
 
530
        if (retval) {
 
531
            if (log_flags.coproc_debug) {
 
532
                msg_printf(0, MSG_INFO,
 
533
                    "[coproc] cuCtxCreate(%d) returned %d", devnum, retval
 
534
                );
 
535
            }
 
536
            continue;
 
537
        }
 
538
        retval = (*__cuMemGetInfo)(&memfree, &memtotal);
 
539
        if (retval) {
 
540
            if (log_flags.coproc_debug) {
 
541
                msg_printf(0, MSG_INFO,
 
542
                    "[coproc] cuMemGetInfo(%d) returned %d", devnum, retval
 
543
                );
 
544
            }
 
545
            (*__cuCtxDestroy)(ctx);
 
546
            continue;
 
547
        }
 
548
        (*__cuCtxDestroy)(ctx);
 
549
        available_ram[i] = (double) memfree;
 
550
        available_ram_unknown[i] = false;
 
551
    }
 
552
#else
 
553
    for (int i=0; i<count; i++) {
 
554
        available_ram_unknown[i] = false;
 
555
        available_ram[i] = prop.totalGlobalMem;
 
556
    }
 
557
#endif
 
558
}
 
559
 
 
560
// check whether each GPU is running a graphics app (assume yes)
 
561
// return true if there's been a change since last time
 
562
//
 
563
bool COPROC_CUDA::check_running_graphics_app() {
 
564
    int retval, j;
 
565
    bool change = false;
 
566
    for (j=0; j<count; j++) {
 
567
        bool new_val = true;
 
568
        int device, kernel_timeout;
 
569
        retval = (*__cuDeviceGet)(&device, j);
 
570
        if (!retval) {
 
571
            retval = (*__cuDeviceGetAttribute)(&kernel_timeout, CU_DEVICE_ATTRIBUTE_KERNEL_EXEC_TIMEOUT, device);
 
572
            if (!retval && !kernel_timeout) {
 
573
                new_val = false;
 
574
            }
 
575
        }
 
576
        if (new_val != running_graphics_app[j]) {
 
577
            change = true;
 
578
        }
 
579
        running_graphics_app[j] = new_val;
 
580
    }
 
581
    return change;
 
582
}
 
583
 
 
584
////////////////// ATI STARTS HERE /////////////////
 
585
//
 
586
// Docs:
 
587
// http://developer.amd.com/gpu_assets/Stream_Computing_User_Guide.pdf
 
588
// ?? why don't they have HTML docs??
 
589
 
 
590
// criteria:
 
591
//
 
592
// - double precision support
 
593
// - local RAM
 
594
// - speed
 
595
//
 
596
int ati_compare(COPROC_ATI& c1, COPROC_ATI& c2, bool loose) {
 
597
    if (c1.attribs.doublePrecision && !c2.attribs.doublePrecision) return 1;
 
598
    if (!c1.attribs.doublePrecision && c2.attribs.doublePrecision) return -1;
 
599
    if (loose) {
 
600
        if (c1.attribs.localRAM> 1.4*c2.attribs.localRAM) return 1;
 
601
        if (c1.attribs.localRAM< .7* c2.attribs.localRAM) return -1;
 
602
        return 0;
 
603
    }
 
604
    if (c1.attribs.localRAM > c2.attribs.localRAM) return 1;
 
605
    if (c1.attribs.localRAM < c2.attribs.localRAM) return -1;
 
606
    double s1 = c1.peak_flops();
 
607
    double s2 = c2.peak_flops();
 
608
    if (s1 > s2) return 1;
 
609
    if (s1 < s2) return -1;
 
610
    return 0;
 
611
}
 
612
 
 
613
#ifdef _WIN32
 
614
typedef int (__stdcall *ATI_ATTRIBS) (CALdeviceattribs *attribs, CALuint ordinal);
 
615
typedef int (__stdcall *ATI_CLOSE)(void);
 
616
typedef int (__stdcall *ATI_GDC)(CALuint *numDevices);
 
617
typedef int (__stdcall *ATI_GDI)(void);
 
618
typedef int (__stdcall *ATI_INFO) (CALdeviceinfo *info, CALuint ordinal);
 
619
typedef int (__stdcall *ATI_VER) (CALuint *cal_major, CALuint *cal_minor, CALuint *cal_imp);
 
620
typedef int (__stdcall *ATI_STATUS) (CALdevicestatus*, CALdevice);
 
621
typedef int (__stdcall *ATI_DEVICEOPEN) (CALdevice*, CALuint);
 
622
typedef int (__stdcall *ATI_DEVICECLOSE) (CALdevice);
 
623
 
 
624
ATI_ATTRIBS __calDeviceGetAttribs = NULL;
 
625
ATI_CLOSE   __calShutdown = NULL;
 
626
ATI_GDC     __calDeviceGetCount = NULL;
 
627
ATI_GDI     __calInit = NULL;
 
628
ATI_INFO    __calDeviceGetInfo = NULL;
 
629
ATI_VER     __calGetVersion = NULL;
 
630
ATI_STATUS  __calDeviceGetStatus = NULL;
 
631
ATI_DEVICEOPEN  __calDeviceOpen = NULL;
 
632
ATI_DEVICECLOSE  __calDeviceClose = NULL;
 
633
 
 
634
#else
 
635
 
 
636
int (*__calInit)();
 
637
int (*__calGetVersion)(CALuint*, CALuint*, CALuint*);
 
638
int (*__calDeviceGetCount)(CALuint*);
 
639
int (*__calDeviceGetAttribs)(CALdeviceattribs*, CALuint);
 
640
int (*__calShutdown)();
 
641
int (*__calDeviceGetInfo)(CALdeviceinfo*, CALuint);
 
642
int (*__calDeviceGetStatus)(CALdevicestatus*, CALdevice);
 
643
int (*__calDeviceOpen)(CALdevice*, CALuint);
 
644
int (*__calDeviceClose)(CALdevice);
 
645
 
 
646
#endif
 
647
 
 
648
void COPROC_ATI::get(
 
649
    bool use_all,
 
650
    vector<string>& descs, vector<string>& warnings, vector<int>& ignore_devs
 
651
) {
 
652
    CALuint numDevices, cal_major, cal_minor, cal_imp;
 
653
    CALdevice device;
 
654
    CALdeviceinfo info;
 
655
    CALdeviceattribs attribs;
 
656
    char buf[256];
 
657
    bool amdrt_detected = false;
 
658
    bool atirt_detected = false;
 
659
    int retval;
 
660
    unsigned int i;
 
661
 
 
662
    attribs.struct_size = sizeof(CALdeviceattribs);
 
663
    device = 0;
 
664
    numDevices =0;
 
665
 
 
666
#ifdef _WIN32
 
667
 
 
668
#if defined _M_X64
 
669
    const char* atilib_name = "aticalrt64.dll";
 
670
    const char* amdlib_name = "amdcalrt64.dll";
 
671
#else
 
672
    const char* atilib_name = "aticalrt.dll";
 
673
    const char* amdlib_name = "amdcalrt.dll";
 
674
#endif
 
675
 
 
676
    HINSTANCE callib = LoadLibrary(atilib_name);
 
677
    if (callib) {
 
678
        atirt_detected = true;
 
679
    } else {
 
680
        callib = LoadLibrary(amdlib_name);
 
681
        if (callib) {
 
682
            amdrt_detected = true;
 
683
        }
 
684
    }
 
685
 
 
686
    if (!callib) {
 
687
        warnings.push_back("No ATI library found.");
 
688
        return;
 
689
    }
 
690
 
 
691
    __calInit = (ATI_GDI)GetProcAddress(callib, "calInit" );
 
692
    __calGetVersion = (ATI_VER)GetProcAddress(callib, "calGetVersion" );
 
693
    __calDeviceGetCount = (ATI_GDC)GetProcAddress(callib, "calDeviceGetCount" );
 
694
    __calDeviceGetAttribs =(ATI_ATTRIBS)GetProcAddress(callib, "calDeviceGetAttribs" );
 
695
    __calShutdown = (ATI_CLOSE)GetProcAddress(callib, "calShutdown" );
 
696
    __calDeviceGetInfo = (ATI_INFO)GetProcAddress(callib, "calDeviceGetInfo" );
 
697
    __calDeviceGetStatus = (ATI_STATUS)GetProcAddress(callib, "calDeviceGetStatus" );
 
698
    __calDeviceOpen = (ATI_DEVICEOPEN)GetProcAddress(callib, "calDeviceOpen" );
 
699
    __calDeviceClose = (ATI_DEVICECLOSE)GetProcAddress(callib, "calDeviceClose" );
 
700
 
 
701
#else
 
702
 
 
703
    void* callib;
 
704
 
 
705
    callib = dlopen("libaticalrt.so", RTLD_NOW);
 
706
    if (!callib) {
 
707
        warnings.push_back("No ATI library found");
 
708
        return;
 
709
    }
 
710
 
 
711
    atirt_detected = true;
 
712
 
 
713
    __calInit = (int(*)()) dlsym(callib, "calInit");
 
714
    __calGetVersion = (int(*)(CALuint*, CALuint*, CALuint*)) dlsym(callib, "calGetVersion");
 
715
    __calDeviceGetCount = (int(*)(CALuint*)) dlsym(callib, "calDeviceGetCount");
 
716
    __calDeviceGetAttribs = (int(*)(CALdeviceattribs*, CALuint)) dlsym(callib, "calDeviceGetAttribs");
 
717
    __calShutdown = (int(*)()) dlsym(callib, "calShutdown");
 
718
    __calDeviceGetInfo = (int(*)(CALdeviceinfo*, CALuint)) dlsym(callib, "calDeviceGetInfo");
 
719
    __calDeviceGetStatus = (int(*)(CALdevicestatus*, CALdevice)) dlsym(callib, "calDeviceGetStatus");
 
720
    __calDeviceOpen = (int(*)(CALdevice*, CALuint)) dlsym(callib, "calDeviceOpen");
 
721
    __calDeviceClose = (int(*)(CALdevice)) dlsym(callib, "calDeviceClose");
 
722
 
 
723
#endif
 
724
 
 
725
    if (!__calInit) {
 
726
        warnings.push_back("calInit() missing from CAL library");
 
727
        return;
 
728
    }
 
729
    if (!__calGetVersion) {
 
730
        warnings.push_back("calGetVersion() missing from CAL library");
 
731
        return;
 
732
    }
 
733
    if (!__calDeviceGetCount) {
 
734
        warnings.push_back("calDeviceGetCount() missing from CAL library");
 
735
        return;
 
736
    }
 
737
    if (!__calDeviceGetAttribs) {
 
738
        warnings.push_back("calDeviceGetAttribs() missing from CAL library");
 
739
        return;
 
740
    }
 
741
    if (!__calDeviceGetInfo) {
 
742
        warnings.push_back("calDeviceGetInfo() missing from CAL library");
 
743
        return;
 
744
    }
 
745
    if (!__calDeviceGetStatus) {
 
746
        warnings.push_back("calDeviceGetStatus() missing from CAL library");
 
747
        return;
 
748
    }
 
749
    if (!__calDeviceOpen) {
 
750
        warnings.push_back("calDeviceOpen() missing from CAL library");
 
751
        return;
 
752
    }
 
753
    if (!__calDeviceClose) {
 
754
        warnings.push_back("calDeviceClose() missing from CAL library");
 
755
        return;
 
756
    }
 
757
 
 
758
    retval = (*__calInit)();
 
759
    if (retval != CAL_RESULT_OK) {
 
760
        sprintf(buf, "calInit() returned %d", retval);
 
761
        warnings.push_back(buf);
 
762
        return;
 
763
    }
 
764
    retval = (*__calDeviceGetCount)(&numDevices);
 
765
    if (retval != CAL_RESULT_OK) {
 
766
        sprintf(buf, "calDeviceGetCount() returned %d", retval);
 
767
        warnings.push_back(buf);
 
768
        return;
 
769
    }
 
770
    retval = (*__calGetVersion)(&cal_major, &cal_minor, &cal_imp);
 
771
    if (retval != CAL_RESULT_OK) {
 
772
        sprintf(buf, "calGetVersion() returned %d", retval);
 
773
        warnings.push_back(buf);
 
774
        return;
 
775
    }
 
776
 
 
777
    if (!numDevices) {
 
778
        warnings.push_back("No usable CAL devices found");
 
779
        return;
 
780
    }
 
781
 
 
782
    COPROC_ATI cc, cc2;
 
783
    string s, gpu_name;
 
784
    vector<COPROC_ATI> gpus;
 
785
    for (CALuint i=0; i<numDevices; i++) {
 
786
        retval = (*__calDeviceGetInfo)(&info, i);
 
787
        if (retval != CAL_RESULT_OK) {
 
788
            sprintf(buf, "calDeviceGetInfo() returned %d", retval);
 
789
            warnings.push_back(buf);
 
790
            return;
 
791
        }
 
792
        retval = (*__calDeviceGetAttribs)(&attribs, i);
 
793
        if (retval != CAL_RESULT_OK) {
 
794
            sprintf(buf, "calDeviceGetAttribs() returned %d", retval);
 
795
            warnings.push_back(buf);
 
796
            return;
 
797
        }
 
798
        switch ((int)attribs.target) {
 
799
        case CAL_TARGET_600:
 
800
            gpu_name="ATI Radeon HD 2900 (RV600)";
 
801
            break;
 
802
        case CAL_TARGET_610:
 
803
            gpu_name="ATI Radeon HD 2300/2400/3200 (RV610)";
 
804
            attribs.numberOfSIMD=1;        // set correct values (reported wrong by driver)
 
805
            attribs.wavefrontSize=32;
 
806
            break;
 
807
        case CAL_TARGET_630:
 
808
            gpu_name="ATI Radeon HD 2600 (RV630)";
 
809
            // set correct values (reported wrong by driver)
 
810
            attribs.numberOfSIMD=3;
 
811
            attribs.wavefrontSize=32;
 
812
            break;
 
813
        case CAL_TARGET_670:
 
814
            gpu_name="ATI Radeon HD 3800 (RV670)";
 
815
            break;
 
816
        case CAL_TARGET_710:
 
817
            gpu_name="ATI Radeon HD 4350/4550 (R710)";
 
818
            break;
 
819
        case CAL_TARGET_730:
 
820
            gpu_name="ATI Radeon HD 4600 series (R730)";
 
821
            break;
 
822
        case CAL_TARGET_7XX:
 
823
            gpu_name="ATI Radeon (RV700 class)";
 
824
            break;
 
825
        case CAL_TARGET_770:
 
826
            gpu_name="ATI Radeon HD 4700/4800 (RV740/RV770)";
 
827
            break;
 
828
        case 8:
 
829
            gpu_name="ATI Radeon HD5800 series (Cypress)";
 
830
            break;
 
831
        case 9:
 
832
            gpu_name="ATI Radeon HD5700 series (Juniper)";
 
833
            break;
 
834
        case 10:
 
835
            gpu_name="ATI Radeon HD5x00 series (Redwood)";
 
836
            break;
 
837
        case 11:
 
838
            gpu_name="ATI Radeon HD5x00 series (Cedar)";
 
839
            break;
 
840
        default:
 
841
            gpu_name="ATI unknown";
 
842
            break;
 
843
        }
 
844
        cc.attribs = attribs;
 
845
        cc.info = info;
 
846
        strcpy(cc.name, gpu_name.c_str());
 
847
        sprintf(cc.version, "%d.%d.%d", cal_major, cal_minor, cal_imp);
 
848
        cc.amdrt_detected = amdrt_detected;
 
849
        cc.atirt_detected = atirt_detected;
 
850
        cc.device_num = i;
 
851
        gpus.push_back(cc);
 
852
    }
 
853
 
 
854
    // shut down, otherwise Lenovo won't be able to switch to low-power GPU
 
855
    //
 
856
    retval = (*__calShutdown)();
 
857
 
 
858
    if (!gpus.size()) {
 
859
        warnings.push_back("No ATI GPUs found");
 
860
        return;
 
861
    }
 
862
 
 
863
    COPROC_ATI best;
 
864
    bool first = true;
 
865
    for (i=0; i<gpus.size(); i++) {
 
866
        if (in_vector(gpus[i].device_num, ignore_devs)) continue;
 
867
        if (first) {
 
868
            best = gpus[i];
 
869
            first = false;
 
870
        } else if (ati_compare(gpus[i], best, false) > 0) {
 
871
            best = gpus[i];
 
872
        }
 
873
    }
 
874
 
 
875
    best.count = 0;
 
876
    for (i=0; i<gpus.size(); i++) {
 
877
        char buf[256], buf2[256];
 
878
        gpus[i].description(buf);
 
879
        if (in_vector(gpus[i].device_num, ignore_devs)) {
 
880
            sprintf(buf2, "ATI GPU %d (ignored by config): %s", gpus[i].device_num, buf);
 
881
        } else if (use_all || !ati_compare(gpus[i], best, true)) {
 
882
            best.device_nums[best.count] = gpus[i].device_num;
 
883
            best.count++;
 
884
            sprintf(buf2, "ATI GPU %d: %s", gpus[i].device_num, buf);
 
885
        } else {
 
886
            sprintf(buf2, "ATI GPU %d: (not used) %s", gpus[i].device_num, buf);
 
887
        }
 
888
        descs.push_back(string(buf2));
 
889
    }
 
890
 
 
891
    if (best.count) {
 
892
        *this = best;
 
893
    }
 
894
}
 
895
 
 
896
void COPROC_ATI::fake(double ram, int n) {
 
897
    strcpy(type, "ATI");
 
898
    strcpy(version, "1.4.3");
 
899
    strcpy(name, "foobar");
 
900
    count = n;
 
901
    memset(&attribs, 0, sizeof(attribs));
 
902
    memset(&info, 0, sizeof(info));
 
903
    attribs.localRAM = (int)(ram/MEGA);
 
904
    attribs.numberOfSIMD = 32;
 
905
    attribs.wavefrontSize = 32;
 
906
    attribs.engineClock = 50;
 
907
    for (int i=0; i<count; i++) {
 
908
        device_nums[i] = i;
 
909
    }
 
910
}
 
911
 
 
912
void COPROC_ATI::get_available_ram() {
 
913
#ifdef MEASURE_AVAILABLE_RAM
 
914
    CALdevicestatus st;
 
915
    CALdevice dev;
 
916
    int i, retval;
 
917
 
 
918
    st.struct_size = sizeof(CALdevicestatus);
 
919
 
 
920
    // avoid crash if faked GPU
 
921
    if (!__calInit) {
 
922
        for (i=0; i<count; i++) {
 
923
            available_ram[i] = available_ram_fake[i];
 
924
            available_ram_unknown[i] = false;
 
925
        }
 
926
        return;
 
927
    }
 
928
    for (i=0; i<count; i++) {
 
929
        available_ram[i] = 0;
 
930
        available_ram_unknown[i] = true;
 
931
    }
 
932
    retval = (*__calInit)();
 
933
    if (retval) {
 
934
        if (log_flags.coproc_debug) {
 
935
            msg_printf(0, MSG_INFO,
 
936
                "[coproc] calInit() returned %d", retval
 
937
            );
 
938
        }
 
939
        return;
 
940
    }
 
941
 
 
942
    for (i=0; i<count; i++) {
 
943
        int devnum = device_nums[i];
 
944
        retval = (*__calDeviceOpen)(&dev, devnum);
 
945
        if (retval) {
 
946
            if (log_flags.coproc_debug) {
 
947
                msg_printf(0, MSG_INFO,
 
948
                    "[coproc] calDeviceOpen(%d) returned %d", devnum, retval
 
949
                );
 
950
            }
 
951
            continue;
 
952
        }
 
953
        retval = (*__calDeviceGetStatus)(&st, dev);
 
954
        if (retval) {
 
955
            if (log_flags.coproc_debug) {
 
956
                msg_printf(0, MSG_INFO,
 
957
                    "[coproc] calDeviceGetStatus(%d) returned %d",
 
958
                    devnum, retval
 
959
                );
 
960
            }
 
961
            (*__calDeviceClose)(dev);
 
962
            continue;
 
963
        }
 
964
        available_ram[i] = st.availLocalRAM*MEGA;
 
965
        available_ram_unknown[i] = false;
 
966
        (*__calDeviceClose)(dev);
 
967
    }
 
968
    (*__calShutdown)();
 
969
#else
 
970
    for (int i=0; i<count; i++) {
 
971
        available_ram_unknown[i] = false;
 
972
        available_ram[i] = attribs.localRAM*MEGA;
 
973
    }
 
974
#endif
 
975
}