c++ кроссплатформенное простенькое приложение

PooH

пишу небольшую утилитку на с++ - она считает контрольную сумму файлов
соот-но, пока я работал с одним файлом (путь до файла передается в параметрах приложения, приложение консольное), удавалось использовать стандартную библиотеку с++
сейчас необходимо, чтобы утилитка сама могла искать файлы и директории в заданной директории
вот тут, как я понимаю, и возникнут сложности с кроссплатформенностью (она нужна, т.к. утилитка будет запускаться как в windows (xp, 7), так и в freeBSD, debian, gentoo)
сейчас качаю и собираю boost - он же умеет кросплатформенность?
PS доп вопрос - многопоточное приложение хорошо переносится?

Maurog

сейчас качаю и собираю boost - он же умеет кросплатформенность?
PS доп вопрос - многопоточное приложение хорошо переносится?
boost поддерживает много платформ
многопоточное переносится так же хреново, как перебор файлов в директории
в любом случае попробуйте boost: там есть boost::filesystem, boost::thread, boost::mutex и еще 100500 ненужных фишек :grin:

elenangel

многопоточное приложение хорошо переносится?
по идее если использовать стандарт c++11 то хорошо - там в стандартной либе есть thread, mutex, conditionvariable и т.д.

erotic

Там по-моему пока не все хорошо работает. По крайней мере в gcc. regex мне не удалось заставить нормально работать в 4.6.1, пришлось бустовые взять. Мож я не особо профессионально их ковырял, конечно.

Werdna

сейчас качаю и собираю boost - он же умеет кросплатформенность?
Зачем в твоём приложении буст?
Если ты первый раз увидишь буст, то ничего хорошего это тебе не даст. Буст — огромная свалка разных штук, какие-то хорошие, какие-то отвратительные. Скачивать буст надо только когда ты точно знаешь, что тебе понадобилась конкретная штука и что она действительно нужна, а не потому что это круто использовать буст.
Вместо этого лучше подумай, как реализовать многопоточное приложение, чтобы оно под виндой работало. Сложности с другими ОС быть не должно, если использовать pthread.

ava3443

Вместо этого лучше подумай, как реализовать многопоточное приложение, чтобы оно под виндой работало.
Не вижу ни одной причины "свежему" человеку изучать виндовый API для работы с потоками вместо boost::thread.

PooH

пока реализовал вот так (кусочек программы, непосредственно отвечающий за расчеты CRC32 для файла)
сихронный:
 
crc::result crc::crc32::checksumm() {
unsigned int t = clock();
f.exceptions(ifstream::badbit);

do {
try {
f.read(buffer, buff_size);
} catch(ifstream::failure e) {
if(!f.eof()) {
wstringstream reason;
reason << L"Exception: " << e.what();
wcout << reason;
throw crc::exception(reason.str());
}
}
bytes += f.gcount();
cs = CRC32Ex(buffer, f.gcount(), cs);
} while(f.good());

t = clock() - t;

result.bytes = bytes;
result.cs = cs;
result.ticks = (double)t;
result.MBs = ((float)result.bytes) / 1024 / 1024 / result.ticks * CLOCKS_PER_SEC;

return result;
}

асинхронный:
 
