To co? Bawimy się? :D

Zaczęty przez mariotti, 24 Maj 2013, 17:01

mariotti

Oszacowałem że work-unity będą się generowały przez około 75 dni.
Taki okres czasu to sporo, ale odrzuciłby wszystkie powtarzające się
zadania na głębokości 8 ruchów. Łączny transfer wszystkich zadań to
około 2-3GB - czyli w sam raz. 

Można też wygenerować work-unity na głębokość 7 ruchów, to zajęłoby
tylko 2.5 doby. Niestety takie podejście nie odrzuci tak dużo układów
jak poprzednie i nie wykorzysta łącz internetowych które dziś są
dość szybkie, bo łączny transfer byłby tylko 0.3GB.

Zastanawiam się czy warto napisać algorytm generowania work-unitów
ręcznie, bez pośrednictwa SQLita. Ciekawe jak szybko by to działało
bez bazy danych. Jakby dało się przyspieszyć z 3 razy, to generowanie
zadań da wersji 8-ruchowej zajęłoby tylko 25 dni. Niestety dodatkowy
nakład pracy i czasu na takie obliczenia jeszcze bardziej  opóźni premierę
projektu.

Więc chyba pozostanę przy wersji 7-ruchowej. Może na 17go będą gotowe
work-unity, może do tej pory będę wiedział jak się je dodaje do serwera :)


Pozdrawiam


mariotti

W trakcie generowania work-units wyczerpały się bufory ram.
Po wyczerpaniu oszacowanie czasu wzrosło 3 krotnie :/ Musiałem
przerwać i przenieść generator na osobny komputer. Tak więc
czekamy na work-unity, a w międzyczasie spróbuję zainstalować
BOINCa na serwerze wirtualnym.

Pozdrawiam

krzyszp

Podaj jakiś adres, żeby można było śledzić postępy :)

Fajne zegarki :)
Należę do drużyny BOINC@Poland
 Moja wizytówka

mariotti

Cytat: krzyszp w 15 Czerwiec 2013, 14:40
Podaj jakiś adres, żeby można było śledzić postępy :)
Na chwilę obecną nic nie jest zrobione, więc nic nie można zobaczyć ani
śledzić. Wykupiony jest tylko serwer i domena. Domena
http://computer-chess.com była zajęta, to wykupiłem http://computers-chess.com .

W wolnej chwili podepnę domenę, zainstaluję apache, mysqla i boinca. Myślę
też, żeby na wordpresie uruchomić jakąś witrynę projektu. To wszystko wymaga
czasu, którego mam diabelnie mało, a teraz jeszcze się okazało, że generowanie
work-unitów (a właściwie to optymalizacja work-unitów ) będzie trwała z tydzień.

Niemniej mam nadzieję że w przeciągu kliku dni pojawi się jakaś aplikacja
szkieletowa pod domeną: http://computers-chess.com.

Pozdrawiam


P.S.
A może doradzicie jakiś algorytm do optymalizacji WU?

Treść zadania:
Program dostaje na standardowe wejście ogromną ilość wierszy. Są trzy
wersje zadania, różniące się ilością wierszy: 119060324, 3195901860, 84998978956.
Unikalnych wierszy jest odpowiednio:9417681, 96400068, 988187354. Jeden
wiersz ma rozmiar około 60-70bajtów. Unikalne wiersze trzeba zapisać na
dysku w takiej kolejności w jakiej przychodzą na standardowe wejście. Dodatkowo za
każdym wierszem musi być zapisana ilość jego wystąpień. Jeśli źródło wierszy
się wyczerpie, to nie ma przeszkód aby wszystkie wiersze dostać na standardowe
wejście jeszcze raz w tej samej kolejności. Natomiast nie można przeglądać
wierszy od tyłu i nie można poprosić o wiersz zadanym numerze - no chyba że
ktoś ma 8TB wolnego miejsca na dysku i sobie najpierw wszystkie wiersze zapisze :D

W skrócie: trzeba zapisać unikalne wiersze w takiej kolejności jak przychodzą
do programu, a z każdym wierszem trzeba jeszcze zapisać ilość powtórzeń.

Na bazie SQLite to zadanie w wersji drugiej będzie się wykonywało prawdopodobnie
cały tydzień. Jakby ktoś chciał rzucić okiem na ostatnią wersję tego programiku i
może coś doradzić, to załączam kod:

#include <QSqlDatabase>
#include <QSqlQuery>
#include <QSqlError>
#include <QVariant>
#include <QDir>
#include <QDebug>
#include <QDateTime>
#include <QFile>
#include <cstdlib>

#include <iostream>
#include <iomanip>

void startTransaction( QSqlDatabase &db ) {
QString q = QString("BEGIN");
QSqlQuery query( db );
if( !query.exec( q ) ) qCritical() << query.lastError().text();
}


void commitTransaction( QSqlDatabase &db ) {
QString q = QString("COMMIT");
QSqlQuery query( db );
if( !query.exec( q ) ) qCritical() << query.lastError().text();
}


QSqlDatabase connectDB( const QString &db_path ) {
if( QFile::exists(db_path) )
QFile(db_path).remove();
QSqlDatabase db = QSqlDatabase::addDatabase("QSQLITE");
db.setDatabaseName( db_path );
if( ! db.open() )
qCritical() << "Cant open connection to database";
return db;
}


void createDB( QSqlDatabase &db ) {

QSqlQuery query( db );

{
QString q =
"CREATE TABLE Fens ("
"id       INTEGER      NOT NULL PRIMARY KEY ASC AUTOINCREMENT,       "  // klucz główny
"fen      VARCHAR(70)  NOT NULL,                                     "  // układ
"repeat   INTEGER      NOT NULL                                      "  // powtórzenia
")";
if( ! query.exec( q ) )
qCritical() << q.toAscii().constData() << query.lastError().text().toAscii().constData();
}

{
QString q = "CREATE UNIQUE INDEX IdxFens ON Fens(fen)";
if( ! query.exec( q ) )
qCritical() <<  q.toAscii().constData() << query.lastError().text().toAscii().constData() ;
}

{
QString q = "PRAGMA synchronous=OFF";
if( ! query.exec( q ) )
qCritical() <<  q.toAscii().constData() << query.lastError().text().toAscii().constData() ;
}

{
QString q = "PRAGMA cache_size=3000000";
if( ! query.exec( q ) )
qCritical() <<  q.toAscii().constData() << query.lastError().text().toAscii().constData() ;
}

{
QString q = "PRAGMA temp_store=2";
if( ! query.exec( q ) )
qCritical() <<  q.toAscii().constData() << query.lastError().text().toAscii().constData() ;
}


{
QString q = "PRAGMA journal_mode=OFF";
if( ! query.exec( q ) )
qCritical() <<  q.toAscii().constData() << query.lastError().text().toAscii().constData() ;
}


}


