Pebble Coding

ソフトウェアエンジニアによるIT技術、数学の備忘録

macOSにSPTKをインストール

macOS 10.12.4

xgrを動かすのに必要なのでconfigure前に入れておきます。

$ brew cask install quartz

あとは、 SPTK-3.10.tar.gzをDLし、

$ configure
$ make
$ make install

したら完了です。

参考

SPTKの使い方 (1) インストール・波形描画・音声再生 - 人工知能に関する断創録

sayコマンドで喋らせたものをwavファイルへ出力する。
句読点を入れた方が、ちゃんとしたイントネーションになります。

$ say "あらゆる現実を、全て、自分のほうへねじ曲げたのだ" -o ~/a.wav --data-format=LEI16@16000

wavファイルをrawに変換(waveヘッダを除去)

$ wav2raw a.wav

C++11 unique_ptrとshared_ptr

unique_ptrは所有者が1人以下のポインタとして利用する。
実装にatomic関数は用いられていないためコピーや破棄はスレッドセーフではないが、それによるオーバーヘッドは存在しない。

shared_ptrは所有者が2人以上のポインタとして利用する。
いったん値を設定した後は、コピーや破棄はスレッドセーフに動作する。
shared_ptr<T>とした場合は、Tのデストラクタは最後に所有を解放したスレッドで一度だけ実行されることが保証される。
実装にはatomic関数が用いられているため、その分のオーバーヘッドがある。
例えば、ThreadAで

shared_ptr<T> a = shared_ptr<T>(new T); // 生成
shared_ptr<T> b = a // コピー

としたとき、同時にThreadAがaのnon-constなメソッドを実行、threadBがbのnon-constなメソッドを実行してもスレッド安全性は保たれる。
各スレッドがそのスレッド専用のshared_ptrにアクセスする分には安全ということである。
同時にThreadAがaのconstなメソッドを実行、ThreadBがnon-constなメソッドを実行した場合、動作は未定義である。

ここまでがネットを漁った結果得た知識である。


以下、ネット上の情報を羅列しておく。

shared_ptrはC++11でのプリミティブな型が持つスレッドセーフ性のみを持つ。
つまり、同一のshared_ptrに対して、
例1) ThreadA : read, ThreadB : read -> スレッドセーフ
例2) ThreadA : read, ThreadB : write -> 動作未定義
例3) ThreadA : write, ThreadB : write -> 動作未定義
となる。

参照1
std::shared_ptr - cppreference.com

“全てのメンバ関数(コピーコンストラクタとコピーアサインメントを含む)は異なるshared_ptrのインスタンスにおいて、複数のスレッドから呼び出せる。 この呼び出しでは、それらのインスタンスがコピーであり、同一のオブジェクトの所有権を共有している場合においてさえも、追加の同期処理は不要である。 複数のスレッドが同じshared_ptrに対して同期処理なしで、shared_ptrにアクセスし、shared_ptrのnon-const関数を使った場合、データレースが起こりうる。 データレースを防ぐには、atomic関数のshared_ptrオーバーロードを使うことにより、データレースの発生を防ぐことができる。”

“All member functions (including copy constructor and copy assignment) can be called by multiple threads on different instances of shared_ptr without additional synchronization even if these instances are copies and share ownership of the same object. If multiple threads of execution access the same shared_ptr without synchronization and any of those accesses uses a non-const member function of shared_ptr then a data race will occur; the shared_ptr overloads of atomic functions can be used to prevent the data race.”

参照2
shared_ptr - 1.53.0

“shared_ptr objects offer the same level of thread safety as built-in types. A shared_ptr instance can be "read” (accessed using only const operations) simultaneously by multiple threads. Different shared_ptr instances can be “written to” (accessed using mutable operations such as operator= or reset) simultaneosly by multiple threads (even when these instances are copies, and share the same reference count underneath.) Any other simultaneous accesses result in undefined behavior."

ここで複数のスレッドから呼び出せると書いてあるが、同時に安全に呼び出せるとは書いていない。
同期処理なしで同時に安全に呼び出せるのは、constのメンバ関数だけであるということである。

メンバ関数でconstとnon-constがあるらしいので、挙げておこう。
constructor : non-const
destructor : non-const
operator= : non-const
reset : non-const
swap : non-const
get : const
operator* : const
operator-> : const
use_count : const
unique : const
operator bool : const
owner_before : const

