Ext4 は ext2 と比較して予期しない書き込み待ち時間の差を示します。

Ext4 は ext2 と比較して予期しない書き込み待ち時間の差を示します。

組み込みシステムで待ち時間に敏感なアプリケーションを実行していますが、同じ物理デバイスでext4パーティションとext2パーティションに書き込むことにはいくつかの違いがあります。特に、メモリマップで小さな更新をたくさん実行すると、断続的に遅延が発生しますが、ext4でのみ発生します。パフォーマンス(特に待ち時間の変更)を改善するために、ext4をさまざまなオプションでインストールし、次のインストールオプションを決定していくつかの一般的なトリックを試しました。

mount -t ext4 -o remount,rw,noatime,nodiratime,user_xattr,barrier=1,data=ordered,nodelalloc /dev/mmcblk0p6 /media/mmc/data

barrier=0いかなる改善も提供されないようです。

ext2 パーティションの場合は、次のフラグを使用します。

/dev/mmcblk0p3 on /media/mmc/data2 type ext2 (rw,relatime,errors=continue)

私が使っているテストプログラムは次のとおりです。

#include <stdio.h>
#include <cstring>
#include <cstdio>
#include <string.h>
#include <stdint.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <fcntl.h>
#include <stdint.h>
#include <cstdlib>
#include <time.h>
#include <stdio.h>
#include <signal.h>
#include <pthread.h>
#include <unistd.h>
#include <errno.h>
#include <stdlib.h>

uint32_t getMonotonicMillis()
{
    struct timespec time;
    clock_gettime(CLOCK_MONOTONIC, &time);
    uint32_t millis = (time.tv_nsec/1000000)+(time.tv_sec*1000);
    return millis;
}

void tune(const char* name, const char* value)
{
    FILE* tuneFd = fopen(name, "wb+");
    fwrite(value, strlen(value), 1, tuneFd);
    fclose(tuneFd);
}

void tuneForFasterWriteback()
{
    tune("/proc/sys/vm/dirty_writeback_centisecs", "25");
    tune("/proc/sys/vm/dirty_expire_centisecs", "200");
    tune("/proc/sys/vm/dirty_background_ratio", "5");
    tune("/proc/sys/vm/dirty_ratio", "40");
    tune("/proc/sys/vm/swappiness", "0");
}


class MMapper
{
public:
    const char* _backingPath;
    int _blockSize;
    int _blockCount;
    bool _isSparse;

    int _size;
    uint8_t *_data;
    int _backingFile;
    uint8_t *_buffer;

    MMapper(const char *backingPath, int blockSize, int blockCount, bool isSparse) :
        _backingPath(backingPath),
        _blockSize(blockSize),
        _blockCount(blockCount),
        _isSparse(isSparse),
        _size(blockSize*blockCount)
    {
        printf("Creating MMapper for %s with block size %i, block count %i and it is%s sparse\n",
                _backingPath,
                _blockSize,
                _blockCount,
                _isSparse ? "" : " not");
        _backingFile = open(_backingPath, O_CREAT | O_RDWR | O_TRUNC, 0600);

        if(_isSparse)
        {
            ftruncate(_backingFile, _size);
        }
        else
        {
            posix_fallocate(_backingFile, 0, _size);
            fsync(_backingFile);
        }
        _data = (uint8_t*)mmap(NULL, _size, PROT_READ | PROT_WRITE, MAP_SHARED, _backingFile, 0);
        _buffer = new uint8_t[blockSize];
        printf("MMapper %s created!\n", _backingPath);
    }

    ~MMapper()
    {
        printf("Destroying MMapper %s\n", _backingPath);
        if(_data)
        {
            msync(_data, _size, MS_SYNC);
            munmap(_data, _size);
            close(_backingFile);
            _data = NULL;
            delete [] _buffer;
            _buffer = NULL;
        }
        printf("Destroyed!\n");
    }

    void writeBlock(int whichBlock)
    {
        memcpy(&_data[whichBlock*_blockSize], _buffer, _blockSize);
    }
};



