FAQ - maximální počet CPU jader pro jednotlivé projekty

Sekce pro všeobecné rady a návody z oblasti DC projektů
Zamčeno
Zpráva
Autor
Uživatelský avatar
forest
Admin webu a fóra CNT
Admin webu a fóra CNT
Příspěvky: 19635
Registrován: pát 27 říj, 2006 10:19
rok narození: 03 bře 1977
ID CNT statistics: 71
Bydliště: Újezd u Brna

FAQ - maximální počet CPU jader pro jednotlivé projekty

#1 Příspěvek od forest »

S příchodem a postupným rozšiřováním procesorů s mnoha jádry (vlákny), stoupají požadavky uživatelů na podrobnější nastavení výpočtů. Zde si ukážeme, jak lze snadno řídit kolik CPU jader (vláken) si má vzít pro sebe který z (pod)projektů. Základem tohoto nastavení je soubor app_config.xml, který je třeba si vytvořit a uložit do adresáře konkrétního projektu. Na týmovém Golemovi to na projektu Rosetta vypadá například takto:
<app_config>
<app>
<project_max_concurrent>14</project_max_concurrent>
<name>minirosetta</name>
<max_concurrent>14</max_concurrent>
<cpu_versions>
<cpu_usage>1</cpu_usage>
</cpu_versions>
</app>
</app_config>
Nejdůležitější jsou tyto dva řádky:
1. <project_max_concurrent>xx</project_max_concurrent>
2. <max_concurrent>yy</max_concurrent>

Položka č.1. se píše na začátek editace app_config.xml a říká BM, že pro daný projekt jako celek nemá spustit více než xx jednotek. Pokud chcete spustit 14 jednotek daného projektu xx=14.

Pokud počítáte více podprojektů naráz a chcete jim přerozdělit počet vláken na každý podprojekt, pak musíte editovat další řádky v app_config.xml a vložit ke každému podprojektu řádek s položkou č.2. Tudíž dejme tomu lze nastavit aby v projektu WCG jelo počítání podprojektu AIDS yy=10, další řádek s položkou číslo 2. TBC yy=8 a Ebola yy=14 . Tím je součet 32 vláken CPU přerozdělen mezi tři podprojekty. Pokud máte nastaveno na účtu i počítáni beta jednotek a v app_config.xml je nemáte zapsány jako podprojekt, tak BM ví, že může spustit až xx=32 jednotek celkem. Máte to tedy pojištěné právě tou prvni položkou na začátku skriptu app_config.xml.

Takže shrnuto položka č.1 je v app_configu.xml pouze jednou a to na začátku skriptu, a položka č.2 může být v takovém množství, podle toho kolik podprojektů hodláte počítat a kolik vám to dovoluje množství vláken CPU. Položka xx je vždy nadřazena, i kdyby součet yy všech podprojektů v daném projektu byl vyšší než xx.

Aby bylo nastavení ještě dokonalejší, tak můžete takovýto soubor app_config.xml vložit do adresáře každého projektu který počítáte a nastavit si jej dle vlastních potřeb. Tedy například aby na 32 jádrovém Golemu jelo vždy max 10 jednotek Asteroids, 10 jednotek WCG, 6 jednotek Rosetty a 6 jednotek PrimeGrid.

EDIT by nenym

Může se stát, resp. zákonitě se musí stát, že při záměru provozovat dva a více projektů paralelně s nastaveným parametrem <max_concurrent> či <project_max_concurrent> natáhne jeden projekt full buffer a další si o práci neřeknou. Pak je v provozu 1/2, 1/3 ... 1/4 jader, protože <project_max_concurrent>n</project_max_concurrent>, kde n= 1/2, 1/3 ... 1/4 počtu jader. Existuje na to finta, kterou provozuji dlouhá léta a začal jsem ji aplikovat nejdříve při lovení exotů. Jde o to udržet boinc core stále hladové.
Jedná se o známý vkliberův script pro ponechání určitého počtu aktivních jednotek daného projektu, ostatní jsou suspendované. Script nerozlišuje podprojekty/aplikace, pozastavuje v pořadí došlých úloh - což nemusí být nutně totéž, jako pořadí DL. Na projektech s dynamickým přidělováním DL podle FLOPSů tasku tedy velmi opatrně s velikostí zásoby - typicky CSG Wildlife a Seti. Scripty se spouští s více či méně rozumným odstupem schedulerem.
Např. pro kombinaci ODLK + ODLK1 spouštím scripty s odstupem 1,5 minuty a frekvencí 3 minuty, protože jde o krátké úlohy. Pro kombinaci Acoustics + Asteroids mám odstup 5 minut, frekvenci 10 minut. Počet uvolněných jednotek mám o 3 větší, než je max_concurrent u úloh pod 30 minut, u ostatních stačí o 2. U velmi krátkých (GoofyxGridCPU, SRBase shorty) mám nastaven dvojnásobek, odstup 1 minuta, frekvence 2 minuty. IMHO není potřeba zvláštního mozkového úsilí k extrapolaci návodu na libovolný smysluplný počet projektů.