void incOrInsert( QSqlDatabase &db , const char fen[] ) {
QSqlQuery query( db );
QString q = QString("SELECT COUNT(*) FROM Fens WHERE fen='%1'").arg(fen);
if( ! query.exec( q ) ) qCritical() << q.toAscii().constData() << query.lastError().text().toAscii().constData() ;
if( ! query.next()    ) qCritical() << q.toAscii().constData() << query.lastError().text().toAscii().constData() ;
if( query.value(0).toInt() > 0 )
q = QString("UPDATE Fens SET repeat=repeat+1 WHERE fen='%1'").arg(fen);
else
q = QString("INSERT INTO Fens (fen,repeat) VALUES ('%1',1)").arg(fen);
if( ! query.exec( q ) ) qCritical() << q.toAscii().constData() << query.lastError().text().toAscii().constData() ;
}




long long numberUnique( QSqlDatabase &db ) {
QSqlQuery query( db );
QString q = QString("SELECT count(*) AS cnt FROM Fens");
if( ! query.exec( q ) ) qCritical() <<  q.toAscii().constData() << query.lastError().text().toAscii().constData() ;
if( ! query.next()    ) qCritical() <<  q.toAscii().constData() << query.lastError().text().toAscii().constData() ;

const long long uniq = query.value(0).toLongLong();
std::cout<<"number of unique: " << uniq << std::endl;
return uniq;
}


void allSum( QSqlDatabase &db ) {
QSqlQuery query( db );
QString q = QString("SELECT sum(repeat) AS cnt FROM Fens");
if( ! query.exec( q ) ) qCritical() <<  q.toAscii().constData() << query.lastError().text().toAscii().constData() ;
if( ! query.next()    ) qCritical() <<  q.toAscii().constData() << query.lastError().text().toAscii().constData() ;
std::cout<<"all sum: " << query.value(0).toLongLong() << std::endl;
}


void maxRepeat( QSqlDatabase &db ) {
QSqlQuery query( db );
QString q = QString("SELECT fen, repeat FROM Fens WHERE repeat = (SELECT max(repeat) FROM Fens)");
if( ! query.exec( q ) ) qCritical() << q.toAscii().constData() << query.lastError().text().toAscii().constData() ;
std::cout<<"max repeated:"<<std::endl;
for( int i=1 ; query.next() ; i++ )
std::cout<<i<<". "<<query.value(0).toString().toAscii().constData()<<" rep:"<<query.value(1).toInt()<< std::endl;
std::cout<<"------------------"<<std::endl;
}

void panic(QtMsgType type, const char *msg) {
(void)type;
(void)msg;
abort();
}

int main(int argc, char *argv[]) {
qInstallMsgHandler(panic);

if( argc != 2 ) {
std::cout<<"using:"<<std::endl;
std::cout<<"wu_test_perft db_path"<<std::endl;
qCritical();
}

QSqlDatabase db = connectDB( argv[1] );
createDB( db );

startTransaction(db);

QDateTime bench = QDateTime::currentDateTime();

char fen[1024];
for( long long i=1 ; ! std::cin.getline( fen , 1024 ).fail() ; i++ ) {
int len = strlen(fen)-1;
while( len>=0 && ( fen[len] == ' ' || fen[len] == '\t' || fen[len] == '\n' || fen[len] == '\r' ) )
fen[len--] = 0;
incOrInsert( db , fen );
if( i % 10000 == 0 ) {
// const double percent = i * 100.0 / 4865609.0;       // depth=5
// const double percent = i * 100.0 / 119060324.0;     // depth=6
const double percent = i * 100.0 / 3195901860.0;    // depth=7
// const double percent = i * 100.0 / 84998978956.0;   // depth=8
const QDateTime curr = QDateTime::currentDateTime();
const double elapsed = curr.toTime_t() - bench.toTime_t();
std::cout<<"\rtotal: "<<i<<";";
std::cout<<"   percent: "<<std::setprecision(2)<<std::fixed<<percent<<"%;";
std::cout<<"   all time: "<<std::setprecision(2)<<std::fixed<< ( elapsed/percent * 100.0 )<<"s;";
std::cout<<"   time left: "<<std::setprecision(2)<<std::fixed<< ( elapsed/percent * (100.0 - percent) )<<"s";
}
}
std::cout<<std::endl;

numberUnique(db);
allSum(db);
maxRepeat(db);

commitTransaction(db);

db.close();
return 0;
}


krzyszp

1. W jakiej postaci wiersze "przychodzą"? Są generowane, czy też to np. plik?
2. Jeśli są w pliku, to dlaczego zadania policzenia nie zrzucisz na bazę?

Jeżeli już wszystkie liczby będziesz miał w db, to robisz SELECT COUNT i SELECT DISTINCT i masz odpowiedzi na swoje pytania...

Sorki, jeżeli coś źle zrozumiałem :)

Ps. Serwer BOINC postawi swoją stronę, więc chyba nie ma sensu stawiać dodatkowej. Na radioaktywnym zbudowaliśmy taką dodatkową stronę główną i chyba to się okazało bez sensu...

Fajne zegarki :)
Należę do drużyny BOINC@Poland
 Moja wizytówka

Szopler

No dokładnie. To sensu nie ma. Dobry opis i logo można wrzucić na stronę ./boinc Dodatkowa to dodatkowy problem.
8TB... a ile po kompresji gzipem, tarem czy innym rarem? Można przesyłać zadania skopmpresowane, rozkompresować u klienta, policzyć, skompresować wynik i odesłać na serwer.

mariotti

#246
Cytat: krzyszp w 15 Czerwiec 2013, 20:13
1. W jakiej postaci wiersze "przychodzą"? Są generowane, czy też to np. plik?
Są generowane, więc można je także zapisać do pliku. Jednak taki plik
zajmuje sporo miejsca. Nie wiem czy lepiej zapisywać w pliku, czy
wiele razy wygenerować od nowa :)


