StoreBuffer(仮)

StoreBuffer とりあえずコンパイル通った版。

#include <stdio.h>
#include <string.h>
#include "type.h"
#include "util.h"
#include "Dmac.h"
#include "StoreBuffer.h"




#define MAX_STORE_BUFFER_NUM    4
#define MAX_LINE_NUM            32
#define MIN_LINE_SIZE           16




typedef struct STORE_BUFFER_T {
    UINT32                      buffer;         /* store buffer */
    UINT32                      capacity;       /* size of buffer in bytes */
    UINT32                      lineSize;       /* size of cache line in bytes */
    UINT32                      lineNum;        /* count of cache line */
    UINT32                      lineUsed;       /* count of used cache line */
    UINT32                      lineDict[MAX_LINE_NUM]; /* high bit: destination address.
                                                           low  bit: index of cache line.
                                                           The number of bits changes according to
                                                           the lineSize.
                                                           */
    UINT32                      destBeg;        /* begin address of destination */
    UINT32                      destEnd;        /* end address of destination (exclude this) */
    UINT8                       flag;           /* flag */
} STORE_BUFFER_T;

/* STORE_BUFFER_T.flag */
#define FLAG_USED               0x01
#define FLAG_PRELOAD            0x02



typedef struct STORE_BUFFER_WORK_T {
    STORE_BUFFER_T              storeBuffer[MAX_STORE_BUFFER_NUM];
} STORE_BUFFER_WORK_T;




PRIVATE STORE_BUFFER_WORK_T     sMtkStoreBufferWork;



#define work                    (&sMtkStoreBufferWork)
#define getItem(id)             (&work->storeBuffer[id])
#define checkID(id)             ((0 <= id) && (id < MAX_STORE_BUFFER_NUM))



PRIVATE UINT32 MostLeftHighBit(UINT32 data);
PRIVATE void* GetLine(STORE_BUFFER_T* self, void* dest);
PRIVATE UINT32 GetDict(STORE_BUFFER_T* self, void* dest);
PRIVATE UINT32* SearchDict(STORE_BUFFER_T* self, void* dest);
PRIVATE UINT32* AllocDict(STORE_BUFFER_T* self, void* dest);
PRIVATE void Preload(STORE_BUFFER_T* self, UINT32 dict);
PRIVATE void RotateMruDict(STORE_BUFFER_T* self, UINT32 index);
PRIVATE void FlushLruDict(STORE_BUFFER_T* self);
PRIVATE BOOL CheckDestRange(STORE_BUFFER_T* self, void* dest);
PRIVATE UINT32 WriteLine(STORE_BUFFER_T* self, void* line, const void* dest, const void* src, UINT32 size);
PRIVATE UINT32 FillLine(STORE_BUFFER_T* self, void* line, const void* dest, INT filler, UINT32 size);
PRIVATE void ShellSort(UINT32* arr, UINT32 num);
PRIVATE void JoinWrite(STORE_BUFFER_T* self);




void MtkInitializeStoreBuffer(void)
{
    memset(work, 0, sizeof(*work));
}

INT MtkCreateStoreBuffer(void* buffer, UINT32 capacity, UINT32 lineSize, void* destBeg, void* destEnd)
{
    INT id;
    STORE_BUFFER_T* sb;
    INT i;

    for (id = 0; id < MAX_STORE_BUFFER_NUM; ++id) {
        sb = getItem(id);
        if (MTK_BTST(sb->flag, FLAG_USED) == 0) {
            break;
        }
    }
    if (id >= MAX_STORE_BUFFER_NUM) {
        return INVALID_STORE_BUFFER_ID;
    }

    lineSize = MostLeftHighBit(lineSize);
    lineSize = MTK_MIN(lineSize, capacity);
    lineSize = MTK_MAX(lineSize, MIN_LINE_SIZE);

    sb->buffer   = (UINT32) buffer;
    sb->capacity = capacity;
    sb->lineSize = lineSize;
    sb->lineNum  = capacity / lineSize;
    sb->lineNum  = MTK_MIN(sb->lineNum, MAX_LINE_NUM);
    sb->lineUsed = 0;
    for (i = 0; i < MAX_LINE_NUM; ++i) {
        sb->lineDict[i] = i;                    /* set cache line id into low bits */
    }
    sb->destBeg  = (UINT32) destBeg;
    sb->destEnd  = (UINT32) destEnd;
    sb->flag     = FLAG_USED;

    return id;
}

void MtkDestroyStoreBuffer(INT id)
{
    STORE_BUFFER_T* sb;

    if (! checkID(id)) {
        return;
    }

    MtkFlushStoreBuffer(id);

    sb = getItem(id);

    MTK_BCLR(sb->flag, FLAG_USED);
}

BOOL MtkSetPreload(INT id,  BOOL preload)
{
    STORE_BUFFER_T* sb;

    if (! checkID(id)) {
        return FALSE;
    }

    sb = getItem(id);

    if (preload) {
        MTK_BSET(sb->flag, FLAG_PRELOAD);
    }
    else {
        MTK_BCLR(sb->flag, FLAG_PRELOAD);
    }

    return TRUE;
}