int main(int argc, char** argv)
{
    tuneForFasterWriteback();

    int timeBetweenBlocks = 40*1000;
    //2^12 x 2^16  = 2^28  = 2^10*2^10*2^8 = 256MB
    int blockSize = 4*1024;
    int blockCount = 64*1024;
    int bigBlockCount = 2*64*1024;
    int iterations = 25*40*60; //25 counts simulates 1 layer for one second, 5 minutes here


    uint32_t startMillis = getMonotonicMillis();

    int measureIterationCount = 50;

    MMapper mapper("sparse", blockSize, bigBlockCount, true);
    for(int i=0; i<iterations; i++)
    {
        int block = rand()%blockCount;
        mapper.writeBlock(block);
        usleep(timeBetweenBlocks);
        if(i%measureIterationCount==measureIterationCount-1)
        {
            uint32_t elapsedTime = getMonotonicMillis()-startMillis;
            printf("%i took %u\n", i, elapsedTime);
            startMillis = getMonotonicMillis();
        }
    }

    return 0;
}

とてもシンプルなテストケースです。私はタイミングが非常に正確であるとは思わず、一般的な傾向にもっと興味があります。テストを実行する前に、次の手順を実行して、システムがかなり安定した状態にあり、ディスク書き込みアクティビティが頻繁に発生しないことを確認しました。

watch grep -e Writeback: -e Dirty: /proc/meminfo

ディスクアクティビティがほとんどまたはまったくありません。これは、出力のキューに0または1が表示され、確認することもできますvmstat 1。また、テストを実行する直前に同期を実行します。 vmサブシステムには積極的な書き込み保存パラメータもあります。

ext2パーティションでテストを実行したとき、最初の100回の50書き込みバッチは、標準偏差が8ミリ秒の2012ミリ秒を記録しました。 ext4パーティションで同じテストを実行したときの平均時間は2151ミリ秒でしたが、標準偏差は409ミリ秒で高かった。最大の心配は変化が遅くなるのですが、それが苦しいですね。 ext4パーティションテストの実際の時間は次のとおりです。

{2372, 3291, 2025, 2020, 2019, 2019, 2019, 2019, 2019, 2020, 2019, 2019, 2019, 2019, 2020, 2021, 2037, 2019, 2021, 2021, 2020, 2152, 2020, 2021, 2019, 2019, 2020, 2153, 2020, 2020, 2021, 2020, 2020, 2020, 2043, 2021, 2019, 2019, 2019, 2053, 2019, 2020, 2023, 2020, 2020, 2021, 2019, 2022, 2019, 2020, 2020, 2020, 2019, 2020, 2019, 2019, 2021, 2023, 2019, 2023, 2025, 3574, 2019, 3013, 2019, 2021, 2019, 3755, 2021, 2020, 2020, 2019, 2020, 2020, 2019, 2799, 2020, 2019, 2019, 2020, 2020, 2143, 2088, 2026, 2017, 2310, 2020, 2485, 4214, 2023, 2020, 2023, 3405, 2020, 2019, 2020, 2020, 2019, 2020, 3591}

残念ながら、ext2が最終的なソリューションのオプションであるかどうかはわかりませんので、ファイルシステム間の動作の違いを理解しようとしています。少なくともext4システムをインストールして調整するときに使用されるフラグを制御できます。

  • noatime / nodiratimeは大きな影響を与えないようです。

  • Barrier=0/1 は重要ではないようです。

  • nodellallocは少し役に立ちますが、待ち時間の変更を取り除くのに十分ではありません。

  • ext4パーティションは約10%しか占めません。

この問題についてどんな意見でも送ってくれてありがとう!

ベストアンサー1

一言:日記を書いてください。

http://www.thegeekstuff.com/2011/05/ext2-ext3-ext4/

埋め込みについて話すとき、どのようなフラッシュメモリがあるとしますか? Flashでジャーナル処理されたext4のパフォーマンスは優れています。 Ext2を使用することをお勧めします。

ext4を使用する必要がある場合は、ログを無効にしてログを避けるためにファイルシステムを調整する方法についての良い記事は次のとおりです。 http://fenidik.blogspot.com/2010/03/ext4-disable-journal.html

おすすめ記事