crc::result crc::crc32a::checksumm()
{
unsigned int t = clock();

OVERLAPPED over = {0};
BOOL readDone = FALSE;
char dataBuffer[buff_size];

DWORD err;
DWORD lastStepBytesRead = 0;
DWORD stepBytesRead = 0;
bool eof = false;

while(!eof)
{
readDone = ReadFile(hFile, buffer, buff_size, &stepBytesRead, &over);

if(!readDone)
{
switch(err = GetLastError())
{
case ERROR_HANDLE_EOF:
{
eof = true;
break;
}
case ERROR_IO_PENDING:
{
cs = CRC32Ex(dataBuffer, lastStepBytesRead, cs);
WaitForSingleObject(hFile, INFINITE);
err = GetOverlappedResult(hFile, &over, &stepBytesRead, FALSE);
lastStepBytesRead = stepBytesRead;
if(stepBytesRead < buff_size) {
eof = true;
break;
}
memcpy(dataBuffer, buffer, buff_size);
if(!err)
switch(err) {
case ERROR_HANDLE_EOF:
{
eof = true;
break;
}
case ERROR_IO_INCOMPLETE:
{
break;
}
default:
{
wstringstream reason;
reason << "Error read file near " << stepBytesRead << " byte, error: " << GetLastError();
using crc::exception;
throw exception(reason.str());
}
}

break;
}
default:
{
wstringstream reason;
reason << "Error read file near " << stepBytesRead << " byte, error: " << GetLastError();
using crc::exception;
throw exception(reason.str());
}

}
} else {
eof = true;
}

over.Offset += stepBytesRead;
bytes += stepBytesRead;
}

cs = CRC32Ex(buffer, stepBytesRead, cs);

result.bytes = bytes;
result.cs = cs;
result.ticks = (double)(clock() - t);
result.MBs = ((double)result.bytes) / 1024 / 1024 / result.ticks * CLOCKS_PER_SEC;

return result;
}

но я не понимаю - асинхронный работает так же или чуть быстрее - проверял на папке игры ~2.5gb, ~500 файловэ
синхронный ~35 сек
асинхронный ~34 сек
хотя вроде судя по профайлеру расчеты CRC32Ex занимают около 20% времени от всего времени выполнения, т.е. прирост скорости должен был быть более заметным - 4-8 секунд
кто хорошо разбирается в этом - скажите, мож чего напутал

Maurog

WaitForSingleObject(hFile, INFINITE);
вот тут ваша асинхронность умерла, превратившись в синхронный подход
получить выигрыш от асинхронности можно в том случае, если в то время, как OS вычитывает какие-то файлы для вас, программа попутно делает что-то полезное
в вашем случае, можно ее заставить считать crc, но опять же, если это делается очень быстро (относительно скорости чтения файлов), то все время будет тратиться на IO и тут разницу между синх\асинхр подходом вы не заметите
ну и кстати, для асинхронности в бусте сделали boost::asio
успехов
зы: впрочем, перед WaitForSingleObject стоит подсчет CRC, так что, возможно, я ошибся с убийством асинхронности

PooH

crc32 считается примерно в течении 20-30% скорости чтения файлов
UPD проверил - вообще без расчетов файлы обрабатываются за 33 секунды в среднем
походу, уже некуда оптимизировать - все упирается в скорость чтения
какие есть быстрые способы чтения файлов? файлы читаются последовательно, кусок за куском
размер файлов варьируется - от 10кб до 500мб или 2гб

margadon

мапинг файла в память?

Maurog

какие есть быстрые способы чтения файлов?
на ум приходят следующие:
1) врубить буферизацию, размер буфера подобрать с умом
2) рейд (тут можно в несколько потоков дрючить разные диски)
3) быстрые носители

Maurog

мапинг файла в память?
за счет чего здесь может ускориться чтение?

PooH

1) врубить буферизацию, размер буфера подобрать с умом
2) рейд (тут можно в несколько потоков дрючить разные диски)
3) быстрые носители
1) везде советуют оную буферизацию выключать - т.к. по сути организуется собственный буфер, 2) нету возможности контролировать платформу на которой будет запускаться приложение (т.е. приложение пишется не под себя)
3) аналогично
у меня вообще ощущение, что асинхронность не работает
 
void crc::crc32a::init(unsigned __int32 start, wstring filename) {
buffer = new char[buff_size];
cs = start;
bytes = 0;

result.filename = filename;

hFile = CreateFile(filename.c_str(),
GENERIC_READ, 0, NULL, OPEN_EXISTING,
FILE_FLAG_NO_BUFFERING|FILE_FLAG_OVERLAPPED, NULL);

if(hFile == INVALID_HANDLE_VALUE) {
wstringstream reason;
reason << L"Can't open file " << filename << L". Reason: " << GetLastError();
using crc::exception;
throw exception(reason.str());
}
}

margadon

скорее, оно *не замедлится* из-за возможных глупостей с выбором неправильного размера буфера итд
упд: отсутствие переключения в ядро и назад при каждом чтении блока
упд2: отпадает необходимость копирования данных в юзер-буфер вообще