Cytat: krzyszp w 15 Czerwiec 2013, 20:13
2. Jeśli są w pliku, to dlaczego zadania policzenia nie zrzucisz na bazę?
Właśnie przerzuciłem na bazę, dokładnie na bazę SQLite. Tamten program, którego
źródło załączyłem powyżej, sam nie robi nic, wszystko jest przerzucone na bazę.


Cytat: krzyszp w 15 Czerwiec 2013, 20:13
Jeżeli już wszystkie liczby będziesz miał w db, to robisz SELECT COUNT i SELECT DISTINCT i masz odpowiedzi na swoje pytania...
Jakby wszystkie dane były już w bazie, to w grę wchodzi mniej/więcej
takie zapytanie:


SELECT
  DISTINCT ON (row),
  row,
  (select min(nr) from table as t2 where t2.row = t1.row) as min_nr,
  (select count(*) from table as t3 where t3.row = t1.row) as cnt
FROM
  table as t1
ORDER BY
  min_nr;

Tabela ma tylko dwa pola:
nr - bigint
row - varchar(70)



Są trzy wersje tabeli, różniące się ilością rekordów:
1) 119.060.324
2) 3.195.901.860
3) 84.998.978.956

Nie wiem czy jakaś baza poradzi sobie w rozsądnym czasie z drugą wersją :)
Jeśli w ogóle nie uda się tego policzyć dla wersji trzeciej, to nic złego się nie
stanie. Ale wersję drugą, czyli 3mld rekordów, trzeba jakoś policzyć.

Cytat: krzyszp w 15 Czerwiec 2013, 20:13
Sorki, jeżeli coś źle zrozumiałem :)
To chyba ja mam tendencję do mętnego opisywania :) Gdy się coś napisze, to
wypada włożyć napisany tekst do szuflady, przetrzymać go tam chociaż przez
tydzień, a po tygodniu gruntownie go przeredagować. Ja piszę z doskoku, czasami
będąc bardzo zmęczony, czasami niedowidzący, więc miło że w ogóle macie
ochotę czytać moje wypociny :D


Cytat: krzyszp w 15 Czerwiec 2013, 20:13
Ps. Serwer BOINC postawi swoją stronę, więc chyba nie ma sensu stawiać dodatkowej. Na radioaktywnym zbudowaliśmy taką dodatkową stronę główną i chyba to się okazało bez sensu...
Hmmm, a to pomyślimy jeszcze jak to będzie ze stronami. Zauważyłem że BOINC instaluje dwie
witryny, jedną dla userów, drugą dla operatorów.

Generalnie projekty szachowe chciałbym rozwijać przez wiele lat w różnych kierunkach  (jeśli tylko
czas i zdrówko pozwolą), więc jakaś główna strona powstanie, a na niej będą odnośniki do projektów
szczegółowych. Obecny projekt, o którym rozmawiamy od początku w tym wątku, czyli zliczanie
węzłów w drzewie gry, właśnie ma być jednym z kierunków. Może za rok będę gotowy
na jakiś bardziej ambitny projekt związany z szachami. Ambitnych zagadnień jest sporo, chociażby
tuning końcówek, tuning debiutów, no i to co mnie najbardziej kręci: szachy w pełni oparte na
technikach sztucznej inteligencji. Wszystkie te projekty nadają się w miarę dobrze na BOINC.
Ale w dłuższej perspektywie na stronie może także zamieszczę jakieś projekty zupełnie
nie związane z BOINC.


mariotti

Cytat: Szopler w 15 Czerwiec 2013, 20:37
No dokładnie. To sensu nie ma. Dobry opis i logo można wrzucić na stronę ./boinc Dodatkowa to dodatkowy problem.
8TB... a ile po kompresji gzipem, tarem czy innym rarem? Można przesyłać zadania skopmpresowane, rozkompresować u klienta, policzyć, skompresować wynik i odesłać na serwer.

Są tak jakby dwa rodzaje kompresji :D

Pierwsza kompresja ma zliczyć powtórzenia. Niektóre zadania będą się powtarzały
wiele razy, np. to samo zadanie może powtórzyć się aż 500 razy. Nie ma sensu
wysyłać 500 razy tego samego zadania, wystarczy wysłać raz do policzenia i drugi raz
do weryfikacji.

Druga kompresja, to upakowanie zadań po około 10-300 sztuk do jednej paczki i
zastosowanie zipa.

Pierwsza kompresja (szacunkowo) zmniejszy rozmiar danych 80 razy. Druga
około 10 razy. Więc obie razem zmniejszą te 8TB 800 razy. Więc zostanie
około 5-10GB danych.

Ta cała męka z optymalizacją work-unitów jest w dwóch celach. Pierwszy cel
jest właśnie taki, aby z 8TB zrobiło się 5-10GB.

Drugi cel jest bardziej skomplikowany.... w skrócie przyspieszy obliczenia,
być może nawet dwukrotnie :D

Pozdrawiam

krzyszp

Mam bazę z 8,8mld rekordów, w 20-kilku tabelach, każdy rekord jest większy, niż Ty używasz. Cala baza ma w chwili obecnej ok 400GB (293,2GB dane i 103.3GB indeksy), całość chodzi... wolno ;) niemniej jakoś działa :)

Fajne zegarki :)
Należę do drużyny BOINC@Poland
 Moja wizytówka

mariotti

Cytat: krzyszp w 15 Czerwiec 2013, 22:10
Mam bazę z 8,8mld rekordów, w 20-kilku tabelach, każdy rekord jest większy, niż Ty używasz. Cala baza ma w chwili obecnej ok 400GB (293,2GB dane i 103.3GB indeksy), całość chodzi... wolno ;) niemniej jakoś działa :)
Na pewno Twoja baza działa i to dobrze, ale w jednym zapytaniu
nie wyciągasz z bazy 96400068 albo 988187354 rekordów - w dodatku
posortowanych po wyniku innego podzapytania :)

Nie dowiemy się czy Twój pomysł jest dobry, jeśli nie sprawdzimy
go w praktyce. Naprawdę nie mam bladego pojęcia ile czasu
zajmie tamto zapytanie z kilku postów powyżej. Trzeba do tabeli
dodać 3mld rekordów i uruchomić.

