source: trunk/CrypPlugins/KeySearcher/ExternalClient/Cryptool.cpp @ 2486

Last change on this file since 2486 was 2486, checked in by schwittmann, 11 years ago

Improved external client:

  • added possibility to choose device
  • added verbose device information
  • added device name to identification
  • misc cleanup
File size: 13.7 KB
Line 
1#include <cstdio>
2#include <cstdlib>
3#include <iostream>
4#include <SDKFile.hpp>
5#include <SDKCommon.hpp>
6#include <SDKApplication.hpp>
7
8#ifdef _OPENMP
9#include <omp.h>
10#else
11#warning No OpenMP support. Expect performance impacts.
12#endif
13
14#define __NO_STD_STRING
15
16#include "Cryptool.h"
17
18unsigned long DiffMicSec(timeval & start, timeval & end)
19{
20    return (end.tv_sec - start.tv_sec)*1000000 + (end.tv_usec - start.tv_usec);
21}
22
23Cryptool::Cryptool()
24{
25    cl_int err;
26
27    kernel = 0;
28    platformChoice = -1;
29    deviceChoice = -1;
30
31    // Get platform information
32    std::cout<<"Getting platform"<<std::endl;
33    err = cl::Platform::get(&platforms);
34
35    if(err != CL_SUCCESS)
36    {
37        std::cerr << "Platform::get() failed (" << err << ")" << std::endl;
38        throw std::exception();
39    }
40    if (platforms.size() <= 0)
41    {
42        std::cerr << "No platforms available!" << std::endl;
43        throw std::exception();
44    }
45
46    //Iterate over all platforms to generate output for platform choosing by user
47    std::vector<cl::Platform>::iterator i;
48        std::cout << "Number of platforms: " << platforms.size() << std::endl << std::endl;
49        for(i = platforms.begin(); i != platforms.end(); ++i)
50        {
51                std::cout << "Platform Profile: " << (*i).getInfo<CL_PLATFORM_PROFILE>(&err).c_str() << std::endl;
52                std::cout << "Platform Version: " << (*i).getInfo<CL_PLATFORM_VERSION>(&err).c_str() << std::endl;
53                std::cout << "Platform Name: " << (*i).getInfo<CL_PLATFORM_NAME>(&err).c_str() << std::endl;
54                std::cout << "Platform Vendor: " << (*i).getInfo<CL_PLATFORM_VENDOR>(&err).c_str() << std::endl;
55                std::cout << std::endl;
56
57            if(err != CL_SUCCESS)
58            {
59                std::cerr << "Platform::getInfo() failed (" << err << ")" << std::endl;
60                throw std::exception();
61            }
62
63            //Get all devices for the current platform to show device information for each device
64                (*i).getDevices((cl_device_type)CL_DEVICE_TYPE_ALL, &devices);
65
66                std::cout << "There are " << devices.size() << " devices for this platform." <<std::endl;
67
68                for (std::vector<cl::Device>::iterator z = devices.begin(); z != devices.end(); ++z) {
69                        cl_device_type dtype = (*z).getInfo<CL_DEVICE_TYPE>();
70                        std::cout << "  Device Type: ";
71                        switch (dtype) {
72                                case CL_DEVICE_TYPE_ACCELERATOR:
73                                        std::cout << "CL_DEVICE_TYPE_ACCELERATOR" << std::endl;
74                                        break;
75                                case CL_DEVICE_TYPE_CPU:
76                                        std::cout << "CL_DEVICE_TYPE_CPU" << std::endl;
77                                        break;
78                                case CL_DEVICE_TYPE_DEFAULT:
79                                        std::cout << "CL_DEVICE_TYPE_DEFAULT" << std::endl;
80                                        break;
81                                case CL_DEVICE_TYPE_GPU:
82                                        std::cout << "CL_DEVICE_TYPE_GPU" << std::endl;
83                                        break;
84                                }
85
86            std::cout << "  Platform ID: " << (*z).getInfo<CL_DEVICE_PLATFORM>() << std::endl;
87            std::cout << "  Name: " << (*z).getInfo<CL_DEVICE_NAME>().c_str() << std::endl;
88            std::cout << "  Vendor: " << (*z).getInfo<CL_DEVICE_VENDOR>().c_str() << std::endl;
89            std::cout << "  Driver version: " << (*z).getInfo<CL_DRIVER_VERSION>().c_str() << std::endl;
90            std::cout << "  Is device available?  " << ((*z).getInfo<CL_DEVICE_AVAILABLE>() ? "Yes" : "No") << std::endl;
91            std::cout << "  Device ID: " << (*z).getInfo<CL_DEVICE_VENDOR_ID>() << std::endl;
92            std::cout << "  Max clock frequency: " << (*z).getInfo<CL_DEVICE_MAX_CLOCK_FREQUENCY>() << "Mhz" << std::endl;
93            std::cout << "  Local memory size: " << (*z).getInfo<CL_DEVICE_LOCAL_MEM_SIZE>() << std::endl;
94            std::cout << "  Global memory size: " << (*z).getInfo<CL_DEVICE_GLOBAL_MEM_SIZE>() << std::endl;
95            std::cout << "  Max memory allocation: " << (*z).getInfo<CL_DEVICE_MAX_MEM_ALLOC_SIZE>() << std::endl;
96            std::cout << "  Cache size: " << (*z).getInfo<CL_DEVICE_GLOBAL_MEM_CACHE_SIZE>() << std::endl;
97            std::cout << "  Extensions: " << (*z).getInfo<CL_DEVICE_EXTENSIONS>().c_str() << std::endl;
98            std::cout << "  Execution capabilities: " << std::endl;
99            std::cout << "    Execute OpenCL kernels: " << ((*z).getInfo<CL_DEVICE_EXECUTION_CAPABILITIES>() & CL_EXEC_KERNEL ? "Yes" : "No") << std::endl;
100            std::cout << "    Execute native function: " << ((*z).getInfo<CL_DEVICE_EXECUTION_CAPABILITIES>() & CL_EXEC_NATIVE_KERNEL ? "Yes" : "No") << std::endl;
101
102                        std::cout << std::endl;
103                }
104        }
105
106        if(platforms.size() > 1) {
107                while(platformChoice<0 || platformChoice>=(int)platforms.size()) {
108                        std::cout << "Choose your platform!" << std::endl;
109                        std::cin >> platformChoice;
110                }
111        } else {
112                std::cout << "Choosing only available platform." << std::endl;
113                platformChoice=0;
114        }
115
116        if(devices.size() > 1) {
117                while(deviceChoice<0 || deviceChoice>=(int)devices.size()) {
118                        std::cout << "Choose the device to calculate on!" << std::endl;
119                        std::cin >> deviceChoice;
120                }
121        } else {
122                std::cout << "Choosing only available device." << std::endl;
123                deviceChoice=0;
124        }
125
126
127    /*
128     * If we could find our platform, use it. Otherwise pass a NULL and get whatever the
129     * implementation thinks we should be using.
130     */
131
132    cl_context_properties cps[3] = { CL_CONTEXT_PLATFORM, (cl_context_properties)platforms.at(platformChoice)(), 0 };
133
134    std::cout<<"Creating CL context" << std::endl;
135    context = new cl::Context(CL_DEVICE_TYPE_ALL, cps, NULL, NULL, &err);
136
137    if (err != CL_SUCCESS) {
138        std::cerr << "Context::Context() failed (" << err << ")\n";
139        throw std::exception();
140    }
141   
142    // results
143    costs = cl::Buffer(*context, CL_MEM_WRITE_ONLY, sizeof(float)*subbatch, NULL, &err);
144
145    if(err != CL_SUCCESS)
146    {
147        std::cerr << "Failed allocate to costsbuffer(" << err << ")\n";
148        throw std::exception();
149    }
150   
151    localCosts = new float[subbatch];
152
153    gettimeofday(&lastSubbatchCompleted, NULL);
154
155    // required for thousand/million separator in printf
156    setlocale(LC_ALL,"");
157}
158
159void Cryptool::buildKernel(const Job& j)
160{
161    if (j.Src == "")
162    {
163        if (kernel != 0)
164            return;
165        else
166        {
167            std::cout << "Source transmission failure!" << std::endl;
168            throw std::exception();
169        }
170    }
171
172    cl_int err;
173
174    std::cout<<"Compiling CL source\n";
175    cl::Program::Sources sources(1, std::make_pair(j.Src.c_str(), j.Src.length()));
176
177    cl::Program program = cl::Program(*context, sources, &err);
178    if (err != CL_SUCCESS) {
179        std::cerr << "Program::Program() failed (" << err << ")\n";
180        throw std::exception();
181    }
182
183    err = program.build(devices);
184    if (err != CL_SUCCESS) {
185
186                if(err == CL_BUILD_PROGRAM_FAILURE)
187                        {
188                                cl::string str = program.getBuildInfo<CL_PROGRAM_BUILD_LOG>(devices[0]);
189
190                                std::cout << " \n\t\t\tBUILD LOG\n";
191                                std::cout << " ************************************************\n";
192                                std::cout << str.c_str() << std::endl;
193                                std::cout << " ************************************************\n";
194                        }
195
196                //If there is an -33 error thrown here, you are most commonly trying to run an unsupported OpenCL version
197        std::cerr << "Program::build() failed (" << err << ")\n";
198        throw std::exception();
199    }
200
201    if (kernel != 0)
202        delete kernel;
203
204    kernel = new cl::Kernel(program, "bruteforceKernel", &err);
205    if (err != CL_SUCCESS) {
206        std::cerr << "Kernel::Kernel() failed (" << err << ")\n";
207        throw std::exception();
208    }
209}
210
211JobResult Cryptool::doOpenCLJob(const Job& j)
212{
213    res.Guid = j.Guid;
214    cl_int err;
215
216    buildKernel(j);
217
218    std::cout << "Using device named: " << getDeviceName() << " to calculate." << std::endl;
219    //Use the chosen device to calculate on!
220    cl::CommandQueue queue(*context, devices[deviceChoice], 0, &err);
221    if (err != CL_SUCCESS) {
222        std::cerr << "CommandQueue::CommandQueue() failed (" << err << ")\n";
223        throw std::exception();
224    }
225
226    // key
227    cl::Buffer keybuffer = cl::Buffer(*context, CL_MEM_READ_ONLY, j.KeySize*sizeof(float), NULL, &err);
228    if(err != CL_SUCCESS)
229    {
230        std::cerr << "Failed to allocate keybuffer(" << err << ")\n";
231        throw std::exception();
232    }
233
234    err = queue.enqueueWriteBuffer(keybuffer, 1, 0, j.KeySize*sizeof(float), j.Key);
235    if(err != CL_SUCCESS)
236    {
237        std::cerr << "Failed write to keybuffer(" << err << ")\n";
238        throw std::exception();
239    }
240
241    this->compareLargerThan = j.LargerThen;
242    this->resultSize = j.ResultSize;
243    res.ResultList.resize(j.ResultSize);
244    initTop(res.ResultList, j.LargerThen);
245
246    //execute:
247    std::cout<<"Running CL program with " << j.Size << " calculations!\n";
248    enqueueKernel(queue, j.Size, keybuffer, costs, j);
249
250    std::cout<<"Done!\n";
251
252    return res;
253}
254
255void Cryptool::enqueueSubbatch(cl::CommandQueue& queue, cl::Buffer& keybuffer, cl::Buffer& costs, int add, int length, const Job& j)
256{
257    timeval openCLStart;
258    gettimeofday(&openCLStart, NULL);
259        cl_int err;
260
261        err = kernel->setArg(0, keybuffer);
262        if (err != CL_SUCCESS) {
263                std::cerr << "Kernel::setArg() failed (" << err << ")\n";
264                throw std::exception();
265        }
266
267        err = kernel->setArg(1, costs);
268        if (err != CL_SUCCESS) {
269                std::cerr << "Kernel::setArg() failed (" << err << ")\n";
270                throw std::exception();
271        }
272
273        err = kernel->setArg(2, add);
274        if (err != CL_SUCCESS) {
275                std::cerr << "Kernel::setArg() failed (" << err << ")\n";
276                throw std::exception();
277        }
278
279        err = queue.enqueueNDRangeKernel(*kernel, cl::NullRange, cl::NDRange(256, 256, 256), cl::NullRange);
280
281        if (err != CL_SUCCESS) {
282                std::cerr << "CommandQueue::enqueueNDRangeKernel()" \
283                    " failed (" << err << ")\n";
284                throw std::exception();
285        }
286
287        err = queue.finish();
288        if (err != CL_SUCCESS) {
289                std::cerr << "Event::wait() failed (" << err << ")\n";
290                throw std::exception();
291        }
292
293        queue.enqueueReadBuffer(costs, 1, 0, sizeof(float)*length, localCosts);
294        err = queue.finish();
295        if (err != CL_SUCCESS) {
296                std::cerr << "Event::wait() failed (" << err << ")\n";
297                throw std::exception();
298        }
299
300    timeval openCLEnd;
301    gettimeofday(&openCLEnd, NULL);
302#ifdef _OPENMP
303#pragma omp parallel
304    {
305        std::list<std::pair<float, int> > localtop;
306        int eachChunk = length/omp_get_num_threads();
307        int from = omp_get_thread_num()*eachChunk;
308        int to = from + eachChunk;
309        if(omp_get_thread_num() == omp_get_num_threads()-1)
310        {
311            to = length;
312        }
313        for(int i=from; i<to; ++i)
314        {
315            std::list<std::pair<float, int> >::iterator it = isInTop(localtop, localCosts[i], j.LargerThen);
316            if (it != localtop.end() || it == localtop.begin())
317                pushInTop(localtop, it, localCosts[i], i+add);
318        }
319        // merge it
320#pragma omp critical
321        {
322            std::list<std::pair<float, int> >::iterator itr;
323            for(itr = localtop.begin(); itr != localtop.end(); ++itr)
324            {
325                std::list<std::pair<float, int> >::iterator posInGlobalList = isInTop(res.ResultList, itr->first, j.LargerThen);
326                if (posInGlobalList != res.ResultList.end())
327                    pushInTop(res.ResultList, posInGlobalList, itr->first, itr->second);
328            }
329        }
330    }
331#else
332        //check results:
333        for(int i=0; i<length; ++i)
334        {
335                //std::cout << localCosts[i] << std::endl;
336                std::list<std::pair<float, int> >::iterator it = isInTop(res.ResultList, localCosts[i], j.LargerThen);
337                if (it != res.ResultList.end())
338                        pushInTop(res.ResultList, it, localCosts[i], i+add);
339        }
340#endif
341
342    timeval finishedSubbatch;
343    gettimeofday(&finishedSubbatch, NULL);
344
345    unsigned long totalMic= DiffMicSec(openCLStart, finishedSubbatch);
346
347    printf("Completed a subbatch in %.3f seconds. %.2f%% spent on OpenCL, %.2f%% on sorting.\n",
348            (float)totalMic/1000000, DiffMicSec(openCLStart, openCLEnd)/(float)totalMic*100, DiffMicSec(openCLEnd, finishedSubbatch)/(float)totalMic*100);
349
350}
351
352void Cryptool::enqueueKernel(cl::CommandQueue& queue, int size, cl::Buffer& keybuffer, cl::Buffer& costs, const Job& j)
353{
354    for (int i = 0; i < (size/subbatch); i++)
355    {
356        enqueueSubbatch(queue, keybuffer, costs, i*subbatch, subbatch, j);
357
358        timeval now;
359        gettimeofday(&now, NULL);
360        unsigned long timeDiffMicroSec = (now.tv_sec - lastSubbatchCompleted.tv_sec)*1000000 + (now.tv_usec - lastSubbatchCompleted.tv_usec);
361        lastSubbatchCompleted = now;
362        float keysPerSecond = subbatch/((float)timeDiffMicroSec/1000000);
363        printf("% .2f%% done. %'u keys/sec %u seconds remaining\n", ((i+1)*subbatch)/(float)size*100, (unsigned int)keysPerSecond,
364                (unsigned int)((float)(size-(subbatch*(i+1)))/keysPerSecond));
365    }
366
367    int remain = (size%subbatch);
368    if (remain != 0)
369    {
370        enqueueSubbatch(queue, keybuffer, costs, size-remain, remain, j);
371    }
372}
373
374void Cryptool::pushInTop(std::list<std::pair<float, int> >& top, std::list<std::pair<float, int> >::iterator it, float val, int k) {
375        top.insert(it, std::pair<float, int>(val, k));
376    if(top.size() > this->resultSize)
377        top.pop_back();
378}
379
380std::list<std::pair<float, int> >::iterator Cryptool::isInTop(std::list<std::pair<float, int> >& top, float val, bool LargerThen) {
381    if (top.size() == 0)
382        return top.begin();
383
384        if (LargerThen)
385        {
386                if(top.size() > 0 && val <= top.rbegin()->first)
387                        return top.end();
388                for (std::list<std::pair<float, int> >::iterator k = top.begin(); k != top.end(); k++)
389                        if (val > k->first)
390                                return k;
391        }
392        else
393        {
394                if(top.size() > 0 && val >= top.rbegin()->first)
395                        return top.end();
396                for (std::list<std::pair<float, int> >::iterator k = top.begin(); k != top.end(); k++)
397                        if (val < k->first)
398                                return k;
399        }
400
401        return top.end();
402}
403
404void Cryptool::initTop(std::list<std::pair<float, int> >& top, bool LargerThen) {
405        for (std::list<std::pair<float, int> >::iterator k = top.begin(); k != top.end(); k++)
406        {
407            if (LargerThen)
408                k->first = -1000000.0;
409            else
410                k->first = 1000000.0;
411        }
412}
413
414std::string Cryptool::getDeviceName()
415{
416    return std::string(devices[deviceChoice].getInfo<CL_DEVICE_NAME>().c_str());
417}
Note: See TracBrowser for help on using the repository browser.