PooH

мапинг файла в память?
куда файл мапится? в кучу?
какой файл можно будет замапить на 32bit системах?
ну т.е. расскажи, какие +/- у подхода

Maurog

скорее, оно *не замедлится* из-за возможных глупостей с выбором неправильного размера буфера итд
то бишь существуют глупые буфера, а есть умные. вопрос лишь как подобрать
упд: отсутствие переключения в ядро и назад при каждом чтении блока
что такое проблема переключений из юзер мода в кернел по сравнению с кернел_левел маппингом файла? имхо копейки
упд2: отпадает необходимость копирования данных в юзер-буфер вообще
данные все равно копируются в виртуальную память процесса. вопрос лишь в том, кто это будет делать: либо заставим OS, либо будем это велосипедить в приложении
имхо данный подход эквивалентен просто ReadFile(buffer_of_file_size) в плане скорости чтения

Serab

Fj_ же создавал тред, где маппинг был медленнее :)
Вообще, преимущества маппинга в автоматическом кешировании при произвольном доступе, может еще в чем-то, но не в перечисленном 'ом, имхо.

Maurog

1) везде советуют оную буферизацию выключать - т.к. по сути организуется собственный буфер
можно ссылочку? несмотря на буфера в OS и дисковые кеши буфера на уровне приложения обычно дают прирост производительности

okunek

упд: отсутствие переключения в ядро и назад при каждом чтении блока
Скорее всего, в первый раз чтения из замапленной страницы, выполнится прерывание, на тему, что там ничего еще нет и управление также уйдет в ведро и там произойдет вычитывание страницы из файла. В итоге шило на мыло.

Maurog

преимущества маппинга в автоматическом кешировании при произвольном доступе
согласен, у нас с этой помощью реализованы "гарантированные доставки" с хорошим перформансом. коррапты возможны, но мы готовы пожертвовать ради скорости

margadon

ну ясно дело, что есть трейдоф между стоимостью вызова мапа и выигрышем (?) от чтения из файла таким образом, и можно придумать ситуацию, где мап будет сосать
но всё равно, когда делается мэп, не происходит ПОЛНОЕ копирование в юзерспейсный массив - можно представить ситуацию, когда бОльшая часть диска УЖЕ есть в кеше (а ведь на него-то и будет выдан указатель map-а, точнее, из его страниц скорее всего и будет составлен тот массив, который выдадут юзеру)

Serab

бОльшая часть диска УЖЕ есть в кеше
это как так-то o_O

Serab

так вот и получается, что если мы все равно собираемся читать все, то выигрыш с map'ом становится сильно менее вероятным.

margadon

извиняюсь, опечатался. бОльшая часть файла может быть закэширована, и тогда мы в явном виде экономим на пересылке всего этого файла в юзерспейс-буфер.
и да, я и не говорил, что map - это универсальная magic bullet, просто это занятный способ, в некоторых задачах повышающий производительность ввода-вывода, который можно потестить в конкретном кейсе

PooH

переписал на двухпоточность через буст
 

crc::result crc::crc32ba::checksumm()
{
unsigned int t = clock();
cs = 0;
bytesRead = 0;
lastBytesRead = 0;
data_ready = false;
read_complete = false;

boost::thread reader(boost::bind(&crc::crc32ba::read, this), &reading);
boost::thread calcer(boost::bind(&crc::crc32ba::calc, this), &reading);

reader.join();
calcer.join();

result.bytes = bytes;
result.cs = cs;
result.ticks = (double)(clock() - t);
result.MBs = ((double)result.bytes) / 1024 / 1024 / result.ticks * CLOCKS_PER_SEC;

return result;
}