Pozdrawiam

mariotti

Mam jeszcze jedną prośbę. Polećcie mi jakiś serwer wirtualny.
Na początek do testów ważne jest tylko to, żeby był tani.

Jakiś kupiłem i nie mogę zrobić nic. Z poziomu panelu ani nie
mogę przeinstalować systemu, ani zresetować, a administrator
podgląda moje hasła i wysyła w niezaszyfrowanych e-mailach :D

Pozdrawiam

krzyszp

Już polecałem - polecę jeszcze raz ;)
ultimahost.pl

Co do bazy, to generalnie na niej leci głownie
select * from tabela where Time = xxx
oraz np.
select max(Time) from tabela... gdzie jest 378'432'617 rekordów w tabeli

Także też ma co robić :)

Fajne zegarki :)
Należę do drużyny BOINC@Poland
 Moja wizytówka

mariotti

#252
Cytat: krzyszp w 15 Czerwiec 2013, 23:21
Już polecałem - polecę jeszcze raz ;)
ultimahost.pl
Zamówiłem, jutro zapłacę. Nie przysłali mi hasła mailem - może
to naprawdę profesjonalne usługi ;-)


Cytat: krzyszp w 15 Czerwiec 2013, 23:21
Co do bazy, to generalnie na niej leci głownie
select * from tabela where Time = xxx
oraz np.
select max(Time) from tabela... gdzie jest 378'432'617 rekordów w tabeli
Także też ma co robić :)
Indeksy świetnie pełnią rolę w zapytaniach które zaprezentowałeś. U mnie indeksy
choć pomogą, to prawdopodobnie nie załatwią sprawy, bowiem tabela ma za
dużo danych i zapytanie wyciąga za dużo danych.

Pozdrawiam.

P.S.
Właśnie widzę że na drugim kompie programik do wygenerowania work-units wywalił po
kilkunastu godzinach pracy. Nie mam pojęcia jaka jest przyczyna... może SQLite
ma problemy na tamtym kompie, może jest wrażliwy na jakieś specyficzne parametry.
Programik jest mały, więc raczej błędów nie narobiłem... Kurde czuję że czeka
mnie ręczne wyrzeźbienie jakiegoś programu do optymalizacji WU :/ Wszystko
idzie źle, ale wierzę że w końcu się uda.

krzyszp

Zapomniałem dodać, że u tego samego usługodawcy jest wykupiony dedyk dla radioaktywnego i cóż... działa świetnie :)

Fajne zegarki :)
Należę do drużyny BOINC@Poland
 Moja wizytówka

mariotti

Cytat: krzyszp w 16 Czerwiec 2013, 00:27
Zapomniałem dodać, że u tego samego usługodawcy jest wykupiony dedyk dla radioaktywnego i cóż... działa świetnie :)
Fajnie. Ale niestety i tak i tak trzeba poczekać. Nie wiem czemu na stacjonarnym kompie ten
programik do optymalizowana WU pada. Na laptopie działa, ale laptopa potrzebuję do pracy,
nie mogę tam zostawić włączonego generatora. Pewne będę musiał napisać jakiś
program bez pośrednictwa bazy.
Pozdrawiam

mariotti

Cytat: mariotti w 16 Czerwiec 2013, 09:18
Fajnie. Ale niestety i tak i tak trzeba poczekać...

Zmniejszyłem rozmiar bufora RAM w SQLite i odpaliłem ten programik do
optymalizacji WU. Na zmniejszonym buforze mam oszacowanie czasu 670tys
sekund i rośnie. Jeśli się tym razem nie wywali, to za niecałe 10 dni będzie
materiał na WU. Jeśli się wywali znowu, to nie wiem... będzie trzeba opracować
jakiś specjalny algorytm do tego zadania.

Pozdrawiam

krzyszp

albo podeślij komuś z nas do odpalenia i podesłania Ci samej bazy :)

Fajne zegarki :)
Należę do drużyny BOINC@Poland
 Moja wizytówka

mariotti

Cytat: krzyszp w 16 Czerwiec 2013, 17:09
albo podeślij komuś z nas do odpalenia i podesłania Ci samej bazy :)
W obecnej wersji ma to sens, jedynie w przypadku gdy ktoś dysponuje
bardzo szybkim dyskiem. Zwłaszcza bardzo ważne jest, aby losowy ( w losowych
miejscach dysku ) odczyt i zapis małych porcji danych był szybki. Takie wymagania
spełniają jedynie niektóre dyski SSD, albo macierze dyskowe z bardzo
dużym buforem RAM. Jak ktoś ma taki sprzęt, to możemy spróbować :D

Raczej będzie trzeba napisać dobry algorytm. Jeśli WU na serwerze mają być
policzone na głębokość ośmiu ruchów, to na jednym kompie na bazie SQLite będą
się generowały rok czasu :D

Osiem ruchów to byłby wypas, zwłaszcza jakbyśmy doszli do (łącznej)
głębokości 16 (lub więcej) ruchów. Jakby na serwerze leżały work-unity
policzone już na 8 ruchów, to klient dostawałby zadania do policzenia na
głębokość od 4-8 ruchów. Łącznie dawałoby to głębokość od 12 do 16
ruchów. Jakby ktoś chciał liczyć długo, to by brał dużą paczkę, jakby ktoś
miał mało czasu albo wolny komputer, to by brał małą paczkę.

Ponadto wraz z głębokością (z tą głębokością na której WU są przygotowane
na serwerze) rosną oszczędności. Przy głębokości:
6 ruchów oszczędność wynosi: 92,09%
7 ruchów: 96,98%
8 ruchów: 98,84%
dla ponad 8 nie mam danych :)

Więc chyba warto przeliczyć WU na te 8 ruchów, a tym czasem nie mogę
się uporać z głębokością 7 :) Ciekawe jest to, że dla głębokości 6 ruchów
można policzyć ( o ile dobrze pamiętam ) w kilkadziesiąt sekund, bo całość mieści
się w RAM :)

Baza bez kompresji dla 8 ruchów będzie miała rozmiar około 60GB, ale podejrzewam
że skompresuje się 15-krotnie. Więc transfer  na jedno przeliczenie wyniósłby
4GB - więc akurat.