BOOL MtkWriteStoreBuffer(INT id, void* dest, const void* src, UINT32 size)
{
    STORE_BUFFER_T* sb;
    void* line;
    UINT32 written;

    if (! checkID(id)) {
        return FALSE;
    }

    sb = getItem(id);

    if (! CheckDestRange(sb, dest)) {
        return FALSE;
    }

    do {
        line    = GetLine(sb, dest);
        written = WriteLine(sb, line, dest, src, size);
        dest    = (      void*)((UINT32)dest + written);
        src     = (const void*)((UINT32)src  + written);
        size   -= written;
    } while (size);

    return TRUE;
}

BOOL MtkFillStoreBuffer(INT id, void* dest, INT filler, UINT32 size)
{
    STORE_BUFFER_T* sb;
    void* line;
    UINT32 written;

    if (! checkID(id)) {
        return FALSE;
    }

    sb = getItem(id);

    if (! CheckDestRange(sb, dest)) {
        return FALSE;
    }

    do {
        line    = GetLine(sb, dest);
        written = FillLine(sb, line, dest, filler, size);
        dest    = (      void*)((UINT32)dest + written);
        size   -= written;
    } while (size);

    return TRUE;
}

BOOL MtkFlushStoreBuffer(INT id)
{
    STORE_BUFFER_T* sb;

    if (! checkID(id)) {
        return FALSE;
    }

    sb = getItem(id);

    /* lineDict[]をソートする */
    ShellSort(sb->lineDict, sb->lineUsed);

    /* destが連続している領域は一気に出力 */
    JoinWrite(sb);

    /* preload を ON にする */
    MTK_BSET(sb->flag, FLAG_PRELOAD);

    return TRUE;
}








PRIVATE UINT32 MostLeftHighBit(UINT32 data)
{
    UINT32 tester = 0x80000000;
    while (tester) {
        if (MTK_BTST(data, tester)) {
            return tester;
        }
        tester >>= 1;
    }
    return 0;
}

PRIVATE void* GetLine(STORE_BUFFER_T* self, void* dest)
{
    UINT32 dict = GetDict(self, dest);
    UINT32 mask = ~(self->lineSize - 1);
    UINT32 index;
    UINT32 line;

    index = MTK_BTST(dict, ~mask);
    line  = self->buffer + self->lineSize * index;

    return (void*)line;
}

PRIVATE UINT32 GetDict(STORE_BUFFER_T* self, void* dest)
{
    UINT32* dict;

    dict = SearchDict(self, dest);
    if (! dict) {
        dict = AllocDict(self, dest);
        if (! dict) {
            FlushLruDict(self);
            dict = AllocDict(self, dest);
        }
    }

    return *dict;
}

PRIVATE UINT32* SearchDict(STORE_BUFFER_T* self, void* dest)
{
    UINT32 mask = ~(self->lineSize - 1);
    INT i;

    /* serch backward for search MRU entry first */
    for (i = self->lineUsed - 1; i >= 0; --i) {
        if (MTK_BTST((UINT32)dest, mask) == MTK_BTST(self->lineDict[i], mask)) {
            RotateMruDict(self, i);
            return &self->lineDict[self->lineUsed - 1];
        }
    }

    return NULL;
}

PRIVATE UINT32* AllocDict(STORE_BUFFER_T* self, void* dest)
{
    UINT32 mask = ~(self->lineSize - 1);
    UINT32 i;

    if (self->lineUsed >= self->lineNum) {
        return NULL;
    }

    i = self->lineUsed++;

    /* @note low bit(cache line id) was saved in lineDict[i]*/
    MTK_BCLR(self->lineDict[i], mask);
    MTK_BSET(self->lineDict[i], MTK_BTST((UINT32)dest, mask));

    if (MTK_BTST(self->flag, FLAG_PRELOAD)) {
        Preload(self, self->lineDict[i]);
    }

    return &self->lineDict[i];
}

PRIVATE void Preload(STORE_BUFFER_T* self, UINT32 dict)
{
    UINT32 mask = ~(self->lineSize - 1);
    UINT32 dest;
    UINT32 index;
    UINT32 line;

    dest  = MTK_BTST(dict, mask);
    index = MTK_BTST(dict, ~mask);
    line  = self->buffer + self->lineSize * index;

    ReadDmac((void*)line, (void*)dest, self->lineSize);
}

PRIVATE void RotateMruDict(STORE_BUFFER_T* self, UINT32 index)
{
    UINT32 tmp = self->lineDict[index];
    UINT32 i;

    for (i = index; i < self->lineNum - 1; ++i) {
        self->lineDict[i] = self->lineDict[i + 1];
    }
    self->lineDict[self->lineNum - 1] = tmp;
}

