c++ кроссплатформенное простенькое приложение
сейчас качаю и собираю boost - он же умеет кросплатформенность?boost поддерживает много платформ
PS доп вопрос - многопоточное приложение хорошо переносится?
многопоточное переносится так же хреново, как перебор файлов в директории
в любом случае попробуйте boost: там есть boost::filesystem, boost::thread, boost::mutex и еще 100500 ненужных фишек
многопоточное приложение хорошо переносится?по идее если использовать стандарт c++11 то хорошо - там в стандартной либе есть thread, mutex, conditionvariable и т.д.
Там по-моему пока не все хорошо работает. По крайней мере в gcc. regex мне не удалось заставить нормально работать в 4.6.1, пришлось бустовые взять. Мож я не особо профессионально их ковырял, конечно.
сейчас качаю и собираю boost - он же умеет кросплатформенность?Зачем в твоём приложении буст?
Если ты первый раз увидишь буст, то ничего хорошего это тебе не даст. Буст — огромная свалка разных штук, какие-то хорошие, какие-то отвратительные. Скачивать буст надо только когда ты точно знаешь, что тебе понадобилась конкретная штука и что она действительно нужна, а не потому что это круто использовать буст.
Вместо этого лучше подумай, как реализовать многопоточное приложение, чтобы оно под виндой работало. Сложности с другими ОС быть не должно, если использовать pthread.
Вместо этого лучше подумай, как реализовать многопоточное приложение, чтобы оно под виндой работало.Не вижу ни одной причины "свежему" человеку изучать виндовый API для работы с потоками вместо boost::thread.
сихронный:
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 секунд
кто хорошо разбирается в этом - скажите, мож чего напутал
WaitForSingleObject(hFile, INFINITE);вот тут ваша асинхронность умерла, превратившись в синхронный подход
получить выигрыш от асинхронности можно в том случае, если в то время, как OS вычитывает какие-то файлы для вас, программа попутно делает что-то полезное
в вашем случае, можно ее заставить считать crc, но опять же, если это делается очень быстро (относительно скорости чтения файлов), то все время будет тратиться на IO и тут разницу между синх\асинхр подходом вы не заметите
ну и кстати, для асинхронности в бусте сделали boost::asio
успехов
зы: впрочем, перед WaitForSingleObject стоит подсчет CRC, так что, возможно, я ошибся с убийством асинхронности
UPD проверил - вообще без расчетов файлы обрабатываются за 33 секунды в среднем
походу, уже некуда оптимизировать - все упирается в скорость чтения
какие есть быстрые способы чтения файлов? файлы читаются последовательно, кусок за куском
размер файлов варьируется - от 10кб до 500мб или 2гб
мапинг файла в память?
какие есть быстрые способы чтения файлов?на ум приходят следующие:
1) врубить буферизацию, размер буфера подобрать с умом
2) рейд (тут можно в несколько потоков дрючить разные диски)
3) быстрые носители
мапинг файла в память?за счет чего здесь может ускориться чтение?
1) врубить буферизацию, размер буфера подобрать с умом1) везде советуют оную буферизацию выключать - т.к. по сути организуется собственный буфер, 2) нету возможности контролировать платформу на которой будет запускаться приложение (т.е. приложение пишется не под себя)
2) рейд (тут можно в несколько потоков дрючить разные диски)
3) быстрые носители
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());
}
}
упд: отсутствие переключения в ядро и назад при каждом чтении блока
упд2: отпадает необходимость копирования данных в юзер-буфер вообще
мапинг файла в память?куда файл мапится? в кучу?
какой файл можно будет замапить на 32bit системах?
ну т.е. расскажи, какие +/- у подхода
скорее, оно *не замедлится* из-за возможных глупостей с выбором неправильного размера буфера итдто бишь существуют глупые буфера, а есть умные. вопрос лишь как подобрать
упд: отсутствие переключения в ядро и назад при каждом чтении блокачто такое проблема переключений из юзер мода в кернел по сравнению с кернел_левел маппингом файла? имхо копейки
упд2: отпадает необходимость копирования данных в юзер-буфер вообщеданные все равно копируются в виртуальную память процесса. вопрос лишь в том, кто это будет делать: либо заставим OS, либо будем это велосипедить в приложении
имхо данный подход эквивалентен просто ReadFile(buffer_of_file_size) в плане скорости чтения
Вообще, преимущества маппинга в автоматическом кешировании при произвольном доступе, может еще в чем-то, но не в перечисленном 'ом, имхо.
1) везде советуют оную буферизацию выключать - т.к. по сути организуется собственный буферможно ссылочку? несмотря на буфера в OS и дисковые кеши буфера на уровне приложения обычно дают прирост производительности
упд: отсутствие переключения в ядро и назад при каждом чтении блокаСкорее всего, в первый раз чтения из замапленной страницы, выполнится прерывание, на тему, что там ничего еще нет и управление также уйдет в ведро и там произойдет вычитывание страницы из файла. В итоге шило на мыло.
преимущества маппинга в автоматическом кешировании при произвольном доступесогласен, у нас с этой помощью реализованы "гарантированные доставки" с хорошим перформансом. коррапты возможны, но мы готовы пожертвовать ради скорости
но всё равно, когда делается мэп, не происходит ПОЛНОЕ копирование в юзерспейсный массив - можно представить ситуацию, когда бОльшая часть диска УЖЕ есть в кеше (а ведь на него-то и будет выдан указатель map-а, точнее, из его страниц скорее всего и будет составлен тот массив, который выдадут юзеру)
бОльшая часть диска УЖЕ есть в кешеэто как так-то o_O
так вот и получается, что если мы все равно собираемся читать все, то выигрыш с map'ом становится сильно менее вероятным.
и да, я и не говорил, что map - это универсальная magic bullet, просто это занятный способ, в некоторых задачах повышающий производительность ввода-вывода, который можно потестить в конкретном кейсе
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..."
очевидно, что проблема в синхронизации, но никак не могу поймать
без вывода в консоль программа стабильно "зависает", с выводом - может и вычислить файлы
c++ кроссплатформенное простенькое приложение
_tmain does not exist in C++. main does.
_tmain is a Microsoft extension.
http://stackoverflow.com/questions/895827/what-is-the-differ...
reader.join();
calcer.join();
вызов checksumm() получается синхронный с тредами ридера и калькера, так как внутри стоят два последовательных join.
таким образом, это не многопоточная программа, в каждый конкретный момент работает один поток, а остальные его ждут в unique_lock или в join().
виноват, ошибся, сразу не увидел внутри condition_variable. таки двухпоточная.
другое дело, что он "зависает" - т.е. в какой-то случайный момент программа перестает работать, а в консоли последняя строчка "calc waiting..."К примитивным типам нельзя обращаться из разных потоков без синхронизации.
Если есть обращения к примитивным типам из разных потоков, то они должны быть объявлены как volatile.
while(!data_ready && !read_complete)
boost::thread reader(boost::bind(&crc::crc32ba::read, this), &reading);
я не могу понять, зачем ты передаешь &reading, если функция read() не ожидает параметров, аналогично и calc()
я не могу понять, зачем ты передаешь &reading, если функция read() не ожидает параметров, аналогично и calc()
посмотрел - нету такого конструктора
Если есть обращения к примитивным типам из разных потоков, то они должны быть объявлены как volatile.volatile - я так понимаю, это обращение к компилятору, что не стоит делать какие-либо оптимизации с этой переменной?
while(!data_ready && !read_complete)
while(!data_ready && !read_complete) - не понял
data_ready - true, когда кусок файла прочитался в буфер и его можно просчитывать
read_complete - true, когда _последний_ кусок файла прочитался в буфер, т.е. этот последний кусок надо еще просчитать
не совсем понимаю, зачем нужно дополнительное условие && !read_complete
не придирайся - это не принципиальное место - я с юникодом еще не разбирался вплотную
В ответ на:
c++ кроссплатформенное простенькое приложение
В ответ на:
_tmain does not exist in C++. main does.
_tmain is a Microsoft extension.
http://stackoverflow.com/questions/895827/what-is-the-differ...
сейчас задача - заставить приложение работать в венде, при этом постараться использовать как можно меньше непереносимых вещей
volatile - я так понимаю, это обращение к компилятору, что не стоит делать какие-либо оптимизации с этой переменной?Значение этого ключевого слова - мистика, которая зависит от платформы и компилятора.
Лучше использовать другие способы доступа к данным, которые используются в разных потоках.
не совсем понимаю, зачем нужно дополнительное условие && !read_completeС ним всё ещё повисает?
запустил - пока жду, среднее время прогонки на тестовом каталоге - 500секунд
Лучше использовать другие способы доступа к данным, которые используются в разных потоках.подскажи какие?
я правильно понимаю концепцию локов в бусте - ставим лок и в ближайшем куске кода ({...}) можем спокойно работать с пошаренными ресурсами, при этом другие потоки будут ждать снятия лока?
запустил - пока жду, среднее время прогонки на тестовом каталоге - 500секундСделай себе маленький тестовый каталог, иначе задолбаешься дебажить.
я правильно понимаю концепцию локов в бусте - ставим лок и в ближайшем куске кода ({...}) можем спокойно работать с пошаренными ресурсами, при этом другие потоки будут ждать снятия лока?Это отдельная и сложная тема, в которую входят CAS-ы, memory barrier-ы и так далее.
Начинающим в этом деле всегда лучше использовать простые локи; и иногда read/write локи.
Сделай себе маленький тестовый каталог, иначе задолбаешься дебажить.как раз-таки проблема и вылазит раз от разу - я как-то запускал - иногда по 5-6 раз спокойно до конца работало, поэтому так и гоняю
Если есть обращения к примитивным типам из разных потоков, то они должны быть объявлены как volatile.Как atomic. volatile вообще из другой степи. Возможно, ты с джавой путаешь.
atomicв моей книжке Страуструпа такого нет
Как atomic. volatile вообще из другой степи. Возможно, ты с джавой путаешь.Я не знаю C++0X
Ключевое слово "volatile" не из другой степи, просто его одного недостаточно, поэтому я написал, что нельзя обращаться без синхронизации. Если ты сложишь это вместе, то скорее всего получишь те гарантии, которые есть у atomic в новом стандарте.
при этом версия на бусте в каких-то файлах контрольную сумму считает не так, как другие версии - т.е. еще разбираться надо с багами в логике
нельзя обращаться без синхронизацииВот это неверно. Ну или смотря что ты под синхронизацией понимаешь.
Вот это неверно. Ну или смотря что ты под синхронизацией понимаешь.Это верно. А что ты понимаешь под синхронизацией?
Мьютексы и сигналы понимаю. Атомики — нет.
Атомики — нет.Вот это неверно. Твои "атомики" почти наверняка делают синхронизацию памяти: acquire, release или обе. Ты же скорее написал разницу между blocking и non blocking.
#!/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
Оставить комментарий
PooH
пишу небольшую утилитку на с++ - она считает контрольную сумму файловсоот-но, пока я работал с одним файлом (путь до файла передается в параметрах приложения, приложение консольное), удавалось использовать стандартную библиотеку с++
сейчас необходимо, чтобы утилитка сама могла искать файлы и директории в заданной директории
вот тут, как я понимаю, и возникнут сложности с кроссплатформенностью (она нужна, т.к. утилитка будет запускаться как в windows (xp, 7), так и в freeBSD, debian, gentoo)
сейчас качаю и собираю boost - он же умеет кросплатформенность?
PS доп вопрос - многопоточное приложение хорошо переносится?