Trochę przeraża mnie ilość paczek. Jakby w paczce było 1000 zadań, to
ilość paczek wynosiłaby około 1mln. Jednak 1000 zadań dla zoptymalizowanego
programu to raczej mała ilość. Może da się upakować po 10tys zadań do jednej
paczki. W takim przypadku byłoby to 100tys paczek. 

Wszystko ładnie, pięknie, ale pozostaje problem wyboru unikalnych
zadań z bagatela 85mld :)

Pozdrawiam

mariotti

Na małym buforze program też się wywalił. Nie mam bladego
pojęcia jaka jest przyczyna. Program tylko wczytuje wiersze ze
standardowego wejścia, sprawdza czy wiersz jest unikalny, następnie
go dodaje do bazy lub lub zwiększa licznik powtórzeń - czyli
banał.

Nie wiem czy coś mam źle z konfiguracją SQLite, czy może jakaś
usterka w bashu... może coś z moim komputerem źle, może ja mam
jakiś błąd w kodzie, ale jaki mogę mieć błąd w tych paru linijkach kodu :/


Niestety muszę zawiesić prace na około tydzień, aż będę miał trochę w
wolnego czasu,  żeby wklepać ręcznie cały algorytm do optymalizacji
work-units.


AXm77

Spróbuj podesłać obecny program do paru chętnych, może wygeneruje próbki, przy okazji dowiesz się w ciągu tygodnia, czy masz problem z programem, czy z komputerem .

mariotti

Cytat: AXm77 w 17 Czerwiec 2013, 13:57
Spróbuj podesłać obecny program do paru chętnych, może wygeneruje próbki, przy okazji dowiesz się w ciągu tygodnia, czy masz problem z programem, czy z komputerem .
Ok. Jestem strasznie zalatany, ale przygotuję wszystko wieczorem lub jutro rano.

Podejrzewam że będą problemy z bibliotekami. Program perft się nie zmienił, ale dodatkowy
program korzysta z biblioteki QT, ze sterownika do SQLita no i z samej
bazy SQLite. Nie wiem czy da się to wszystko wkompilować statycznie. Możliwe
że każdy chętny będzie musiał sobie zainstalować środowisko i skompilować
sam. Niemniej spróbować możemy. Właściwie to można już pobrać środowisko
QTCreator i skompilować ostatnie źródło które podwałem.


Z drugiej strony zastanawiam się czy jest sens uruchamiania tego programu na zwykłych komputerach,
bowiem on nawet w optymistycznym przypadku wygeneruję bazę tylko dla 7 ruchów. Bazy
8-ruchowej nie da rady nawet w kilka miesięcy. De facto baza 7-ruchowa nie jest taka zła, w
mniejszych (łącznych) głębokościach jest nawet lepsza, ale w dłuższej perspektywie 8-ruchowa da 
większe możliwości. Jakby ktoś miał dostęp do super-szybkiej macierzy
dyskowej to wtedy byłby wielki sens, bo ja bym zaoszczędził dużo czasu na pisaniu
specjalistycznego programu, całą robotę za mnie by odwaliła baza i dobry sprzęt.

No ale przygotuję i zobaczymy jak to się zachowa. Skrypt ma opcje do wygenerowania bazy
dla dowolnej ilości ruchów, można uruchomić testy dla 4 i 5 ruchów - trwają one krótko.

Pozdrawiam i dzięki za zainteresowanie.











krzyszp

root@krzyszp-OptiPlex-330:/home/krzyszp/Szachy# gcc -o generuj szachy.c
szachy.c:1:24: fatal error: QSqlDatabase: No such file or directory
compilation terminated.


Możesz dołączyć resztę plików projektu?
Mogę próbować, ale po SSH...

Fajne zegarki :)
Należę do drużyny BOINC@Poland
 Moja wizytówka

mariotti

#262
Cytat: krzyszp w 17 Czerwiec 2013, 14:32
root@krzyszp-OptiPlex-330:/home/krzyszp/Szachy# gcc -o generuj szachy.c
szachy.c:1:24: fatal error: QSqlDatabase: No such file or directory
compilation terminated.

Możesz dołączyć resztę plików projektu?
Mogę próbować, ale po SSH...

Pisałem pół godziny szczegółową instrukcję, po czym otrzymałem wiadomość
że załącznik się nie wysłał, a mojej instrukcji już nie było :) Napiszę za jakiś
czas jeszcze raz, teraz nie mogę. Przepraszam.

---------------------------------------------------------------------------------------------------------
No więc jeszcze raz:

Najwygodniej zainstalować jakieś GUI na serwerze. Następnie logujemy się
np. przez vncviewer i instalujemy środowisko qtcretor. W środowisku zakładamy
nowy projekt typu konsola i wklejamy kod do pliku main.cpp. Srodowisko samo
zbuduje polecenia do kompilacji.

Jeśli nie można zainstalować qtcratora, to zapisujemy źródło jako plik main.cpp.
Następnie kompilujemy i linkujemy (dwoma poleceniami):

g++ -c -m64 -pipe -O2 -Wall -W -D_REENTRANT -DQT_WEBKIT -DQT_NO_DEBUG -DQT_SQL_LIB -DQT_CORE_LIB -DQT_SHARED -I/usr/share/qt4/mkspecs/linux-g++-64 -I../wu_test_perft -I/usr/include/qt4/QtCore -I/usr/include/qt4/QtSql -I/usr/include/qt4 -I. -o main.o ../wu_test_perft/main.cpp
g++ -m64 -Wl,-O1 -o wu_test_perft main.o    -L/usr/lib/x86_64-linux-gnu -lQtSql -lQtCore -lpthread


Otrzymujemy w ten sposób program wu_test_perft.

Uruchamiamy go komendą z nazwą bazy danych, np.:

./wu_test_perft db.sqlite


Program czeka na wiersze, trzeba je podać na standardowe wejście.
Wiersze nie mogą być dłuższe niż 70 bajtów. Kończymy podawanie
wierszy znakiem końca pliku Ctrl+D. Zrzut z mojego ekranu:

ls -l
-rw-rw-r-- 1 x x    7345 2013-06-15 13:07 main.cpp
-rwxrwxr-x 1 x x 2140238 2013-06-17 15:04 perft4