void crc::crc32ba::read() {
do {
{
boost::unique_lock<boost::mutex> l(mutex);
while(data_ready) {wcout << "reader: waiting..." << endl;reading.timed_wait(l,boost::posix_time::milliseconds(1));}
wcout << "reader: reading bytes from " << bytes << " to " << bytes + buff_size << "..." << endl;
try {
f.read(rawbuffer, buff_size);
} catch(ifstream::failure e) {
if(!f.eof()) {
wstringstream reason;
reason << L"Exception: " << e.what();
wcout << reason;
throw crc::exception(reason.str());
}
}
bytesRead = f.gcount();
bytes += bytesRead;
data_ready = true;
wcout << "calc: data_ready to true" << endl;
wcout << "reader: " << bytesRead << " bytes read, total: " << bytes << " bytes read" << endl;
}
wcout << "reader notify" << endl;
reading.notify_all();
if(f.good()) wcout << "reader good" << endl;
} while(f.good());
read_complete = true;
}

void crc::crc32ba::calc() {
do {
{
boost::unique_lock<boost::mutex> l(mutex);
while(!data_ready) {wcout << "calc: waiting..." << endl;reading.timed_wait(l,boost::posix_time::milliseconds(1));}
wcout << "calc: copying buffer..." << endl;
lastBytesRead = bytesRead;
memcpy(buffer, rawbuffer, lastBytesRead);
data_ready = false;
wcout << "calc: data_ready to false" << endl;
wcout << "calc: buffer copied... (" << lastBytesRead << ")" << endl;
}
wcout << "calc notify" << endl;
reading.notify_all();
wcout << "calc: calculating crc..." << endl;
cs = CRC32Ex(buffer, lastBytesRead, cs);
wcout << "calc: calculating crc ended" << endl;
} while(!read_complete);
}


судя по всему код работает быстрее, чем предыдущие варианты, причем существенно
другое дело, что он "зависает" - т.е. в какой-то случайный момент программа перестает работать, а в консоли последняя строчка "calc waiting..."
очевидно, что проблема в синхронизации, но никак не могу поймать
без вывода в консоль программа стабильно "зависает", с выводом - может и вычислить файлы

elenangel

c++ кроссплатформенное простенькое приложение

_tmain does not exist in C++. main does.
_tmain is a Microsoft extension.

http://stackoverflow.com/questions/895827/what-is-the-differ...

elenangel

 


reader.join();
calcer.join();

вызов checksumm() получается синхронный с тредами ридера и калькера, так как внутри стоят два последовательных join. reader и calcer тоже выполняются строго один после другого так-как стоит синхронизация об unique_lock(mutex).
таким образом, это не многопоточная программа, в каждый конкретный момент работает один поток, а остальные его ждут в unique_lock или в join().

виноват, ошибся, сразу не увидел внутри condition_variable. таки двухпоточная.

kokoc88

другое дело, что он "зависает" - т.е. в какой-то случайный момент программа перестает работать, а в консоли последняя строчка "calc waiting..."
К примитивным типам нельзя обращаться из разных потоков без синхронизации.
Если есть обращения к примитивным типам из разных потоков, то они должны быть объявлены как volatile.
while(!data_ready && !read_complete)

elenangel


boost::thread reader(boost::bind(&crc::crc32ba::read, this), &reading);

я не могу понять, зачем ты передаешь &reading, если функция read() не ожидает параметров, аналогично и calc()

PooH

я не могу понять, зачем ты передаешь &reading, если функция read() не ожидает параметров, аналогично и calc()
reading - condition_variable, я так понимаю ,что ее _надо_ передавать
посмотрел - нету такого конструктора :)

PooH

Если есть обращения к примитивным типам из разных потоков, то они должны быть объявлены как volatile.
while(!data_ready && !read_complete)
volatile - я так понимаю, это обращение к компилятору, что не стоит делать какие-либо оптимизации с этой переменной?
while(!data_ready && !read_complete) - не понял
data_ready - true, когда кусок файла прочитался в буфер и его можно просчитывать
read_complete - true, когда _последний_ кусок файла прочитался в буфер, т.е. этот последний кусок надо еще просчитать
не совсем понимаю, зачем нужно дополнительное условие && !read_complete

PooH

 
В ответ на:
c++ кроссплатформенное простенькое приложение
  
 
В ответ на:
_tmain does not exist in C++. main does.
_tmain is a Microsoft extension.
http://stackoverflow.com/questions/895827/what-is-the-differ...
не придирайся - это не принципиальное место - я с юникодом еще не разбирался вплотную
сейчас задача - заставить приложение работать в венде, при этом постараться использовать как можно меньше непереносимых вещей