Jest otázkou, zda je pak nutné a praktické za dávat parametry (u všech projektů), které jsou předmětem tohoto postu. Záleží na poměru RS projektů, zamýšleného hlavního a vedlejšího efektu a vkusu každého soudruha. Já typicky stanovuji u jednoho projektu a u zbytku nikoli, nějaxe to samo poskládá při správné volbě poměru RS a parametru <rec_half_life_days>0.100000</rec_half_life_days> v cc_config.xml. Jak vidíte, používám 2,5 hodiny, což nutí core k rozložení práce podle RS kontinuálně, defaultní hodnota 10 dnů vede k velkým výkyvům a zdánlivému nerespektování poměrů RS.

Pro jistotu zopakuji script, ať to máte pohromadě.

Kód: Vybrat vše

/*!
    Autor : Vit Kliber, Czech National Team o.s.
Vytvoreno : 11.01.2012
    Nazev : boinc_manage_wcg_tasks.js
    Zdroj : boinc_framework.js
    Popis : Skript vychazi z Kostry skriptu pro budouci skripy pro manipulaci s jednotkami
            ( url=http://forum.czechnationalteam.cz/viewtopic.php?p=53537#p53537 )
            Tento provadi manipulaci s jednotkami projektu WCG ( http://www.worldcommunitygrid.org/ )
            Pozadavek je nasledujici : nechat pocitat maximalne 8 <pocet_aktivnich_jednotek> jednotky
            a ostatni pozastavit
*/

// adresa projektu (nejpravdepodobneji bude skript manipulovat s jednotkami jednoho projektu)
var projekt = "http://www.worldcommunitygrid.org/";

// pocet maximalne aktivnich jednotek
var pocet_aktivnich_jednotek = 8;

// verze boinc, je zjistena funkci verze(), pokud je prazdne nepodarilo se zjistit a skript se ukonci
var boinc_verze = "";

// prikaz pro zjisteni aktualnich jednotek v BOINC
var boinc_jednotky = "";

// prikaz pro manipulaci s jednotkou
var boinc_jednotka = "";

// 0 = Debug ON, 1 = Debug OFF ... jestli se maji vypisovat pri behu hlaseni 
var debug_code = 1;
// corrected by nenym: co bylo puvodně uvedeno bylo logicke, ale naopak. Nyni je to patologicke, ale správne. Do kodu nepolezu.

// pro vypsani ladiciho textu pri behu skriptu
function alert(text)
{
  if (debug_code != 1)
  {
    return (new ActiveXObject("WScript.Shell")).Popup(text, 0, WScript.ScriptName, 64);
  }
}

// zjisti verzi BOINC
function verze()
{
  // definice noveho objektu WScript.Shell (umozni mi spoustet externi prikazy)
  var objekt_shell = new ActiveXObject("WScript.Shell");
  // definice metody Exec, spusti prikaz a pointer uchova v promene <objekt_exec>
  var objekt_exec  = objekt_shell.Exec("boinccmd.exe --version");
  // textova promena do ktere se ulozi cely obsah standardniho vystupu predchazejiciho prikazu
  var verze_text   = objekt_exec.StdOut.ReadAll();
  alert(verze_text);
  // promena <verze_text> by mela nyni obsahovat jeden radek s timto obsahem (podle verze) : "boinccmd,  built from BOINC 6.12.33"
  // uchovam pozici textu BOINC z promene <verze_text>
  var pozice = verze_text.indexOf("BOINC");
  // jestlize je ruzne od -1 tak v obsahu <verze_text> je na pozici <pozice> text BOINC (podarilo se spustit prikaz "boinccmd.exe --version")
  if (pozice != -1)
  {
    // z <verze_text> vyriznu text od pozice <pozice> (prictu 6 ... delka textu BOINC a mezera)
    boinc_verze = verze_text.substr(6+pozice);
    // nyni je v <boinc_verze> hodnota 6.12.33
    alert(boinc_verze);
    return true;
  }
  else // verzi BOINC se nepodarilo zjistit, vracim FALSE
  {
    return false;
  }
}

// funkce pro porovnani dvou verzi ve tvaru X.X.X, ocekava promene ve tvaru pole
function porovnani_verzi(verze1, verze2)
{
  // promenne <verze1> a <verze2> jsou pole textovych retezcu, tak je prevedu na cisla
  verze1[0] = (parseInt(verze1[0]) || 0);
  verze1[1] = (parseInt(verze1[1]) || 0);
  verze1[2] = (parseInt(verze1[2]) || 0);
  verze2[0] = (parseInt(verze2[0]) || 0);
  verze2[1] = (parseInt(verze2[1]) || 0);
  verze2[2] = (parseInt(verze2[2]) || 0);
  if (verze2[0] != verze1[0])
    return (verze2[0] > verze1[0]);
  else
  {
    if (verze2[1] != verze1[1])
      return (verze2[1] > verze1[1]);
    else
    {
      if (verze2[2] != verze1[2])
        return (verze2[2] > verze1[2]);
      else
        return true;
    }
  }
}

// odstrani z retezce koncove mezery a znak konce radku
function trim(text)
{
  return text.replace(new RegExp("[" + "\\n\\s" + "]+$", "g"), "");
}

// provede nastaveni promenych podle verze BOINC
function init()
{
  // od verze BOINC 6.12 vcetne je jina syntaxe programu boinccmd
  var verze_pro_porovnani = "6.12.0";
  // zavolam funkci ktera zjisti verzi BOINC a pokud se to podari nastavim zakladni promenne
  if (verze())
  {
    alert("Porovnavam verzi " + boinc_verze + " s referencni verzi " + verze_pro_porovnani);
    if (porovnani_verzi(verze_pro_porovnani.split('.'),boinc_verze.split('.')))
    {
      // Verze BOINC >= 6.12
      boinc_jednotky = "boinccmd.exe --get_tasks";
      boinc_jednotka = "boinccmd.exe --task";
    }
    else
    {
      // Verze BOINC <  6.12
      boinc_jednotky = "boinccmd.exe --get_results";
      boinc_jednotka = "boinccmd.exe --result";
    }
    return true;
  }
  else // funkce pro zjisteni verze se nepodarila, vracim FALSE
  {
    return false;
  }
}