g++ -c -m64 -pipe -O2 -Wall -W -D_REENTRANT -DQT_WEBKIT -DQT_NO_DEBUG -DQT_SQL_LIB -DQT_CORE_LIB -DQT_SHARED -I/usr/share/qt4/mkspecs/linux-g++-64 -I../wu_test_perft -I/usr/include/qt4/QtCore -I/usr/include/qt4/QtSql -I/usr/include/qt4 -I. -o main.o ../wu_test_perft/main.cpp

g++ -m64 -Wl,-O1 -o wu_test_perft main.o    -L/usr/lib/x86_64-linux-gnu -lQtSql -lQtCore -lpthread

/wu_test_perft db.sqlite
aaa
bbb
ccc
aaa
aaa
ddd
ddd
[crtl+d]

number of unique: 4
all sum: 7
max repeated:
1. aaa rep:3
------------------

ls -l
razem 2244
-rw-r--r-- 1 x x    4096 2013-06-17 16:09 db.sqlite
-rw-rw-r-- 1 x x    7345 2013-06-15 13:07 main.cpp
-rw-rw-r-- 1 x x   59008 2013-06-17 16:09 main.o
-rwxrwxr-x 1 x x 2140238 2013-06-17 15:04 perft4
-rwxrwxr-x 1 x x   50557 2013-06-17 16:09 wu_test_perft



Jako źródła wierszy używamy programu perft4. Łączymy oba programy skryptem powłoki:

(
(
echo printLeafs 4
echo quit
) | ./perft4
) | ./wu_test_perft db.sqllite


U mnie wygląda to tak, widać że program z 197281 WU sporo odrzucił, zostało tylko:72078

number of unique: 72078
all sum: 197281
max repeated:
1. rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w qkQK - rep:16
------------------


Jeśli zadziała, to w powyższym skrypcie zwiększamy czwórkę na 7
echo printLeafs 7

Program w trakcie działania będzie wyświetlał statystyki i oszacowanie końcowego czasu.

W źródle programu można zmienić rozmiar cache w bazie SQLite:
QString q = "PRAGMA cache_size=xxxxxx";
Opis komendy jest na tej stronie:
http://www.sqlite.org/pragma.html#pragma_cache_size

Poprzednia instrukcja była bardziej szczegółowa, ale może ta też wystarczy  :)

Pozdrawiam














krzyszp

Cytat: mariotti w 17 Czerwiec 2013, 15:30
Napiszę za jakiś czas jeszcze raz, teraz nie mogę. Przepraszam.
No problem ;)

Fajne zegarki :)
Należę do drużyny BOINC@Poland
 Moja wizytówka

mariotti

Cytat: krzyszp w 17 Czerwiec 2013, 16:02
Cytat: mariotti w 17 Czerwiec 2013, 15:30
Napiszę za jakiś czas jeszcze raz, teraz nie mogę. Przepraszam.
No problem ;)

Można też work-units zrzucić do pliku testowego out.txt takim skryptem:

(
echo printLeafs 4
echo quit
) | ./perft4 > out.txt


Dla głębokości 7, trzeba wpisać:
echo printLeafs 7

Następnie można jakimiś innymi narzędziami zrealizować to samo zadanie :)

Pozdrawiam

krzyszp

rnbqkb1r/ppppp1pp/7n/5p2/8/7N/PPPPPPPP/RNBQKBR1 w qkQ -
rnbqkb1r/pppppp1p/6pn/8/8/7N/PPPPPPPP/RNBQKBR1 w qkQ -
rnbqkb1r/pppppp1p/7n/6p1/8/7N/PPPPPPPP/RNBQKBR1 w qkQ -
r1bqkb1r/pppppppp/n6n/8/8/7N/PPPPPPPP/RNBQKBR1 w qkQ -
r1bqkb1r/pppppppp/2n4n/8/8/7N/PPPPPPPP/RNBQKBR1 w qkQ -
rnbqkb1r/pppppppp/8/8/6n1/7N/PPPPPPPP/RNBQKBR1 w qkQ -
rnbqkbnr/pppppppp/8/8/8/7N/PPPPPPPP/RNBQKBR1 w qkQ -
rnbqkb1r/pppppppp/8/5n2/8/7N/PPPPPPPP/RNBQKBR1 w qkQ -
rnbqkbr1/pppppppp/7n/8/8/7N/PPPPPPPP/RNBQKBR1 w qQ -

?

Fajne zegarki :)
Należę do drużyny BOINC@Poland
 Moja wizytówka

mariotti

#266
Cytat: krzyszp w 17 Czerwiec 2013, 17:46

rnbqkb1r/ppppp1pp/7n/5p2/8/7N/PPPPPPPP/RNBQKBR1 w qkQ -
rnbqkb1r/pppppp1p/6pn/8/8/7N/PPPPPPPP/RNBQKBR1 w qkQ -
rnbqkb1r/pppppp1p/7n/6p1/8/7N/PPPPPPPP/RNBQKBR1 w qkQ -
r1bqkb1r/pppppppp/n6n/8/8/7N/PPPPPPPP/RNBQKBR1 w qkQ -
r1bqkb1r/pppppppp/2n4n/8/8/7N/PPPPPPPP/RNBQKBR1 w qkQ -
rnbqkb1r/pppppppp/8/8/6n1/7N/PPPPPPPP/RNBQKBR1 w qkQ -
rnbqkbnr/pppppppp/8/8/8/7N/PPPPPPPP/RNBQKBR1 w qkQ -
rnbqkb1r/pppppppp/8/5n2/8/7N/PPPPPPPP/RNBQKBR1 w qkQ -
rnbqkbr1/pppppppp/7n/8/8/7N/PPPPPPPP/RNBQKBR1 w qQ -

?

Wszystko się zgadza, to są dane wejściowe dla aplikacji liczącej :)
Pozdrawiam

P.S.
U mnie tak to wygląda:


cat go2.sh
(
echo printLeafs 7
echo quit
) | ./perft4 > 'all_rows.txt'
time ./go2.sh
real    120m19.033s (dwie godziny sam zapis, bez żadnego przetwarzania!)
user    41m47.965s
sys     76m9.274s


ls -l
-rw-rw-r-- 1 x x 195013386079 2013-06-17 18:32 all_rows.txt


head -n 20 all_rows.txt
rnbqkbnr/2pppppp/1p6/p7/PP6/8/2PPPPPP/RNBQKBNR b qkQK -
rnbqkbnr/2pppppp/1p6/p7/P7/1PP5/3PPPPP/RNBQKBNR b qkQK -
rnbqkbnr/2pppppp/1p6/p7/P1P5/1P6/3PPPPP/RNBQKBNR b qkQK -
rnbqkbnr/2pppppp/1p6/p7/P7/1P1P4/2P1PPPP/RNBQKBNR b qkQK -
rnbqkbnr/2pppppp/1p6/p7/P2P4/1P6/2P1PPPP/RNBQKBNR b qkQK -
rnbqkbnr/2pppppp/1p6/p7/P7/1P2P3/2PP1PPP/RNBQKBNR b qkQK -
rnbqkbnr/2pppppp/1p6/p7/P3P3/1P6/2PP1PPP/RNBQKBNR b qkQK -
rnbqkbnr/2pppppp/1p6/p7/P7/1P3P2/2PPP1PP/RNBQKBNR b qkQK -
rnbqkbnr/2pppppp/1p6/p7/P4P2/1P6/2PPP1PP/RNBQKBNR b qkQK -
rnbqkbnr/2pppppp/1p6/p7/P7/1P4P1/2PPPP1P/RNBQKBNR b qkQK -
rnbqkbnr/2pppppp/1p6/p7/P5P1/1P6/2PPPP1P/RNBQKBNR b qkQK -
rnbqkbnr/2pppppp/1p6/p7/P7/1P5P/2PPPPP1/RNBQKBNR b qkQK -
rnbqkbnr/2pppppp/1p6/p7/P6P/1P6/2PPPPP1/RNBQKBNR b qkQK -
rnbqkbnr/2pppppp/1p6/p7/P7/NP6/2PPPPPP/R1BQKBNR b qkQK -
rnbqkbnr/2pppppp/1p6/p7/P7/1PN5/2PPPPPP/R1BQKBNR b qkQK -
rnbqkbnr/2pppppp/1p6/p7/P7/1P3N2/2PPPPPP/RNBQKB1R b qkQK -
rnbqkbnr/2pppppp/1p6/p7/P7/1P5N/2PPPPPP/RNBQKB1R b qkQK -
rnbqkbnr/2pppppp/1p6/p7/P7/1P6/1BPPPPPP/RN1QKBNR b qkQK -
rnbqkbnr/2pppppp/1p6/p7/P7/BP6/2PPPPPP/RN1QKBNR b qkQK -
rnbqkbnr/2pppppp/1p6/p7/P7/1P6/R1PPPPPP/1NBQKBNR b qkK -


cat go3.sh
wc -l < all_rows.txt
./go3.sh
3195901860       (ilość wierszy perfekcyjna, tyle ile węzłów na głębokości 7 ruchów)
real    105m41.485s (niecałe 2h na samo zliczenie wierszy)
user    2m9.580s
sys     4m7.959s


krzyszp


Fajne zegarki :)
Należę do drużyny BOINC@Poland
 Moja wizytówka

mariotti

Cytat: krzyszp w 17 Czerwiec 2013, 19:44
Odpaliłem
Dzięki.

P.S.
BOINC uzależnia, obiecałem sobie szlaban na kilka dni, a wciąż się tym zajmuję :D

krzyszp

Cytat: mariotti w 17 Czerwiec 2013, 19:46
BOINC uzależnia, obiecałem sobie szlaban na kilka dni, a wciąż się tym zajmuję :D
oj prawda :)
Ja na próbę włączyłem i już 12-13 lat siedzę w temacie ;)

Fajne zegarki :)
Należę do drużyny BOINC@Poland
 Moja wizytówka

mariotti

Cytat: krzyszp w 17 Czerwiec 2013, 21:24
Cytat: mariotti w 17 Czerwiec 2013, 19:46
BOINC uzależnia, obiecałem sobie szlaban na kilka dni, a wciąż się tym zajmuję :D
oj prawda :)
Ja na próbę włączyłem i już 12-13 lat siedzę w temacie ;)
Wow!

Myślę podświadomie cały czas o tym algorytmie do optymalizacji WU.
Algorytm koncepcyjnie jest całkiem prosty, jednak w realizacji będzie
kilka pułapek, więc na szybko go nie zrobię ale za parę dni tak.
Obliczenia na komputerach z taśmami magnetycznymi od jakiegoś
czasu wyszły z mody, ale gdy danych jest dużo, to trzeba wrócić do
korzeni :D

No chyba że ktoś czytający ten wątek rozwiąże problem zanim ja
się wezmę do roboty :) Teoretycznie można go rozwiązać przy
pomocy dwóch poleceń powłoki sort i jednego uniq. W praktyce
nie wiem ile to zajmie czasu.  Wiem na pewno, że dla depth=8
może zająć 8TB miejsca na dysku i następne 8-16TB na pliki
tymczasowe :/

Pozdrawiam

krzyszp

Cytat: mariotti w 17 Czerwiec 2013, 19:46
Odpaliłem
Zakończył się.
W wyniku mam plik all_rows.txt 94.1GB
Spakować i wystawić na ftp'a?


Fajne zegarki :)
Należę do drużyny BOINC@Poland
 Moja wizytówka

mariotti

Cytat: krzyszp w 17 Czerwiec 2013, 21:40
Zakończył się.
W wyniku mam plik all_rows.txt 94.1GB
Spakować i wystawić na ftp'a?
Oj nie, to zaledwie początek zadania.
Teraz trzeba usunąć powtarzające się wiersze. Przy każdym wierszu trzeba
dopisać ile razy się ten wiersz powtarza. W dodatku kolejność wierszy musi
być zachowana.

Można to zrealizować np. tak:
1) Do każdego wiersza dodać numer kolejny (np. po średniku).
2) Dane posortować po tym co jest przed średnikiem
3) Przy pomocy komendy uniq usunąć duplikaty
4) zapamiętać ilość duplikatów (np. po drugim średniku)
5) Posortować po numerze kolejnym
6) Wywalić numer kolejny z wierszy

Wiem że to jest całkiem żmudne zadanie, jeśli nie jesteś pasjonatem
obróbki dużych plików tekstowych, to zostaw. Ja to dokończę za jakiś czas.
Dane u mnie też się ładnie generują, problem jest z dalszą obróbką.

Pozdrawiam

mariotti

Cytat: krzyszp w 17 Czerwiec 2013, 21:40
Cytat: mariotti w 17 Czerwiec 2013, 19:46
Odpaliłem
Zakończył się.
W wyniku mam plik all_rows.txt 94.1GB
Spakować i wystawić na ftp'a?