kokoc88

volatile - я так понимаю, это обращение к компилятору, что не стоит делать какие-либо оптимизации с этой переменной?
Значение этого ключевого слова - мистика, которая зависит от платформы и компилятора.
Лучше использовать другие способы доступа к данным, которые используются в разных потоках.
не совсем понимаю, зачем нужно дополнительное условие && !read_complete
С ним всё ещё повисает?

PooH

запустил - пока жду, среднее время прогонки на тестовом каталоге - 500секунд

PooH

Лучше использовать другие способы доступа к данным, которые используются в разных потоках.
подскажи какие?
я правильно понимаю концепцию локов в бусте - ставим лок и в ближайшем куске кода ({...}) можем спокойно работать с пошаренными ресурсами, при этом другие потоки будут ждать снятия лока?

kokoc88

запустил - пока жду, среднее время прогонки на тестовом каталоге - 500секунд
Сделай себе маленький тестовый каталог, иначе задолбаешься дебажить.

kokoc88

я правильно понимаю концепцию локов в бусте - ставим лок и в ближайшем куске кода ({...}) можем спокойно работать с пошаренными ресурсами, при этом другие потоки будут ждать снятия лока?
Это отдельная и сложная тема, в которую входят CAS-ы, memory barrier-ы и так далее.
Начинающим в этом деле всегда лучше использовать простые локи; и иногда read/write локи.

PooH

Сделай себе маленький тестовый каталог, иначе задолбаешься дебажить.
как раз-таки проблема и вылазит раз от разу - я как-то запускал - иногда по 5-6 раз спокойно до конца работало, поэтому так и гоняю

oliver11

Если есть обращения к примитивным типам из разных потоков, то они должны быть объявлены как volatile.
Как atomic. volatile вообще из другой степи. Возможно, ты с джавой путаешь.

PooH

atomic
в моей книжке Страуструпа такого нет :)

kokoc88

Как atomic. volatile вообще из другой степи. Возможно, ты с джавой путаешь.
Я не знаю C++0X
Ключевое слово "volatile" не из другой степи, просто его одного недостаточно, поэтому я написал, что нельзя обращаться без синхронизации. Если ты сложишь это вместе, то скорее всего получишь те гарантии, которые есть у atomic в новом стандарте.

PooH

короче, я потестировал - пока результат: выигрыш в 1-2 секунды по сравнению с асинхронной версией (из 35секунд), т.е. укладывается в погрешность - прогнозирую 1 секунду выигрыша в среднем
при этом версия на бусте в каких-то файлах контрольную сумму считает не так, как другие версии - т.е. еще разбираться надо с багами в логике

oliver11

нельзя обращаться без синхронизации
Вот это неверно. Ну или смотря что ты под синхронизацией понимаешь.

kokoc88

Вот это неверно. Ну или смотря что ты под синхронизацией понимаешь.
Это верно. А что ты понимаешь под синхронизацией?

oliver11

Мьютексы и сигналы понимаю. Атомики — нет.

kokoc88

Атомики — нет.
Вот это неверно. :grin: Твои "атомики" почти наверняка делают синхронизацию памяти: acquire, release или обе. Ты же скорее написал разницу между blocking и non blocking.

elenangel

 
#!/bin/sh
if [ -e filelist ]; then
rm filelist
fi

mkfifo filelist
find ./ > filelist &

while read filename1;
do
if [ -f $filename1 ]
then
echo 1 $(crc32 $filename1)
fi
done < filelist &

while read filename1;
do
if [ -f $filename1 ]
then
echo 2 $(crc32 $filename1)
fi
done < filelist &

даже лучше так:

#!/bin/sh
if [ -e filelist ]; then
rm filelist
fi

mkfifo filelist
find ./ > filelist &

for i in $(seq 1 1 2); do
while read filename;
do
if [ -f $filename ]
then
echo $i $(crc32 $filename)
fi
done < filelist &
done
Оставить комментарий
Имя или ник:
Комментарий: