2016-09-01 1 views
0

これからはquestion私は(複数の)QFileを使用して適切な非同期ファイルI/Oを実装できるかどうかを確認することにしました。 1つのファイル上で動作するオブジェクト「QFile」の「プール」を使用して、それぞれオブジェクトで実行されるQtConcurrent APIを介して要求をディスパッチすることです。タスクが終了すると、結果が出力され(読み取りの場合)、QFileオブジェクトがプールに戻されます。私の最初のテストは、これが有効なアプローチであり、実際には同時の読み書き操作(例えば、書き込み中に読み込み)を可能にし、さらに性能を助けることができることを示しています。OSレベルのファイルQtプラットフォームのI/Oロック

明らかな問題は、ファイルの同じセグメントのの読み取りと書き込みです。何が起きているのかを見るために、私は上記の方法を使って状況を設定し、ファイルの同じ部分に狂って書いたり読んだりするようにしました。可能性のある「腐敗」を見つけ出すために、私はセグメントの始めと終わりに数字を増やしています。そのような場合に部分的に書き込まれたデータを読み込んだため、読み込みが最初から最後まで異なる数字を読み取ると、実際の状況では破損したデータを読み取ることができるという考えがあります。

読み取りと書き込みが重複していたため、非同期的に発生していることがわかっていましたが、出力が「間違っていました」ということは一度もありませんでした。これは基本的には、読み取りが部分的に書き込まれたデータを読み取ることはないということを意味少なくともWindowsでは。 QIODevice::Unbufferedフラグを使用しても変更されませんでした。

OSレベルで何らかのロックが行われていると思われますが、この想定が間違っていると私を修正してください。私は、書き込み開始後に開始された読み出しは、書き込みが完了する前に終了する可能性があるという事実に基づいています。 Qtがサポートしているプラ​​ットフォーム(主にPOSIXとAndroidをベースにしているプラ​​ットフォーム)の場合にはこれが当てはまるかどうか、あるいは私自身が実際にロック機構を実装する必要があるのだろうかと思っていました。これらの状況は、書き込まれているセグメントから読み込みを遅らせるためです。

+1

あなたの質問には、[XY問題](http://meta.stackexchange.com/questions/66377/what-is-the-xy-problem)のようなにおいを開始します。ファイルの大きさはどれくらいですか?何が入ってるの?なぜあなたは小さな塊でそれにアクセスしますか?あなたはデータベースを再実装しようとしていますか?あなたがsqliteを使用するほうが良いと思うように見えます。それは数日、または数ヶ月、あるいは1年後にも泡立てることができる何かよりも**弾力性とテスト力があります。 –

+0

@KubaOber私は 'X'について以前に質問してみましたが、誰も答えられることができなかったため、私は自分自身でそれに遭遇する特定の問題について尋ねました。基本的に私はデータベースを再実装していない、私はそれを実装しています。実際にグラフデータベース。私はそこに多くを試みたが、私のニーズに合ったものはなかった。それらはJavaベースであり、膨大なメモリオーバーヘッドを持ち、遅すぎるか、必要なもの(例えば、効率的なサブグラフ)をサポートしていませんでした。私はバックエンドとしてSQLiteを使用しようとしましたが、それは辛いだけ遅く、ディスクスペースも大量に消費します。 – Resurrection

+2

あなたのデータベースについて深刻なことは分かりませんが、もし私がそれをやっていたら、できるだけ多くのSQLiteを再利用するでしょう。非常に最小限のページャー、おそらくBツリー。これらのレイヤーは非常に高速で、依存することができます。 –

答えて

1

書き込みのアトミック性を保証するQFileの実装には何もありません。だから、複数のQFileオブジェクトを使用して、同じ基礎ファイルの同じセクションにアクセスするという考えは正しく機能しません。 Windowsでのテストは、問題がないことを示すものではなく、単に不十分です。十分であれば、期待していた問題が発生していたはずです。小さな、おそらく重複チャンクで非常にパフォーマンスの高いファイルアクセスのために

、あなたがする必要があります。

  1. がメモリにファイルをマップします。
  2. おそらく複数のmutexを使用して並行性を向上させて、メモリへのアクセスをシリアル化します。
  3. アクセス同時にメモリ、及びデータがページインされている間、ミューテックスを保持していない

これは、最初のプリフェッチすることによって行われる。 - アクセスされるバイトの範囲内のすべてのページからの読み出し、及び廃棄のいずれか結果、またはプラットフォーム固有のAPIを使用します。次に、ミューテックスをロックして、データをファイルの外にコピーします。 OSは残りの部分を処理します。

class FileAccess : public QObject { 
    Q_OBJECT 
    QFile m_file; 
    QMutex m_mutex; 
    uchar * m_area = nullptr; 
    void prefetch(qint64 pos, qint64 size); 
public: 
    FileAccess(const QString & name) : m_file{name} {} 
    bool open() { 
    if (m_file.open(QIODevice::ReadWrite)) { 
     m_area = m_file.map(0, m_file.size()); 
     if (! m_area) m_file.close(); 
    } 
    return m_area != nullptr; 
    } 
    void readReq(qint64 pos, qint64 size); 
    Q_SIGNAL readInd(const QByteArray & data, qint64 pos); 
    void write(const QByteArray & data, qint64 pos); 
}; 

void FileAccess:prefetch(qint64 pos, qint64 size) { 
    const qint64 pageSize = 4096; 
    const qint64 pageMask = ~pageSize; 
    for (qint64 offset = pos & pageMask; offset < size; offset += pageSize) { 
    volatile uchar * p = m_area+offset; 
    (void)(*p); 
    } 
} 

void FileAccess:readReq(qint64 pos, qint64 size) { 
    QtConcurrent::run([=]{ 
    QByteArray result{size, Qt::Uninitialized}; 
    prefetch(pos, size); 
    QMutexLocker lock{&m_mutex}; 
    memcpy(result.data(), m_area+pos, result.size()); 
    lock.unlock(); 
    emit readInd(result, pos); 
    }); 
} 

void FileAccess::write(const QByteArray & data, qint64 pos) { 
    QtConcurrent::run([=]{ 
    prefetch(pos, data.size()); 
    QMutexLocker lock{&m_mutex}; 
    memcpy(m_area+pos, data.constData(), data.size()); 
    }); 
} 
関連する問題