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
RevLine 
[2205]1#include <cstdio>
2#include <cstdlib>
3#include <iostream>
4#include <SDKFile.hpp>
5#include <SDKCommon.hpp>
6#include <SDKApplication.hpp>
7
[2230]8#ifdef _OPENMP
9#include <omp.h>
10#else
11#warning No OpenMP support. Expect performance impacts.
12#endif
13
[2205]14#define __NO_STD_STRING
15
[2207]16#include "Cryptool.h"
[2205]17
[2258]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
[2207]23Cryptool::Cryptool()
[2205]24{
25    cl_int err;
26
[2207]27    kernel = 0;
[2486]28    platformChoice = -1;
29    deviceChoice = -1;
[2207]30
[2486]31    // Get platform information
32    std::cout<<"Getting platform"<<std::endl;
[2205]33    err = cl::Platform::get(&platforms);
[2486]34
[2205]35    if(err != CL_SUCCESS)
36    {
37        std::cerr << "Platform::get() failed (" << err << ")" << std::endl;
[2207]38        throw std::exception();
[2205]39    }
[2486]40    if (platforms.size() <= 0)
[2212]41    {
42        std::cerr << "No platforms available!" << std::endl;
43        throw std::exception();
44    }
[2205]45
[2486]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;
[2205]56
[2486]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
[2205]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
[2486]132    cl_context_properties cps[3] = { CL_CONTEXT_PLATFORM, (cl_context_properties)platforms.at(platformChoice)(), 0 };
[2205]133
[2486]134    std::cout<<"Creating CL context" << std::endl;
135    context = new cl::Context(CL_DEVICE_TYPE_ALL, cps, NULL, NULL, &err);
136
[2205]137    if (err != CL_SUCCESS) {
138        std::cerr << "Context::Context() failed (" << err << ")\n";
[2207]139        throw std::exception();
[2205]140    }
[2486]141   
[2215]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";
[2259]148        throw std::exception();
[2215]149    }
150   
151    localCosts = new float[subbatch];
[2256]152
[2258]153    gettimeofday(&lastSubbatchCompleted, NULL);
154
[2256]155    // required for thousand/million separator in printf
156    setlocale(LC_ALL,"");
[2207]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;
[2259]168            throw std::exception();
[2207]169        }
170    }
171
172    cl_int err;
173
[2486]174    std::cout<<"Compiling CL source\n";
[2207]175    cl::Program::Sources sources(1, std::make_pair(j.Src.c_str(), j.Src.length()));
[2205]176
[2207]177    cl::Program program = cl::Program(*context, sources, &err);
178    if (err != CL_SUCCESS) {
[2205]179        std::cerr << "Program::Program() failed (" << err << ")\n";
[2259]180        throw std::exception();
[2205]181    }
182
183    err = program.build(devices);
184    if (err != CL_SUCCESS) {
185
[2486]186                if(err == CL_BUILD_PROGRAM_FAILURE)
187                        {
188                                cl::string str = program.getBuildInfo<CL_PROGRAM_BUILD_LOG>(devices[0]);
[2205]189
[2486]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                        }
[2205]195
[2486]196                //If there is an -33 error thrown here, you are most commonly trying to run an unsupported OpenCL version
[2205]197        std::cerr << "Program::build() failed (" << err << ")\n";
[2259]198        throw std::exception();
[2205]199    }
200
[2207]201    if (kernel != 0)
202        delete kernel;
203
204    kernel = new cl::Kernel(program, "bruteforceKernel", &err);
[2205]205    if (err != CL_SUCCESS) {
206        std::cerr << "Kernel::Kernel() failed (" << err << ")\n";
[2259]207        throw std::exception();
[2205]208    }
[2207]209}
[2205]210
[2207]211JobResult Cryptool::doOpenCLJob(const Job& j)
212{
[2258]213    res.Guid = j.Guid;
[2207]214    cl_int err;
215
216    buildKernel(j);
217
[2486]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);
[2205]221    if (err != CL_SUCCESS) {
222        std::cerr << "CommandQueue::CommandQueue() failed (" << err << ")\n";
[2259]223        throw std::exception();
[2205]224    }
225
226    // key
[2212]227    cl::Buffer keybuffer = cl::Buffer(*context, CL_MEM_READ_ONLY, j.KeySize*sizeof(float), NULL, &err);
[2205]228    if(err != CL_SUCCESS)
229    {
230        std::cerr << "Failed to allocate keybuffer(" << err << ")\n";
[2259]231        throw std::exception();
[2205]232    }
233
[2212]234    err = queue.enqueueWriteBuffer(keybuffer, 1, 0, j.KeySize*sizeof(float), j.Key);
[2205]235    if(err != CL_SUCCESS)
236    {
237        std::cerr << "Failed write to keybuffer(" << err << ")\n";
[2259]238        throw std::exception();
[2205]239    }
240
[2230]241    this->compareLargerThan = j.LargerThen;
242    this->resultSize = j.ResultSize;
[2215]243    res.ResultList.resize(j.ResultSize);
244    initTop(res.ResultList, j.LargerThen);
[2205]245
[2207]246    //execute:
[2215]247    std::cout<<"Running CL program with " << j.Size << " calculations!\n";
248    enqueueKernel(queue, j.Size, keybuffer, costs, j);
[2205]249
[2215]250    std::cout<<"Done!\n";
[2205]251
[2207]252    return res;
[2205]253}
[2207]254
[2215]255void Cryptool::enqueueSubbatch(cl::CommandQueue& queue, cl::Buffer& keybuffer, cl::Buffer& costs, int add, int length, const Job& j)
[2212]256{
[2258]257    timeval openCLStart;
258    gettimeofday(&openCLStart, NULL);
[2212]259        cl_int err;
[2207]260
[2212]261        err = kernel->setArg(0, keybuffer);
262        if (err != CL_SUCCESS) {
263                std::cerr << "Kernel::setArg() failed (" << err << ")\n";
[2259]264                throw std::exception();
[2212]265        }
266
267        err = kernel->setArg(1, costs);
268        if (err != CL_SUCCESS) {
269                std::cerr << "Kernel::setArg() failed (" << err << ")\n";
[2259]270                throw std::exception();
[2212]271        }
272
[2215]273        err = kernel->setArg(2, add);
[2212]274        if (err != CL_SUCCESS) {
275                std::cerr << "Kernel::setArg() failed (" << err << ")\n";
[2259]276                throw std::exception();
[2212]277        }
278
[2221]279        err = queue.enqueueNDRangeKernel(*kernel, cl::NullRange, cl::NDRange(256, 256, 256), cl::NullRange);
[2212]280
281        if (err != CL_SUCCESS) {
282                std::cerr << "CommandQueue::enqueueNDRangeKernel()" \
283                    " failed (" << err << ")\n";
[2259]284                throw std::exception();
[2212]285        }
286
287        err = queue.finish();
288        if (err != CL_SUCCESS) {
289                std::cerr << "Event::wait() failed (" << err << ")\n";
[2259]290                throw std::exception();
[2212]291        }
[2215]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";
[2259]297                throw std::exception();
[2215]298        }
[2258]299
300    timeval openCLEnd;
301    gettimeofday(&openCLEnd, NULL);
[2230]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
[2215]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        }
[2230]340#endif
[2258]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
[2212]350}
351
[2215]352void Cryptool::enqueueKernel(cl::CommandQueue& queue, int size, cl::Buffer& keybuffer, cl::Buffer& costs, const Job& j)
[2212]353{
354    for (int i = 0; i < (size/subbatch); i++)
355    {
[2256]356        enqueueSubbatch(queue, keybuffer, costs, i*subbatch, subbatch, j);
357
[2258]358        timeval now;
359        gettimeofday(&now, NULL);
360        unsigned long timeDiffMicroSec = (now.tv_sec - lastSubbatchCompleted.tv_sec)*1000000 + (now.tv_usec - lastSubbatchCompleted.tv_usec);
[2256]361        lastSubbatchCompleted = now;
[2411]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));
[2212]365    }
366
367    int remain = (size%subbatch);
368    if (remain != 0)
369    {
[2256]370        enqueueSubbatch(queue, keybuffer, costs, size-remain, remain, j);
[2212]371    }
372}
373
[2207]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));
[2230]376    if(top.size() > this->resultSize)
377        top.pop_back();
[2207]378}
379
380std::list<std::pair<float, int> >::iterator Cryptool::isInTop(std::list<std::pair<float, int> >& top, float val, bool LargerThen) {
[2230]381    if (top.size() == 0)
382        return top.begin();
383
384        if (LargerThen)
[2207]385        {
[2230]386                if(top.size() > 0 && val <= top.rbegin()->first)
387                        return top.end();
[2207]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        {
[2230]394                if(top.size() > 0 && val >= top.rbegin()->first)
395                        return top.end();
[2207]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}
[2486]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.