/* MAIN */
// zavolam funkci ktera nastavi zakladni promenne
if (init())
{
  // v tuto chvili je uspesne zjistena verze a
  // uspesne nastavene zakladni promenne <boinc_jednotky> a <boinc_jednotka>
  // definuji novy objekt WScript.Shell (umozni mi spoustet externi prikazy)
  var objekt_shell = new ActiveXObject("WScript.Shell");
  // definuji metodu Exec, spusti prikaz <boinc_jednotky> a pointer uchova v promene <objekt_exec>
  var objekt_exec  = objekt_shell.Exec(boinc_jednotky);
  // textova promena do ktere se ulozi cely obsah standardniho vystupu predchazejiciho prikazu
  var text_vystup  = objekt_exec.StdOut.ReadAll();
  // na konec <text_vystup> jeste pridam jednu prazdnou sekci abych pak nemusel resit konec 
  text_vystup = text_vystup + '\n' + "X) -----------" + '\n';
  // promena <text_vystup> by mela nyni obsahovat cely standardni vystup ktery vraci prikaz "boinccmd.exe --get_tasks"
  // definuji pole do ktereho prevedu obsah <text_vystup>
  var pole_vystup  = new Array();
  // oddelovacem bude znak EOL (EndOfLine) neboli \n
  // pole <pole_vystup> se naplni tak, ze kazdy dalsi radek bude v dalsim prvku
  // 1. radek v  pole_vystup[0], 2. radek v  pole_vystup[1], ...
  pole_vystup = text_vystup.split('\n');
  // nyni musime v cyklu projit vsechny radky (vyzaduje znalost vystupu "boinccmd.exe --get_tasks")
  // jednotlive jednotky jsou oddeleny v sekcich a o kterou jednotku se jedna zjistime az uvnitr sekce
  // takze si do nejakych promenych musime ukladat co budeme dale potrebovat a az sekce skonci
  // (zacne nova ... proto jsme si na konec pridali jednu prazdnou sekci pro ukonceni posledni sekce)
  // tak se rozhodneme podle obsahu ulozenych promenych co dal podnikneme
  // pripravim si promenne
  var b_name              = ""; // nazev jednotky (name)
  var b_state             = ""; // stav jednotky (state) 2 ... Pocita nebo Pripraven nebo Cekam na zpracovani, 4 ... Odesilam, 5 ... Odeslan. Pripraven k ohlaseni
  var b_scheduler_state   = ""; // stav scheduleru jednotky (scheduler state) 0 ... Pripraven, 1 ... Cekam na zpracovani, 2 ... Pocita
  var b_suspended_via_GUI = ""; // pozastaveno (suspended via GUI) no, yes
  var b_active_task_state = ""; // stav jednotky (active_task_state) 0 ... Pripraven, 1 ... Pocitam, 9 ... Cekam na zpracovani
  var ano                 = 0;  // 1 = ANO, 0 = NE ... jestli se jedna o projekt ktery nas zajima
  var ind                 = 0;  // index pro pole do ktereho budu ukladat hodnoty
  var jednotka = new Array();   // pole do ktereho se budou ukladat radky
                                // kazdy radek bude zase pole ve tvaru ([0] = name, [1] = state, [2] = scheduler_state, [3] = suspended_via_GUI, [4] = active_task_state)

  // a ted cyklus pres cele pole
  for(var i = 0; i < pole_vystup.length; i++)
  {
    // jestlize radek obsahuje text ") -----------" tak se jedna o zacatek nove sekce
    if (pole_vystup[i].indexOf(") -----------") != -1)
    {
      // jestlize je <ano> nastaveno (jedna se o projekt ktery nas zajima) je treba provest pozadovanou akci
      if (ano == 1) {
        alert("NACITANI JEDNOTEK (" + ind + ") :\nProjekt:" + projekt + "\nJednotka:" + b_name + "\nStav:" + b_state + "\nStav scheduleru:" + b_scheduler_state + "\nsuspended_via_GUI:" + b_suspended_via_GUI + "\nactive_task_state:" + b_active_task_state);
        // dale me budou zajimat pouze jednotky ktere maji stav jednotky = 2 (state), ty ktere jsou ve stavu Odesilam nebo Odeslany uz resit nebudu, doplnen kod by nenym: stav jednotky = "downloaded"
        if (b_state == 2 || b_state == "downloaded") {
          // ulozim hodnoty do pole
          jednotka[ind] = new Array(b_name, b_state, b_scheduler_state, b_suspended_via_GUI, b_active_task_state);
          // a zvednu index o 1
          ind = ind + 1;
        }
      }
      // a po akci si opet vynulujeme promenne
      b_name              = "";
      b_state             = "";
      b_scheduler_state   = "";
      b_suspended_via_GUI = "";
      b_active_task_state = "";
      ano                 = 0;
    }
    // jestlize radek obsahuje text "   name: " tak se jedna o radek kde je nazev jednotky
    else if (pole_vystup[i].indexOf("   name: ") != -1)
    {
      // a nazev jednotky si uchovam do <b_name>
      b_name = trim(pole_vystup[i].substr(9));
    }
    // jestlize radek obsahuje text "   state: " tak se jedna o radek kde je stav jednotky
    else if (pole_vystup[i].indexOf("   state: ") != -1)
    {
      // a stav jednotky si uchovam do <b_state>
      b_state = trim(pole_vystup[i].substr(10));
    }
    // jestlize radek obsahuje text "   scheduler state: " tak se jedna o radek kde je stav scheduleru jednotky
    else if (pole_vystup[i].indexOf("   scheduler state: ") != -1)
    {
      // a stav scheduleru jednotky si uchovam do <b_scheduler_state>
      b_scheduler_state = trim(pole_vystup[i].substr(20));
    }
    // jestlize radek obsahuje text "   suspended via GUI: " tak se jedna o radek kde je informace o pozastaveni jednotky
    else if (pole_vystup[i].indexOf("   suspended via GUI: ") != -1)
    {
      // a informaci o pozastaveni jednotky si uchovam do <b_suspended_via_GUI>
      b_suspended_via_GUI = trim(pole_vystup[i].substr(22));
    }
    // jestlize radek obsahuje text "   active_task_state: " tak se jedna o radek kde je informace o active_task_state
    else if (pole_vystup[i].indexOf("   active_task_state: ") != -1)
    {
      // a informaci o active_task_state si uchovam do <active_task_state>
      b_active_task_state = trim(pole_vystup[i].substr(22));
    }
    // jestlize radek obsahuje text "   project URL: " tak se jedna o radek kde je adresa projektu
    else if (pole_vystup[i].indexOf("   project URL: ") != -1)
    {
      // a jestli radek obsahuje i adresu projektu ktery me zajima, tak si nastavim <ano>
      if (pole_vystup[i].indexOf("   project URL: " + projekt) != -1)
      {
        ano = 1;
      }
      else
      {
        ano = 0;
      }
    }
  }
  // jestlize je pole naplnene, tak <ind> bude vetsi nez 0
  if (ind > 0)
  {
/*
    // cele pole projdeme radek po radku a vypiseme
    for(var i = 0; i < ind; i++)
    {
      alert("VYPIS JEDNOTEK (" + i + ") :\nProjekt:" + projekt + "\nJednotka:" + jednotka[i][0] + "\nStav:" + jednotka[i][1] + "\nStav scheduleru:" + jednotka[i][2] + "\nsuspended_via_GUI:" + jednotka[i][3] + "\nactive_task_state:" + jednotka[i][4]);
    }
*/
    // pro nas pozadavek je ale vhodne prochazet pole od konce, a jednotky postupne pozastavit (nebo nechat pozastavene)
    // a teprve az se dostaneme na zacatek pole, tak prvnich nekolik jednotek pustit (nebo nechat pustenych)
    for(var i = ind-1; i >= 0; i--)
    {
      alert("VYPIS JEDNOTEK OD KONCE (" + i + ") :\nProjekt:" + projekt + "\nJednotka:" + jednotka[i][0] + "\nStav:" + jednotka[i][1] + "\nStav scheduleru:" + jednotka[i][2] + "\nsuspended_via_GUI:" + jednotka[i][3] + "\nactive_task_state:" + jednotka[i][4]);
      // v promenne <pocet_aktivnich_jednotek> je pocet jednotek ktere se maji pocitat
      if (i < pocet_aktivnich_jednotek)
      // vsechny jednotky ktere maji index <i> mensi nez <pocet_aktivnich_jednotek> pustime
      {
        // zjistime jestli je jednotka pozastavena
        if (jednotka[i][3] == "yes")
        {
          // a jestli ano tak ji pustime
          alert("Spoustim jednotku " + jednotka[i][0]);
          var resume_exec = objekt_shell.Exec(boinc_jednotka + " " + projekt + " " + jednotka[i][0] + " resume");
        }
      }
      else
      // vsechny ostatni pozastavime
      {
        // zjistime jestli neni jednotka pozastavena
        if (jednotka[i][3] == "no")
        {
          // a jestli neni tak ji pozastavime
          alert("Zastavuji jednotku " + jednotka[i][0]);
          var suspend_exec = objekt_shell.Exec(boinc_jednotka + " " + projekt + " " + jednotka[i][0] + " suspend");
        }
      }
    }
  }
}
alert("Konec.");
Návod k použití
vkliber píše:Soubor uložit do stejného adresáře, kde je boinccmd.exe. Po uložení souboru WCG_manage.js na něj kliknout pravým tlačítkem myši a kliknout na "Vlastnosti". Ve vlastnostech se přepnout na kartu "Skript", zaškrtnout "Zastavit skript po:" a nastavit 10 sekund a odškrtnout "Zobrazit logo ...". Dále vytvořit zástupce ve stejném adresáři (chytnout soubor WCG_manage.js pravým tlačítkem myši, posunout a pustit a stisknout "Vytvořit zde zástupce". Vytvořeného zástupce přejmenovat na WCG_manage.js, dále na něj kliknout pravým tlačítkem a a kliknout na "Vlastnosti". Ve vlastnostech se přepnout na kartu "Zástupce", zkontrolovat jestli hodnota "Spustit v:" odpovídá adresáři ve kterém se nachází skript WCG_manage.js a nastavit "Spustit:" na hodnotu "V minimalizovaném okně".
Tím je připravený skript ke spuštění. Je možno odzkoušet tak, že spustíte BOINC Manager aktivujte "přebytečné" jednotky, a pak poklepete na WCG_manage.js. Jednotky by se měly označit jako pozastavené.

Dále je nutné zařadit skript WCG_manage.js do naplánovaných úloh ve Windows, tady se postup pro WinXP už výrazně liší od W7 a W10, proto ho nebudu podrobně popisovat. Každý ať si taky zvolí svůj časový plán.
bych doplnil o nutnost údržby adres projektů, které přejdou na https protokol. Script při změně URL celkem logicky přestane fungovat.

nenym
78.9473684211 %
78.9473684211 %
Příspěvky: 7610
Registrován: úte 13 led, 2009 15:33
rok narození: 0- 0-1956
ID CNT statistics: 10124

Re: FAQ - maximální počet CPU jader pro jednotlivé projekty

#2 Příspěvek od nenym »

Doplnil jsem základní příspěvek o drobnou, leč užitečnou pomůcku.
Blíží se konsolidační balíček pětikolky; šetřím trojbojem - piji staré víno, jím plesnivé sýry a jezdím v autě bez střechy.
UotD 767x Obrázek
1xObrázek 3xObrázek 9xObrázek 12xObrázek Obrázek
Obrázek

Zamčeno

Zpět na „Rady a návody“