参照3

Lesson #4: Smart Pointers | Mike's C++11 Blog

“unique_ptrと異なり、shared_ptrは他のshared_ptrへのコピーを可能とし、ポインタは常にただ一度のみdeleteされることが保証される。全てのshared_ptrオブジェクトが保持しているものが破棄された時に。 これにはリファレンスカウントが用いられ、同じポインタを保持するshared_ptrがどのくらいの数あるかを保持する。このリファレンスカウントはアトミック関数が用いられるため、スレッドセーフである。”

“Unlike unique_ptr, it also allows copying of the shared_ptr object to another shared_ptr, and then ensures that the pointer is still guaranteed to always be deleted once (but not before) all shared_ptr objects that were holding it are destroyed (or have released it).

It does this using reference counting – it keeps a shared count of how many shared_ptr objects are holding the same pointer. This reference counting is done using atomic functions and is thus threadsafe."

“どのスレッドが最後にオブジェクトを完了させるのか分からない場合、各スレッドにオブジェクトを参照するshared_ptrを与えればよい。 しかし、各スレッドにそのオブジェクトは一つを与えることをここで注意しておく。shared_ptrクラスは以下の場合においてスレッドセーフではない。 2つのスレッドが同時にただ一つのshared_ptrオブジェクトにアクセスする場合において。 スレッド安全性は各スレッドが自身のshared_ptrオブジェクトにアクセスする場合にのみ保証される。”

“When you’re not sure which thread will finish needing an object last, you can simply give each thread a shared_ptr that references the object. However, note here that I said that you give each thread one such object. The shared_ptr class is not thread-safe for the case that two threads try to access the same shared_ptr object concurrently. Rather, thread-safety is ensured as long as each thread has their own shared_ptr objects (which may in turn all share+reference the same pointer).”

C++最適化手法

メモリ使用量が増えてもよいので速度を優先させたい場合の最適化方法を考えます。

  • 計算した値をキャッシュし計算回数を減らす
  • 同じ値の設定処理を何度も行わない
  • 関数の戻り値では、構造体の値を返す代わりに構造体のconst参照を返す
  • クラスの不要な関数を削除する、不要な機能のインターフェース実装があれば削除する
  • 不要なら空のデストラクタを定義しない

Effective Modern C++ ―C++11/14プログラムを進化させる42項目

Effective Modern C++ ―C++11/14プログラムを進化させる42項目

swift 循環参照

swift 循環参照の一例を挙げます。

iOSのソースを例にとります。

ViewController.swift

import UIKit
class ViewController: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()

        let model = Model()
        model.action()
    }
}

Model.swift

import Foundation

class Model {
    private let message:String = "hello"
    private var myclosure:((Void)->Void)?
    
    init()
    {
        print("Model init")
        
        myclosure = {
            print("\(self.message)")
        }
    }
    
    deinit
    {
        print("Model deinit")
    }
    
    func action(){
        myclosure?()
    }
}

ここでmodelはローカル変数ですから、viewDidLoadを抜けた時は破棄されるはずです。 しかし結果は以下のようになり、"Model deinit"が表示されませんからdeinitが呼ばれず、破棄されていないことが分かります。

Model init
hello

原因は、myclosureがselfを強参照した結果、modelがmyclosureを、myclosureがmodelを互いに参照してるためです。 ガベージコレクション方式でない、リファレンスカウンタ方式のメモリ管理方式を採用している言語ではこのようなことが起こります。 Modelのinit()を以下のように修正すると、破棄されるようになります。

init()
{
    print("Model init")
       
    myclosure = { [unowned self] in // <- 修正
        print("\(self.message)")
    }
}

結果

Model init
hello
Model deinit

このケースでは、actionメソッドを実行中にmodelのインスタンスがなくなってしまうことはありませんので、 [unowned self]を使えばよいです。
ただし、actionメソッドを実行中にmodelのインスタンスnilになってしまうことがあるような複雑なケースの場合は、[weak self]を使います。この場合、少しタイピング量が増えて、以下のような書き方になります。

init()
{
    print("Model init")
        
    myclosure = { [weak self] in 
        if let weakself = self { 
            print("\(weakself.message)")
        }
    }
}

selfがnilになってしまっても、オプショナルで判定して処理をスキップすることができます。

iOS/MacOSXのvDSP関数、多すぎてなかなか見つけられないのをなんとかする(応用編)