PRIVATE void FlushLruDict(STORE_BUFFER_T* self)
{
    UINT32 mask = ~(self->lineSize -1);
    UINT32 dict;
    UINT32 dest;
    UINT32 index;
    UINT32 line;
    UINT32 size;

    /*
     * LRU: Least Recently Used
     * lineDict[0] is always LRU dict.
     */
    dict  = self->lineDict[0];
    dest  = MTK_BTST(dict, mask);
    index = MTK_BTST(dict, ~mask);
    line  = self->buffer + self->lineSize * index;
    size  = self->lineSize;

    if (dest < self->destBeg) {
        UINT32 offset = MTK_BTST(self->destBeg, ~mask);
        dest  = self->destBeg;
        size -= offset;
        line += offset;
    }

    if (self->destEnd <= (dest + size)) {
        size = self->destEnd - dest;
    }

    /* printf("FlushLruDict: dest=%08x line=%08x size=%d\n", (INT)dest, (INT)line, (INT)size); */

    WriteDmac((void*)dest, (void*)line, size);

    RotateMruDict(self, 0);
    self->lineUsed--;
    MTK_BSET(self->flag, FLAG_PRELOAD);
}

PRIVATE BOOL CheckDestRange(STORE_BUFFER_T* self, void* dest)
{
    UINT32 mask = ~(self->lineSize - 1);
    UINT32 dest0, beg, end;

    dest0 = MTK_BTST((UINT32)dest, mask);
    beg   = MTK_BTST(self->destBeg, mask);
    end   = MTK_BTST(self->destEnd, mask);

    if ((dest0 < beg) || (end <= dest0)) {
        return FALSE;
    }

    return TRUE;
}

PRIVATE UINT32 WriteLine(STORE_BUFFER_T* self, void* line, const void* dest, const void* src, UINT32 size)
{
    UINT32 mask   = ~(self->lineSize - 1);
    UINT32 offset = MTK_BTST((UINT32)dest, ~mask);
    UINT32 pos    = (UINT32) line + offset;
    UINT32 len    = self->lineSize - offset;
    len = MTK_MIN(len, size);

    memcpy((void*)pos, src, len);

    return len;
}

PRIVATE UINT32 FillLine(STORE_BUFFER_T* self, void* line, const void* dest, INT filler, UINT32 size)
{
    UINT32 mask   = ~(self->lineSize - 1);
    UINT32 offset = MTK_BTST((UINT32)dest, ~mask);
    UINT32 pos    = (UINT32) line + offset;
    UINT32 len    = self->lineSize - offset;
    len = MTK_MIN(len, size);

    memset((void*)pos, filler, len);

    return len;
}

PRIVATE void ShellSort(UINT32* arr, UINT32 num)
{
    UINT32 h;
    UINT32 i, j;

    h = 1;
    while (h < num / 9) {
        h = 3 * h + 1;
    }

    for (; h > 0; h /= 3) {
        for (i = h; i < num; ++i) {
            for (j = i; j >= h; j -= h) {
                if (arr[j - h] > arr[j]) {
                    UINT32 tmp = arr[j - h];
                    arr[j - h] = arr[j];
                    arr[j]     = tmp;
                }
            }
        }
    }
}

PRIVATE void JoinWrite(STORE_BUFFER_T* self)
{
    UINT32 mask = ~(self->lineSize - 1);
    UINT32 dict;
    UINT32 dest;
    UINT32 index;
    UINT32 line;
    UINT32 size;
    UINT32 i;

    LockDmac();

    for (i = 0; i < self->lineUsed; ++i) {
        dict  = self->lineDict[i];
        dest  = MTK_BTST(dict, mask);
        index = MTK_BTST(dict, ~mask);
        line  = self->buffer + self->lineSize * index;
        size  = self->lineSize;

        if (dest < self->destBeg) {
            dest = self->destBeg;
            size = size - (self->destBeg - dest);
            line += MTK_BTST(self->destBeg, ~mask);
        }

        if (self->destEnd <= (dest + size)) {
            size = self->destEnd - dest;
        }

        CacheDmac((void*) dest, (void*) line, size);
    }
    FlushDmac();
    UnlockDmac();
}

既にある自分の Cygwin 環境で gnupack の emacs だけ使ってみたい

うちの環境は 64bit Windows 10 で Cygwin 64bit 版を利用している。

emacsCygwin に入っていたのをターミナルで使っていたが、最近そぞろに GUI 版を使いたくなってきたので gnupack の emacs を入れることにした。

 

ここにその顛末を述べたいと思う。

 

gnupack をインストールする

gnupack Users Guide

gnupackの開発メモ

上記のサイトを参考にインストールする。

emacs だけ欲しいので

パッケージ emacs for gnupack - gnupack - OSDN

にある emacs 24.2 を入れればいいのかもしれないが、 gnupack に入っている emacs のほうが 24.5 とバージョンが新しいようなので欲を掻いて

gnupack_devel-13.06-2015.11.08.exe

を入れることにした。

 

インストール方法は Users Guide に記載あり。ほぼダウンロードして解凍するだけ。

C:\gnupack

にインストールした。

 

emacs の起動

もともと入っている 64bit 版 Cygwinbash を起動し、その中から gnupack Users Guide に書かれた startup_emacs.exe を使って起動を試みる。

 $ /cygdrive/c/gnupack/gnupack_devel-13.06-2015.11.08/startup_emacs.exe

 