A jeszcze jedno, czy program nie wywalił się w połowie, albo nie
zabrakło miejsca na dysku? Nie pasuje mi rozmiar pliku. U mnie
dla depth=7 plik ma rozmiar 195013386079bajtów i zawiera 3195901860 wierszy.

Pozdrawiam


krzyszp

A nie wiem, zaraz sprawdzę, tylko mam mały problem :)

W międzyczasie napisałem sobie program w VB który czyta plik po linii o zapisuje dane do bazy (tak jak Ty to napisałeś w c, ale ja w VB)... Ale z rozpędu podałem mu plik przez sieć i teraz mam zagwozdkę, co z tym fantem zrobić XD

Co do miejsca, to możliwe że tak, dopiero zauważyłem, że mam zajęte 100% na tej partycji, już odpalam tam, gdzie miejsca więcej :)

Fajne zegarki :)
Należę do drużyny BOINC@Poland
 Moja wizytówka

krzyszp

#275
Zapomniałem dopisać - oczywiście, programik też wyszukuje duplikaty i je zlicza.

Open "\\Serwerek\all_rows.txt" For Input As #1
    Do While Not EOF(1)
        i = i + 1
        Me.txtLinia.Text = "Linia: " & i
        Line Input #1, strTemp
        sSQL = "SELECT * FROM dane WHERE line = '" & strTemp & "'"
        rsS.OpenRs sSQL, cnM
        If rsS.RecordCount = 0 Then
            rsS.AddNew
            rsS.Fields("line") = strTemp
            rsS.Fields("repeat") = 0
        Else
            rsS.Fields("repeat") = CInt(rsS.Fields("repeat")) + 1
        End If
        rsS.Update
        rsS.CloseRecordset
    Loop
   
  Close #1

Fajne zegarki :)
Należę do drużyny BOINC@Poland
 Moja wizytówka

mariotti

Cytat: krzyszp w 17 Czerwiec 2013, 22:38
Zapomniałem dopisać - oczywiście, programik też wyszukuje duplikaty i je zlicza.

I jak idzie? :D


Ja niby napisałem w bashu, ale pozostał jakiś błąd :/

file2='tmp.txt'
file1='out.txt'

(
echo printLeafs 4
echo quit
) | ./perft4 > $file1

gawk '{printf "%s;%s\n",$0,NR}' $file1 > $file2

cat $file2 | sort -k1 -t';' --stable > $file1

gawk -F';' '{ if(field==$1){sum++} else { if(NR!=1) {printf "%s;%d\n",row,sum} sum=1; row=$0; field=$1 } } END{ printf "%s;%d\n",$0,sum}' $file1 | sort -k2 -n -t';' > $file2

gawk -F';' '{printf "%s;%s\n",$1,$3}' $file2 > $file1


rm $file2
head -20 $file1
wc -l $file1


Poprawny wynik to 72078, a u mnie wyświetla 72090. Chyba tego nie da się
zrobić na przysłowiowym kolanie, trzeba przysiąść porządnie i użyć porządnych
narzędzi.

Pozdrawiam

krzyszp

Cytat: mariotti w 17 Czerwiec 2013, 23:56
Cytat: krzyszp w 17 Czerwiec 2013, 22:38
Zapomniałem dopisać - oczywiście, programik też wyszukuje duplikaty i je zlicza.
I jak idzie? :D
powoli :)

Generują się jeszcze dane, potem muszę przesłać plik na dysk lokalny (co chwilę potrwa) i dopiero potem rozpocznę import.

Zastanawia mnie, jaki jest maksymalny rozmiar linii (znaków)? Przydałoby się, aby ograniczyć rozmiar bazy bo i tak obawiam się, że przy 800GB wolnego miejsca na partycji może pojawić się problem (nie mam pojęcia, ile powtórzeń odpadnie)...

Fajne zegarki :)
Należę do drużyny BOINC@Poland
 Moja wizytówka

mariotti

Cytat: krzyszp w 18 Czerwiec 2013, 00:01
powoli :)
Jeszcze jest opcja, żeby ten plik basha poprawić, a następnie dobrze sparametryzować
polecenie sort - może zadziała w miarę szybko. Niestety ja nie umiem, nie mam
zbyt dużego doświadczenia w bashu.

Cytat: krzyszp w 18 Czerwiec 2013, 00:01
Generują się jeszcze dane, potem muszę przesłać plik na dysk lokalny (co chwilę potrwa) i dopiero potem rozpocznę import.
Sporo z tym zabawy... podejrzewam że i tak będzie trzeba napisać specjalistyczny program :/


Cytat: krzyszp w 18 Czerwiec 2013, 00:01
Zastanawia mnie, jaki jest maksymalny rozmiar linii (znaków)?
Założyłem że rozmiar linii maksymalnie wynosi 70 znaków, hmmm może to błędne założenie :/
Pod linuxem można sprawdzić takim poleceniem:
awk ' { if ( length > x ) { x = length; y = $0 } }END{ printf "%s:lenght:%d\n",y,x }' nazwa_pliku
Sprawdzenie trochę to potrwa :/
Można do awk przekierować wiersze prosto z programu - powinno znacznie przyspieszyć.


Cytat: krzyszp w 18 Czerwiec 2013, 00:01
Przydałoby się, aby ograniczyć rozmiar bazy bo i tak obawiam się, że przy 800GB wolnego miejsca na partycji może pojawić się problem (nie mam pojęcia, ile powtórzeń odpadnie)...
Według danych jakie posiadam będzie 96400068 unikalnych wierszy. 50GB powinno wystarczyć
łącznie z indeksami.


Pozdrawiam

krzyszp

No niestety, ja też jestem wyrobnik - w zasadzie VB6 i .NET, trochę (mało) PHP i to w zasadzie wszystko...

Niemniej, jak się dane wygenerują i zaimportują do bazy, to polowa za nami.
Też przyjąłem, że max 70 znaków (sprawdzone kawałki mieszczą się w 60), więc powinno być dobrze.

Jak chcesz, to po całej operacji dam Ci dostęp do gotowej bazy, żeby nie przerabiać znowu tego samego...

Fajne zegarki :)
Należę do drużyny BOINC@Poland
 Moja wizytówka