複素数を扱うためにvDSP.hで定義された構造体

typedef struct DSPComplex {
    float  real;
    float  imag;
} DSPComplex;

typedef struct DSPSplitComplex {
    float  * __nonnull realp;
    float  * __nonnull imagp;
} DSPSplitComplex;

1次元高速フーリエ変換

vDSP_create_fftsetup
vDSP_destroy_fftsetup

1次元高速フーリエ変換(In-Place Complex)

vDSP_fft_zip         テンポラリバッファ未使用
vDSP_fft_zipt        テンポラリバッファ使用

1次元高速フーリエ変換(Out-Of-Place Complex)

vDSP_fft_zop       テンポラリバッファ未使用
vDSP_fft_zopt      テンポラリバッファ使用

1次元高速フーリエ変換(In-Place Real)

vDSP_fft_zrip       テンポラリバッファ未使用
vDSP_fft_zript      テンポラリバッファ使用
vDSP_fftm_zrip      複数信号,テンポラリバッファ未使用
vDSP_fftm_zript     複数信号,テンポラリバッファ使用

vDSP_fft_zripの引数

vDSP_Length Log2N -> 要素数を2の何乗かで指定します。要素数が1024なら210=1024なので10を指定します。
FFTDirection
Direction -> kFFTDirection_Forward: フーリエ変換, kFFTDirection_Inverse : 逆変換

参考: Using Fourier Transforms

1次元高速フーリエ変換(Out-of-Place Real)

vDSP_fft_zrop       テンポラリバッファ未使用
vDSP_fft_zropt      テンポラリバッファ使用
vDSP_fftm_zrop      複数信号,テンポラリバッファ未使用
vDSP_fftm_zropt     複数信号,テンポラリバッファ使用

1次元高速フーリエ変換(固定長)

vDSP_FFT16_copv     16要素 interleaved
vDSP_FFT32_copv     32要素 interleaved
vDSP_FFT16_zopv     16要素 split
vDSP_FFT32_zopv     32要素 split

2次元高速フーリエ変換(In-Place Real)

vDSP_fft2d_zrip
vDSP_fft2d_zript

2次元高速フーリエ変換(Out-of-Place Real)

vDSP_fft2d_zrop
vDSP_fft2d_zropt

離散フーリエ変換

vDSP_DFT_zop_CreateSetup
vDSP_DFT_zrop_CreateSetup
vDSP_DFT_DestroySetup
vDSP_DFT_Execute

離散コサイン変換

vDSP_DCT_CreateSetup
vDSP_DCT_Execute

相関,畳み込み

vDSP_conv           2つの実ベクターの相関または畳み込み
vDSP_zconv          2つの複素ベクターの相関または畳み込み
vDSP_wiener         ウィーナフィルタとして使えるFIRフィルタの生成
vDSP_desamp         デシメーション,アンチエイリアスするFIRフィルタ
vDSP_zrdesamp       デシメーション,アンチエイリアスするComplex-real FIRフィルタ
vDSP_f3x3           3x3カーネルでの2次元畳み込みで画像をフィルタ
vDSP_f5x5           5x5カーネルでの2次元畳み込みで画像をフィルタ
vDSP_imgfir         1カーネルでの2次元畳み込みで画像をフィルタ

ウィンドウ

vDSP_blkman_window
vDSP_hamm_window
vDSP_hann_window

単一チャネル4次IIRフィルタ

vDSP_biquad_CreateSetup
vDSP_biquad_DestroySetup
vDSP_biquad

複数チャネル4次IIRフィルタ

vDSP_biquadm_CreateSetup
vDSP_biquadm_DestroySetup
vDSP_biquadm_ResetState
vDSP_biquadm_SetActiveFilters
vDSP_biquadm_SetCoefficientsSingle
vDSP_biquadm_SetCoefficientsDouble
vDSP_biquadm_SetTargetsSingle
vDSP_biquadm_SetTargetsDouble
vDSP_biquadm_biquadm

複素ベクター変換

vDSP_ctoz   DSPComplex から DSPSplitComplex へ変換
vDSP_ztoc   DSPSplitComplex から DSPSplitComplex へ変換

floatから24ビット整数への変換

vDSP_vsmfix24
vDSP_vsmfixu24
vDSP_vflt24
vDSP_vfltu24
vDSP_vfltsm24
vDSP_vfltsmu24