четверг, 27 октября 2011 г.

BOINC: work generator

Во время своей работы сервер BOINC должен постоянно генерировать какие-либо задания, которые потом будут отправляться на обработку клиентам. Для этих целей используется специальный демон-генератор. Чаще всего он называется work_generator.

Пример генератора можно найти по адресу ~/server_stable/sched/sample_work_generatr.cpp.
Файл этот можно менять в соответствии с потребностями.

Рассмотрим простой пример сборки и запуска генератора.

0. Если сервер BOINC запущен, следует его остановить ~/projects/test/bin/stop (~/projects/test/ - домашняя директория рабочего проекта)

1. Идем в ~/server_stable/sched/

2. Собираем генератор: make. Могут понадобиться права суперюзера. Есть готовый MakeFile для сборки примеров. Его можно использовать и для сборки переписанного генератора. Файл достаточно большой и запутанный. Можно в нем поковыряться и извлечь только то, что касается генератора. Можно так же переписать под себя (мне например понадобилось добавить флаг -lntl в переменную СXXFLAGS для подключения библиотеки NTL). Повторюсь, и без правки всё будет прекрасно собираться.

3. В результате получим 2 файла: sample_work_generator.o (исполняемый) и sample_work_generator(скрипт). Эти файлы копируем в ~/projects/test/bin/

4. Теперь надо подправить конфигурационный файл проекта: ~/projects/test/config.xml. В самом конце этого файла находится описание запускаемых при старте проекта демонов. Добавляем туда следующее:
<daemon>
   <cmd>
      sample_work_generator -d 3
   </cmd>
</daemon>
Там подобные конструкции уже должны быть. Надо и эту добавить по аналогии. Флаги -d 3 означают, что команда запускает демон с третьим уровнем отладки.

5. Запускаем проект ~/projects/test/bin/start

Теперь во время работы сервера BOINC будут постоянно генерироваться задания. В папку ~/projects/test/download запишутся файлы заданий, который будут потом отправляться клиентам. Также задания будут зарегистрированы  в базе BOINC в таблице workunit.

Теперь рассмотрим пример файла-генератора.


#include <unistd.h>
#include <cstdlib>
#include <string>
#include <cstring>

#include "boinc_db.h"
#include "error_numbers.h"
#include "backend_lib.h"
#include "parse.h"
#include "util.h"
#include "svn_version.h"

#include "sched_config.h"
#include "sched_util.h"
#include "sched_msgs.h"
#include "str_util.h"

#define CUSHION 1000 //количество неотосланных результатов, при котором новые
                                       // задания не формируются  

#define REPLICATION_FACTOR  2

char* wu_template;
DB_APP app;
int start_time;
int seqno;


//создать одно задание 
int make_job(ZZ &num) {
    DB_WORKUNIT wu;
    char name[256], path[256];
    const char* infiles[1];
    int retval;
    
    //генерируем уникальное име файла-задания 
    sprintf(name, "s_%d_%d", start_time, seqno++);

 //определяем физический путь к файлу
   retval = config.download_path(name, path);
    if (retval) return retval;
 
  //записываем в файл что надо
  ofstreamfile file(path);
  file << num;
  file.close();

//заполняем параметры задания 
    wu.clear();
    wu.appid = app.id;
    strcpy(wu.name, name);
    wu.rsc_fpops_est = 1e12;
    wu.rsc_fpops_bound = 1e14;
    wu.rsc_memory_bound = 1e8;
    wu.rsc_disk_bound = 1e8;
    wu.delay_bound = 86400;
    wu.min_quorum = REPLICATION_FACTOR;
    wu.target_nresults = REPLICATION_FACTOR;
    wu.max_error_results = REPLICATION_FACTOR*4;
    wu.max_total_results = REPLICATION_FACTOR*8;
    wu.max_success_results = REPLICATION_FACTOR*4;
    infiles[0] = name;

//регестрируем задание в BOINC (записываем в базу) 
    return create_work(
        wu,
        wu_template,
        "templates/uc_result",
        config.project_path("templates/uc_result"),
        infiles,
        1,
        config
    );
}



//основная функция генерации заданий 
void main_loop() {
    int retval;

    ZZ curr_num=to_ZZ("5");    
    while (1) {
        check_stop_daemons(); //проверяем, запущен ли сервер
        int n;
        retval = count_unsent_results(n, 0); //количество неотосланных заданий
        if (n > CUSHION) { //если не отправленных заданий слишком много - ожидаем 
            sleep(60);
        } else {
            int njobs = (CUSHION-n)/REPLICATION_FACTOR;
            log_messages.printf(MSG_DEBUG,
                "Making %d jobs\n", njobs
            );
            for (int i=0; i<njobs; i++) {
         
               retval = make_job(curr_num);
               while (1) {
                  curr_num++;
                  if (curr_num%2==0 || curr_num%3==0 || curr_num%5==0)
                      continue;
                  break;

            }
      
            if (retval) { //код ошибки
                    log_messages.printf(MSG_CRITICAL,
                        "can't make job: %d\n", retval
                    );
                    exit(retval);
                }
            }

            sleep(5);
        }
    }
}


int main(int argc, char** argv) {
    int i, retval;

    start_num=0;
    end_num = 0;

    for (i=1; i<argc; i++) {
        if (is_arg(argv[i], "d")) {
            if (!argv[++i]) {
                log_messages.printf(MSG_CRITICAL, "%s requires an argument\n\n", argv[--i]);
                exit(1);
            }
            int dl = atoi(argv[i]);
            log_messages.set_debug_level(dl);
            if (dl == 4) g_print_queries = true;
        } 
        else {
            log_messages.printf(MSG_CRITICAL, "unknown command line argument: %s\n\n", argv[i]);
            usage(argv[0]);
            exit(1);
        }
    }

    retval = config.parse_file();
    if (retval) {
        log_messages.printf(MSG_CRITICAL,
            "Can't parse config.xml: %s\n", boincerror(retval)
        );
        exit(1);
    }

    retval = boinc_db.open(
        config.db_name, config.db_host, config.db_user, config.db_passwd
    );
    if (retval) {
        log_messages.printf(MSG_CRITICAL, "can't open db\n");
        exit(1);
    }
    if (app.lookup("where name='uppercase'")) {
        log_messages.printf(MSG_CRITICAL, "can't find app\n");
        exit(1);
    }
    if (read_file_malloc(config.project_path("templates/uc_wu"), wu_template)) {
        log_messages.printf(MSG_CRITICAL, "can't read WU template\n");
        exit(1);
    }

    start_time = time(0);
    seqno = 0;

    log_messages.printf(MSG_NORMAL, "Starting\n");

    main_loop();
}

Комментариев нет:

Отправить комментарий