起動するにはするが、これだと gnupack 専用の設定で起動してしまう。

 

HOMEの位置も変わってしまうし、いつも使っている .emacs も読んでくれない。

なんか変な感じ。

 

そこで、直接 emacs を起動してみる。

 $ /cygdrive/c/gnupack/gnupack_devel-13.06-2015.11.08/app/cygwin/emacs/bin/emacs.exe 

すると、ウィンドウがポップアップして落ちる。

どうやら 64bit 版 Cygwin からは直接起動できないらしい…。

 

おとなしく gnupack が作り上げた世界で生きていくことも選択肢の一つだが、ここではとりあえず、ふつうの Cygwin 環境で gnupack の提供する emacs だけ使えるようにしてみる。

 

32bit 版 Cygwin を入れる

64bit版 Cygwin からは直接起動できないようなので 32bit Cygwin を入れることにした。(ここで、「Cygwin を入れ直すなら gnupack の構築した世界で生きていけばいいのでは……」という葛藤としばし戦うことになる)

 

Cygwin は ==> こちら

setup-x86.exe (32-bit installation) を使う。 

この記事の執筆時点での最新は 2.5.1。

Cygwinのインストールについては64bit版の別の記事もご参考にどうぞ) 

 

32bit 版 Cygwin から emacs を直接起動してみる

インストールも無事終わり bash を起動して、そこから gnupack の emacs を起動してみる。

すると、下の warning が出て起動しない。 

$ /cygdrive/c/gnupack/gnupack_devel-13.06-2015.11.08/app/cygwin/emacs/bin/emacs
Warning: arch-dependent data dir `/app/cygwin/emacs/libexec/emacs/24.5/i686-pc-cygwin/': No such file or directory
Warning: arch-independent data dir `/app/cygwin/emacs/share/emacs/24.5/etc/': No such file or directory
Warning: Lisp directory `/app/cygwin/emacs/share/emacs/24.5/lisp': No such file or directory
Error: charsets directory not found:
/app/cygwin/emacs/share/emacs/24.5/etc/charsets
Emacs will not function correctly without the character map files.
Please check your installation!

 

/app をマウントする

どうやら gnupack の提供する /app が見えないために emacs が起動しないようなので gnupack についてきた /etc/fstab を参考に /app をマウントする。

 

gnupack の /etc/fstab

# For a description of the file format, see the Users Guide
# http://cygwin.com/cygwin-ug-net/using.html#mount-table
# This is default anyway:
# none /cygdrive cygdrive binary,posix=0,user 0 0
none / cygdrive binary,nouser,noacl,posix=0 0 0
C:/gnupack/gnupack_devel-13.06-2015.11.08/home /home ntfs binary,nouser,noacl,posix=0 0 0
C:/Users/ユーザー名/AppData/Local/Temp/gnupack /tmp ntfs binary,nouser,noacl,posix=0 0 0
C:/gnupack/gnupack_devel-13.06-2015.11.08/app/cygwin/local /usr/local ntfs binary,nouser,noacl,posix=0 0 0
C:/gnupack/gnupack_devel-13.06-2015.11.08 /root ntfs binary,nouser,noacl,posix=0 0 0
C:/gnupack/gnupack_devel-13.06-2015.11.08/app /app ntfs binary,nouser,noacl,posix=0 0 0
C:/Users/ユーザー名/Desktop /desktop ntfs binary,nouser,noacl,posix=0 0 0
C:/Users/ユーザー名/Desktop /top ntfs binary,nouser,noacl,posix=0 0 0

 

 /app は C:/gnupack/gnupack_devel-13.06-2015.11.08/app が本体らしいので、うちの Cygwin の /etc/fstab にも同じ設定を追加する。

# /etc/fstab
#
#    This file is read once by the first process in a Cygwin process tree.
#    To pick up changes, restart all Cygwin processes.  For a description
#    see https://cygwin.com/cygwin-ug-net/using.html#mount-table

# This is default anyway:
none /cygdrive cygdrive binary,posix=0,user 0 0
#none / cygdrive binary,nouser,noacl,posix=0 0 0
#C:/gnupack/gnupack_devel-13.06-2015.11.08/home /home ntfs binary,nouser,noacl,posix=0 0 0
#C:/Users/ユーザー名/AppData/Local/Temp/gnupack /tmp ntfs binary,nouser,noacl,posix=0 0 0
#C:/gnupack/gnupack_devel-13.06-2015.11.08/app/cygwin/local /usr/local ntfs binary,nouser,noacl,posix=0 0 0
#C:/gnupack/gnupack_devel-13.06-2015.11.08 /root ntfs binary,nouser,noacl,posix=0 0 0
C:/gnupack/gnupack_devel-13.06-2015.11.08/app /app ntfs binary,nouser,noacl,posix=0 0 0
#C:/Users/ユーザー名/Desktop /desktop ntfs binary,nouser,noacl,posix=0 0 0
#C:/Users/ユーザー名/Desktop /top ntfs binary,nouser,noacl,posix=0 0 0

 

とりあえず、全部コピペして /app 以外はコメントアウト。 /etc/fstab を保存して

$ mount  /app

を実行してマウント。

 

再度 emacsCygwin から実行してみる

 

$ /app/cygwin/emacs/bin/emacs  &

 

動いた!

 

その他の設定

gnupack の local に入っている諸々にアクセスできるように local もマウントしておいたほうがいいかもしれない。

 

でも /usr/local にマウントされるので、自分で /usr/local にインストールしたものがあるとそれは使えなくなる。

 

そういう向きは、とりあえず gnupack の local/bin に PATH だけ通しておいてもいいかもしれない。

 

.bashrc

  export PATH=/app/cygwin/local/bin:$PATH 

 

私の場合、32bit  Cygwin を入れたばかりで /usr/local には空の bin, etc, lib ディレクトリがあるだけなので mount してみた。

 

/etc/fstab で下の設定のコメントを外し

C:/gnupack/gnupack_devel-13.06-2015.11.08/app/cygwin/local  /usr/local  ntfs binary,nouser,noacl,posix=0 0 0

 /usr/local にマウントする

$ mount  /usr/local

こうすると gnupack に入っている日本語 man も表示出来てありがたい。

$ man 3 printf

 

PRINTF(3)                             Linux Programmer's Manual                            PRINTF(3)

名前
       printf, fprintf, sprintf, snprintf, vprintf, vfprintf, vsprintf, vsnprintf - 指定された書式に
       変換して出力を行う

書式
       #include <stdio.h>

       int printf(const char *format, ...);

...(後略)...

 

やってみたことリスト

今回は、とりあえず起動しただけ。まだ使い込んでいないのでちゃんと動くのか、ちょっと不安だけど、下のようなことは出来た。

  • Cygwinbash から gnupack の emacs を直接起動。OK
  • /app/cygwin/emacs/bin/emacs ~/.bashrc として自分の .bashrc が開く。(gnupack のホームのものではなく)
  • Cygwin の標準的なドライブ指定方式 /cygdrive/c で Cドライブにアクセス。OK。(gnupack では /cygdrive はマウントされない)
  • 日本語入力 OK。ATOK使えた。
  • テキストファイルに utf-8-unix で書いて保存。OK
  • M-x complile 実行。OK。 g++ -g -Wall -std=c++14 -o exe-name src-name.cpp を実行。コンパイルも通り、実行したら動いた。

 

ちょっと前には 64bit Windows では、 64bit Cygwin じゃないとダメなんじゃないかと思っていたけど、気のせいだったかな。それとも Cygwin のほうでなにか対処したのかな。 

 

最後に

今回は、既存の環境に gnupack の emacs だけを追加してみる方法を検討してみたが、その作業のあいだ gnupack が提供してくれる環境を使っていくなかで、これでもいいかな、いやむしろこっちのほうがいいかな、という気持ちに傾きつつある。

どうしよう。

 

とりあえず動いた memcpy4

 

// g++ -g -Wall -std=c++14 -o memcpy4 memcpy4.cpp
#include <cstdio>
#include <cstdlib>
#include <cstdarg>
#include <cstring>
#include <cassert>
#include <iostream>
using namespace std; #define ASSERTF(assertion, ...) do { if (!(assertion)) { assertf(__FILE__, __LINE__, __func__, __VA_ARGS__); }} while(0) void assertf(const char* file, int line, const char* func, const char* format, ...) { va_list ap; printf("%s:%d:%s: assertion failed: ", file, line, func); va_start(ap, format); vprintf(format, ap); va_end(ap); abort(); } // 4byte 単位でしかハードアクセスできないメモリコピー void memcpy4(void* dest, void* src, size_t size) { //memcpy(dest, src, size); size_t remain_size = size; // 残りのサイズ(バイト数) size_t copy_total = 0; // 読み取りバッファから書き込みバッファにコピーした総バイト数 // 読み取りデータの情報 size_t rd_boff = (size_t)src & 3; // 読み取り開始オフセット //size_t rd_eoff = rd_boff + size; // 読み取り終了オフセット(この値を含まない) size_t rd_beg = (size_t)src & ~3; // 読み取り開始アドレス //size_t rd_end = rd_beg + ((rd_eoff + 3) & ~3); // 読み取り終了アドレス(この値を含まない) // 書き込みデータの情報 size_t wr_boff = (size_t)dest & 3; // 書き込み開始オフセット size_t wr_eoff = wr_boff + size; // 書き込み終了オフセット(この値を含まない) size_t wr_beg = (size_t)dest & ~3; // 書き込み開始アドレス size_t wr_end = wr_beg + ((wr_eoff + 3) & ~3); // 書き込み終了アドレス(この値を含まない) // 読み取りバッファの情報 uint32_t rd_buf = 0; size_t rd_pos = rd_boff; size_t rd_remain = ((remain_size < 4) ? remain_size : 4) - rd_pos; // 書き込みバッファの情報 uint32_t wr_buf = 0; size_t wr_pos = wr_boff; size_t wr_remain = ((remain_size < 4) ? remain_size : 4) - wr_pos; // 読み取りバッファから書き込みバッファへのコピーサイズ size_t copy_size = (rd_remain < wr_remain) ? rd_remain : wr_remain; // 読み取りアドレス uint32_t* rd_addr = (uint32_t*) rd_beg; // 書き込みアドレス uint32_t* wr_addr = (uint32_t*) wr_beg; // 最初の一回目に wr_buf に読み込んでおくべきかどうか bool is_first_preload = ((wr_boff & 3) != (wr_beg & 3));
// 最後の一回で wr_buf に読み込んでおくべきかどうか
bool is_last_preload = ((wr_eoff & 3) != (wr_end & 3)); // 読み取りバッファに最初のデータを取得 rd_buf = *rd_addr; rd_addr += 1; // 最初の書き込みが 4byte アラインでない場合、範囲外のデータを壊さ // ないように書き込みバッファに読み込んでおく if (is_first_preload) { wr_buf = *wr_addr; } while ((size_t)wr_addr < wr_end) { rd_pos &= 3; rd_remain = (rd_remain != 0) ? rd_remain : ((remain_size < 4) ? remain_size : 4); wr_pos &= 3; wr_remain = (wr_remain != 0) ? wr_remain : ((remain_size < 4) ? remain_size : 4); copy_size = (rd_remain < wr_remain) ? rd_remain : wr_remain; for (size_t i = 0; i < copy_size; ++i) { ((char*)&wr_buf)[wr_pos + i] = ((char*)&rd_buf)[rd_pos + i]; } rd_pos += copy_size; rd_remain -= copy_size; wr_pos += copy_size; wr_remain -= copy_size; copy_total += copy_size; if (wr_remain == 0) { *wr_addr = wr_buf; wr_addr += 1; remain_size = size - copy_total; // 最後の書き込みが 4byte アラインでない場合、範囲外のデー // タを壊さないように書き込みバッファを読み込んでおく if (((size_t)wr_addr == wr_end - 4) && is_last_preload ) { wr_buf = *wr_addr; } } if (rd_remain == 0) { rd_buf = *rd_addr; rd_addr += 1; } } } int main(int argc, char* argv[]) { //assert(false); const size_t asize = 24; alignas(4) char obuf[asize] = {0}; alignas(4) char ibuf[asize] = {0}; size_t i; size_t opos; size_t ipos; size_t len; // input data for (i = 0; i < asize; ++i) { ibuf[i] = i; } for (opos = 0; opos <= 4; ++opos) { for (ipos = 0; ipos <= 4; ++ipos) { for (len = 8; len <= 16; ++len) { memset(obuf, 0, asize); memcpy4(obuf + opos, ibuf + ipos, len); for (i = 0; i < asize; ++i) { printf("%2d ", obuf[i]); } printf("\n"); fflush(stdout); for (i = 0; i < opos; ++i) { ASSERTF(obuf[i] == 0, "i=%d opos=%d ipos=%d len=%d obuf[i]=%d\n", (int)i, (int)opos, (int)ipos, (int)len, (int)obuf[i]); } for (; i < (opos + len); ++i) { ASSERTF(obuf[i] == ibuf[i - opos + ipos], "i=%d opos=%d ipos=%d len=%d obuf[i]=%d\n", (int)i, (int)opos, (int)ipos, (int)len, (int)obuf[i]); } for (; i < asize; ++i) { ASSERTF(obuf[i] == 0, "i=%d opos=%d ipos=%d len=%d obuf[i]=%d\n", (int)i, (int)opos, (int)ipos, (int)len, (int)obuf[i]); } } } } return 0; }

 

C++ Atomic operations についてのメモ

mutex が必要ないわけではないということ

atomic より粒度が大きいが現在でも有効な排他制御の手段。ということでいいかな。

 

粒度の細かい順に並べると

  1. atomic_thread_fence, atomic_flag
  2. atomic
  3. mutex, condition_variable
  4. lock_guard, unique_lock, once_flag, call_once
  5. future, promise
  6. packaged_task
  7. async

かな。

 

メモリロケーションについて

適切なメモリモデルを持たない場合、プロセッサが一度にロード・ストア可能なビット幅(1ワード)に複数の変数が割り当てられていると、複数のプロセッサから異なる変数を更新しに行ったとしても data race が起こる。

たとえば1ワード4byteのプロセッサで char arr[4]; と定義して arr[0] と arr[3] に別々のプロセッサから書き込みを伴う同時アクセスがあると data race 発生。(適切なメモリモデルが定義されていない場合)


static char arr[4] = {0};
thread t1{[&]{arr[0] = 1;}};
thread t2{[&]{arr[3] = 1;}};
t1.join(); t2.join();
cout << "arr= {" << arr[0] << "," << arr[3] << "}\n";


出力は arr = {0,1}, arr = {1,0}, arr = {1,1} のいずれかになる。(適切なメモリモデルが定義されていない場合)

(試しにやってみたが {1,1}にしかならないなあ。適切に定義されているということか)

 

メモリロケーションとは:

算術型の変数一つ、ポインタ一つ、非ゼロの幅を持つビットフィールドの並び(但し、たぶん1ワード幅未満)が、それぞれ占有するメモリ領域のこと。幅がゼロのビットフィールドはメモリロケーションの区切りとして使える。

異なるメモリロケーションであれば、複数スレッドが互いに干渉することなく読み書きできる。

 

 

データ競合(data race)

複数のスレッドが同じメモリロケーションに同時にアクセスして、一方が書き込みであれば、data race が発生する。read-modify-write 問題。

 

 

メモリオーダーについて 

Atomic operations ライブラリではデフォルトではアウトオブオーダー実行を抑制する。でいいのかな。つまりコードに書いてある順番で処理が実行される(ように見せる)

 
これは、デフォルトで、メモリーオーダー std::memory_order_seq_cst (sequentially consistent: 逐次一貫) に設定されているため。


高速化のため、ほかの memory_order も設定できる。という認識でいいかな。


http://en.cppreference.com/w/cpp/atomic/memory_order を少し翻訳(意訳)してみよう。

std::memory_order specifies how regular, non-atomic memory accesses are to be ordered around an atomic operation. Absent any constraints on a multi-core system, when multiple threads simultaneously read and write to several variables, one thread can observe the values change in an order different from the order another thread wrote them. Indeed, the apparent order of changes can even differ among multiple reader threads. Some similar effects can occur even on uniprocessor systems due to compiler transformations allowed by the memory model.

The default behavior of all atomic operations in the library provides for sequentially consistent ordering (see discussion below). That default can hurt performance, but the library's atomic operations can be given an additional std::memory_order argument to specify the exact constraints, beyond atomicity, that the compiler and processor must enforce for that operation.


std:: memory_order は、通常のメモリアクセス、つまり非アトミックなメモリアクセスが、アトミック操作を行う前後で、どのように並べ替えられるか(あるいは並べ替えられないか)を規定する。

メモリオーダーについて何の制約もないマルチコアシステムでは、複数のスレッドが複数の変数に同時にリード、ライトを行うとき、ある一つのスレッドが観測する変数の「変化の順番」は、変数を書いたスレッドが書き出した順番と必ずしも一致しない。

事実、読み出しを行う複数のスレッドの間では、変化の順番が見かけ上入れ替わる。

ユニプロセッサシステムでも、コンパイラによる順番の入れ替えが許されているメモリモデルを持つ場合、同じことが起こる。

ライブラリが提供するアトミック操作のデフォルトの振る舞いは、逐次一貫(sequentially consistent)である。

デフォルトの逐次一貫方式は、パフォーマンスを犠牲にしている。しかし、ライブラリのアトミック操作は std::memory_order 引数を取るので、逐次一貫以外の制約を与えることもできる。

コンパイラとプロセッサは操作をアトミックにするだけでなく、引数に与えたメモリオーダーの制約を適用してアトミック操作を行う責任を持つ。

 


memory_order_relaxed:

Relaxed operation: there are no synchronization or ordering constraints, only atomicity is required of this operation.

Relax操作:同期や順序の制約はない。アトミック性だけが、この操作に求められている。


memory_order_consume:

A load operation with this memory order performs a consume operation on the affected memory location: no reads in the current thread dependent on the value currently loaded can be reordered before this load. This ensures that writes to data-dependent variables in other threads that release the same atomic variable are visible in the current thread. On most platforms, this affects compiler optimizations only.

 

このメモリオーダーでのロードは、影響するメモリロケーションに対して「消費操作」を実行する。:

現在のスレッドでまだ読み込んでいない変更のうち、すでにロードされている値に影響するものが、ロードの前に読み込まれる。(RAMやCache上で値が変わっていたら、最新の値がそのスレッドに取り込まれる、ということらしい)

これは、同じアトミック変数を「解放(release)」した他のスレッド(複数可)の書き込みのうち、現在のスレッドに関係のある書き込みが、現在のスレッドで見えることを保証する。

多くのプラットフォームでは、これは、コンパイラの最適化にのみ影響する。

 

memory_order_acquire:

A load operation with this memory order performs the acquire operation on the affected memory location: no memory accesses in the current thread can be reordered before this load. This ensures that all writes in other threads that release the same atomic variable are visible in the current thread.

 


このメモリオーダーでのロードは、影響を受けるメモリロケーションの「獲得操作」を行います:

現在のスレッドでまだ読み込んでいない変更や書き込んでいない変更が、ロードの前に読み書きされる。(書き込みも? no memory accesses と言っているからそうだろうなあ)
これは、同じアトミック変数を「解放(release)」した他のスレッド(複数可)のすべての書き込みが現在のスレッドで見えることを保証する。


memory_order_release:

A store operation with this memory order performs the release operation: no memory accesses in the current thread can be reordered after this store. This ensures that all writes in the current thread are visible in other threads that acquire the same atomic variable and writes that carry a dependency into the atomic variable become visible in other threads that consume the same atomic.

このメモリオーダーでのストアは、「解放操作」を行います。:

現在のスレッドでまだ読み込んでいない変更や書き込んでいない変更が、ストアのあとに読み書きされる。

これは、現在のスレッドによるすべての書き込みが、同じアトミック変数を「獲得」した他のスレッド(複数可)で見えることとを保証する。かつ、書き込みのうち、依存関係をアトミック変数に記録したものが、同じアトミック変数を「消費」した他のスレッド(複数可)で、見えることを保証する。


memory_order_acq_rel:

A read-modify-write operation with this memory order is both an acquire operation and a release operation. No memory accesses in the current thread can be reordered before this load, and no memory accesses in the current thread can be reordered after this store. It is ensured that all writes in another threads that release the same atomic variable are visible before the modification and the modification is visible in other threads that acquire the same atomic variable.

このメモリオーダーでの read-modify-write 操作は、「獲得操作」と「解放操作」の両方を行う。

現在のスレッドでまだ読み込んでいない変更や書き込んでいない変更が、ロードの前に読み書きされる。そしてストアのあとにまた読み書きが実施される。

これは、変更(modification)の前に、同じアトミック変数を「解放」した他のスレッドのすべての書き込みが見えることを保証し、現在のスレッドのすべての変更が、同じアトミック変数を「獲得」した他のスレットで見えることを保証する。

 

memory_order_seq_cst:

Same as memory_order_acq_rel, plus a single total order exists in which all threads observe all modifications (see below) in the same order.

memory_order_acq_rel と同じ。加えて、単一の全順序が存在する。
すなわち、すべてのスレッドにすべての変更が同じ順序で見える。

 

 

 

Windows 10 に teraterm を入れる

teraterm を入れた。

Tera Term (テラターム) プロジェクト日本語トップページ - OSDN

からダウンロードしてきてインストール。そのときのバージョンは 4.87

 

cygterm.cfg 消える問題

 

teraterm を Program Files (x86) フォルダにインストールした場合、変なことになる。

 

teraterm のメニューから何か設定(Cygwinの設定だけかな)をいじると、インストールフォルダにある cygterm.cfg が消えてしまう。

 

消えたものは C:\Users\<user-name>\AppData\Local\VirtualStore\Program Files (x86)\teraterm\cygterm.cfg として保存されるらしい。

 

しかし teraterm 側は相変わらずインストールフォルダしか見ないようで、ファイルがないと文句を言って起動しなくなる。

 

手でコピーして戻したら動いた。

 

※ インストール先が、システムで管理してるフォルダでなければたぶん消えない。teratermのリリースにZIP版もあるのはこのためかな。

 

TERATERM.INI の編集

  • VTPos=1000,0

ウィンドウの左上の角の位置を(x, y)(単位は画面解像度?)で指定するらしい。うちのマシンは 2560x1440 だが、1000で左上の角が画面の左右真ん中くらいになる。

f:id:nodamotoki:20150905151941p:plain

 

  • TerminalSize=103,54

一行に表示する文字数 103 と、ウィンドウに表示する行数 54 かな。

これは、teraterm のウィンドウの大きさを変えてメニューから設定の保存をすれば書き込まれるので、手作業で変える必要はなかった。

 

いじったのは、そんなところだったかな。

 

 

Windows 10 に Cygwin を入れる

Windows 10 に Cygwin を入れた。

Cygwin は 64bit版をインストール。64bit OS に32bit版では何かと問題らしいので。。。

(この記事を書いた 2015年9月頃はネット上にいくつか問題を指摘する声があったように記憶しているが、2016年5月時点では 32bit版 Cygwin でも GitHub から git clone もできるし gccコンパイルもできるので、もう問題ないのかもしれない…)

 

Cygwin から setup-x86_64.exe を落としてきてインストール開始。

f:id:nodamotoki:20150904233230p:plain

 

とりあえず、インターネットから直接インストール。

f:id:nodamotoki:20150904233308p:plain

 

Cygwinのルートはデフォルトのまま、C:\cygwin64

f:id:nodamotoki:20150904233327p:plain

 

ネットからとってきたファイルを置く場所もデフォルトのまま。setup-x86_64.exeのあるディレクトリ。

f:id:nodamotoki:20150904233339p:plain

 

インターネットの設定はIEのを借りる。

当初 Direct Connection でやっていたら回線がぶつぶつ切れた。(うちのルーターとかその通信方式とかプロバイダとかが悪いのかもしれないが…)。

f:id:nodamotoki:20150904233356p:plain

 

接続先はとりあえず  http://ftp.jaist.ac.jp にした。

f:id:nodamotoki:20150904233420p:plain

 

パッケージは

  • Devel
  • Doc
  • Editors
  • Interpreters
  • Math
  • Python
  • Ruby
  • Shells
  • System
  • Text
  • Utils

を選択。とりあえず使いそうなのを。

f:id:nodamotoki:20150904233432p:plain

 

 

夜インストールを始めたら、いつの間にやら東雲の薄明かりに窓が白んできていた。

インストールは体に悪いな。

 

後日 gnupack の emacs を使いたかったので 32bit 版 cygwin をいれた(別の記事)。そのときはパッケージ数を極力少なめにした。すると 30 分程度でインストール完了。欲張ってはいけなかった……。