Rails3でjQueryを使う方法
インストール
gem 'jquery-rails'
jQueryをインストール。(prototype.js関連のファイルが削除される)
$ bundle install $ rails g jquery:install remove public/javascripts/controls.js remove public/javascripts/dragdrop.js remove public/javascripts/effects.js remove public/javascripts/prototype.js create public/javascripts/jquery.min.js create public/javascripts/jquery.js conflict public/javascripts/rails.js Overwrite public/javascripts/rails.js? (enter "h" for help) [Ynaqdh] y force public/javascripts/rails.js
プロジェクト作成時にprototype.jsが不要だと分かっている場合は-Jで生成を抑止できる。
$ rails new APP_NAME -J
動作確認
views/samples/index.html.hamlに以下の記述を追加。
3 %table#fade_table (snip) 19 :css 20 #fade_table { 21 display: none; 22 } 23 :javascript 24 window.onLoad = (function(){ 25 $('#fade_table').fadeIn('slow'); 26 })();
これでサーバを再起動して/samplesにアクセスするとデータのtableがフェードインしながら表示されます。
Rails3でRspecを使う方法
Haml、Sassを使えるようになったので今度はRspecを使えるようにします。
インストール
Rails3ではデフォルトでTest::Unit用のディレクトリやファイルが生成されるのですが、これは不要なのでプロジェクト作成時にTest::Unitを使わないようにします。
$ rails new rails3_rspec -T #-TでSkip Test::Unit
次にGemfileに必要なgemを記述します。
※同時にHaml、Sassを使う方法については以前の記事を参照のこと
Rails3とRuby1.9.2でHamlを使う方法
Rails3でSassを使う方法
gem 'haml-rails' group :development, :test do gem 'rspec-rails', '>= 2.0.0.beta.20' gem 'autotest' gem 'webrat' end
Gemfileができたらインストール
$ bundle install vendor/bundle $ rails generate rspec:install
動作確認
動作確認のためにscaffoldで一通りのファイルを生成してテストを実行
$ rails generate scaffold sample $ rake db:migrate $ rake db:test:prepare #テスト用DBを作成 $ rake spec ...............**............ (省略) 29 examples, 0 failures, 2 pending
こんな感じでRspecが使えるようになります。
また、autotestを使うとファイルに変更があったときに自動でテストが走るようになります。
$ bundle exec autotest
Rails3でSassを使う方法
せっかくHamlを使えるようにしたのでSassも使えるようにしたいところ。
とはいってもSassはHamlに同梱されているので前回の記事の手順でインストールまでは既に済んでいます。
設定
config/initializers/sass_config.rbを作成して以下の記述を追加。
#デフォルトのスタイルシートパスを削除 Sass::Plugin.remove_template_location("./public/stylesheets/sass") #app/stylesheetsをSass用のディレクトリに設定 Sass::Plugin.add_template_location("./app/stylesheets") #最小サイズのCSSに変換する Sass::Plugin.options[:style] = :compressed
デフォルトのパスでも問題ないのですが、Sassは.sassファイルから.cssファイルへコンパイルする仕組みのためソースファイルと生成されたCSSの場所を分離しておいたほうがスッキリすると思います。
またSassの変換結果のスタイルは以下のドキュメントに書かれていますので好きなものを設定してください。
http://sass-lang.com/docs/yardoc/file.SASS_REFERENCE.html#output_style
動作確認
Sass用のディレクトリとファイルを作成
$ mkdir app/stylesheets $ vim app/stylesheets/sample.sass
以下の記述をsample.sassに追加
h1 :font :size 200% :weight bold :border 16px solid #DD66CC :width 400px :padding 5px p :font-weight bold :border 16px solid #6666DD :width 400px :padding 5px
$ rails server
$ open http://localhost:3000/sample/index
こんな画面が表示されたら動作確認OK。public/stylesheets/sample.cssが生成されているはず。
Rails3とRuby1.9.2でHamlを使う方法
会社でRails3とHamlを使っているんですが、既に他の方が環境を作った上でコードを書いていたので、勉強のために一から環境構築をやってみました。
Rails環境構築
$ rvm 1.9.2 $ gem install rails $ rails new rails3_haml $ cd rails3_haml $ bundle install vendor/bundle #必要なgemをvender/bundleに一括インストール $ rails server
これでブラウザからhttp://localhost:3000にアクセスして動作していればひとまず最低限のRailsの環境構築はOK.
Hamlインストール
gem 'haml-rails'
haml-railsはRails3にHaml Generatorを追加するためのプラグインです。
元々はrails3-generatorsだったものがHaml部分だけ分離したものらしいです。
もう一度bundle install
$ bundle install
これで準備は完了。
config/application.rbにg.template_engine :hamlのような記述を追加する必要はありません。
あとは試しにcontrollerをgenerateして動作確認してみましょう。
$ rails g controller sample index create app/controllers/sample_controller.rb route get "sample/index" invoke haml #template_engineがhamlに変わっている exist app/views/sample create app/views/sample/index.html.haml invoke test_unit create test/functional/sample_controller_test.rb invoke helper identical app/helpers/sample_helper.rb invoke test_unit identical test/unit/helpers/sample_helper_test.rb $ rails server
これでhttp://localhost:3000/sample/indexにアクセスしてこんな画面が表示されればhamlが動作しています。
やってみた
makeplex salon:あなたのスキルで飯は食えるか? 史上最大のコーディングスキル判定 (1/2) - ITmedia エンタープライズ
3時間でできなければ優秀でないことが証明されてしまうことで一部で話題になっている問題をやってみた。
結果は...
“ほぼ確実に”優秀ではない!orz
とりあえずコード晒しとく。Rubyで書いたのにやけに長い。
真面目にアルゴリズムの勉強しないとだめだなぁ。
Kotsu = 1 Shuntsu = 2 Any = 3 class Array def Array.deepcopy(arr) Marshal.load(Marshal.dump(arr)) end def top self[self.length - 1] end def car if self.length > 0 self[0] else nil end end def cdr new_a = Array.deepcopy(self) new_a.shift new_a end def rest(i) new_a = Array.deepcopy(self) new_a.delete_at(i) new_a end end class Machi def initialize @machi = nil @atama = nil @triple = [] end attr_accessor :machi, :atama, :triple def eql?(m) self.hash == m.hash end def hash str = @triple.sort.join str += @atama.join if @atama str += @machi.join if @machi str.hash end def print @triple.sort! @triple.each{|t| STDOUT.print "(#{t.join})" } STDOUT.print "(#{@atama.join})" if @atama && @atama.length > 0 STDOUT.print "[#{@machi.join}]" if @machi STDOUT.print "\n" end def deepcopy new_machi = Machi.new new_machi.machi = Array.deepcopy(self.machi) new_machi.atama = Array.deepcopy(self.atama) new_machi.triple = Array.deepcopy(self.triple) new_machi end end def check_four_tiles(n) $four_tiles.each{|t| return true if t == n} return false end def search_same(temp, rest) return {} if temp.length == 0 v = temp[0] for i in 0...rest.length if v == rest[i] return {v => rest.rest(i)} end end return {} end def search_forward_backword(temp, rest) return {} if temp.length == 0 if temp.length == 2 return {} if temp[1] - temp[0] != 1 end forward = temp.first - 1 backward = temp.last + 1 result = {} for i in 0...rest.length if rest[i] == forward || rest[i] == backward if !result.key?(rest[i]) result[rest[i]] = rest.rest(i) end end end return result end def search_aida_machi(temp, rest) return {} if temp.length != 1 forward = temp.first - 2 backward = temp.last + 2 result = {} for i in 0...rest.length if rest[i] == forward || rest[i] == backward if !result.key?(rest[i]) result[rest[i]] = rest.rest(i) end end end return result end def get_machi(shuntsu2) return [] if shuntsu2.length != 2 case shuntsu2[1] - shuntsu2[0] when 1 ret = [] ret << shuntsu2[0] - 1 if shuntsu2[0] > 1 ret << shuntsu2[1] + 1 if shuntsu2[1] < 9 return ret when 2 return [shuntsu2[1] - 1] else return [] end end def search(type, rest, temp, result) if rest.length == 0 && temp.length == 0 $final_result << result return end case type when Kotsu search_same(temp, rest).each{|num, new_rest| new_result = result.deepcopy new_result.triple << (temp + [num]) search(Any, new_rest, [], new_result) } when Shuntsu search_forward_backword(temp, rest).each{|num, new_rest| new_result = result.deepcopy new_result.triple << (temp + [num]).sort search(Any, new_rest, [], new_result) } when Any if temp.length == 0 search(Any, rest.cdr, temp + [rest.car], result.deepcopy) if !result.atama && !result.machi && !check_four_tiles(rest.car) #一個待ちパターン new_result = result.deepcopy new_result.machi = [rest.car] new_result.atama = [] search(Any, rest.cdr, [], new_result) end elsif temp.length == 1 search_same(temp, rest).each{|num, new_rest| search(Kotsu, new_rest, temp + [num], result.deepcopy) if !result.atama #頭パターン new_result = result.deepcopy new_result.atama = temp + [num] search(Any, new_rest, [], new_result) end if !result.machi && !check_four_tiles(num) #刻子待ちパターン new_result = result.deepcopy new_result.machi = temp + [num] search(Any, new_rest, [], new_result) end } search_forward_backword(temp, rest).each{|num, new_rest| search(Shuntsu, new_rest, (temp + [num]).sort, result.deepcopy) exist_machi = false get_machi((temp + [num]).sort) .map{|m| !check_four_tiles(m)} .each{|m| exist_machi |= m} if !result.machi && exist_machi #順子待ちパターン new_result = result.deepcopy new_result.machi = (temp + [num]).sort search(Any, new_rest, [], new_result) end } if !result.machi search_aida_machi(temp, rest).each{|num, new_rest| exist_machi = false get_machi((temp + [num]).sort) .map{|m| !check_four_tiles(m)} .each{|m| exist_machi |= m} if exist_machi new_result = result.deepcopy new_result.machi = (temp + [num]).sort search(Any, new_rest, [], new_result) end } end end end end def check_number_of_every_tile(tiles) arr = [] ret = [] arr.fill(0, 1..9) tiles.each{|t| arr[t.to_i] += 1} for i in 1..9 ret << i if arr[i] == 4 end ret end $final_result = [] $four_tiles = [] input = [ "1112224588899", "1122335556799", "1112223335559", "1223344888999", "1112345678999", "1113335558888", "1222233456788", "1223567789789" ] input.each{|i| $final_result.clear $four_tiles.clear puts i nums = i.split(//).map{|n| n.to_i}.sort $four_tiles = check_number_of_every_tile(nums) search(Any, nums, [], Machi.new) $final_result.uniq.each{|r| r.print} puts }
id<Protocol>と書くときの@protocolの宣言の方法
delegateをメンバに持つようなクラスを作るとき、delegateの型をidじゃなく特定のProtocolが実装されていることを指定したい時がある。そういう時はid
調べてみたところ以下のページに解説があった。
id<Protocol>, Retain/Release and Protocol Inheritance | Mobile Orchard
Protocolの宣言時にNSObject Protocolを継承すればいいらしい。
つまり
@protocol MyProtocol <NSObject> - (void)hoge; @end
と書けば警告が出なくなる。
iPhoneで画像処理の高速化実験
iPhoneでOpenCVが使えることを知ったので、ついでに画像処理の高速化について今までもやもやしていたことをはっきりさせようと思い立ち実験してみた。
題材は画像のモザイク化
githubにあったこちら
niw/iphone_opencv_test · GitHub
のサンプルコードからforkさせてもらってモザイク化処理を追加した。
y310/iphone_opencv_test · GitHub
コンパイルオプションはXcodeのデフォルトで-Os(最も高速で最小)、iPhoneOS3.1.2のReleaseビルドで実験した。
Step1 とりあえず何も考えずに実装
モザイク処理メソッドから抜粋。
元画像のmSize*mSize領域のRGB平均値を計算し、出力先画像の同領域をその平均値で埋める、という処理。
int mSize = [mosaicSize intValue]; IplImage *img = [self CreateIplImageFromUIImage:imageView.image]; IplImage *dst = cvCreateImage(cvGetSize(img), img->depth, img->nChannels); unsigned int r, g, b; int p, pp, c; for (int i = 0; i < img->height; i += mSize) { for (int j = 0; j < img->width; j += mSize) { r = b = g = 0; p = (i * img->width + j) * 3; c = 0; for (int k = 0; k < mSize; k++) { if (i + k < img->height) { for (int l = 0; l < mSize; l++) { if (j + l < img->width) { pp = p + (k * img->width + l) * 3; b += (unsigned char)img->imageData[pp]; g += (unsigned char)img->imageData[pp + 1]; r += (unsigned char)img->imageData[pp + 2]; c++; } } } } r /= c; g /= c; b /= c; for (int k = 0; k < mSize; k++) { if (i + k < img->height) { for (int l = 0; l < mSize; l++) { if (j + l < img->width) { pp = p + (k * img->width + l) * 3; dst->imageData[pp] = (char)r; dst->imageData[pp + 1] = (char)g; dst->imageData[pp + 2] = (char)b; } } } } } } cvReleaseImage(&img); imageView.image = [self UIImageFromIplImage:dst]; cvReleaseImage(&dst);
この処理でmSizeを2,4,8,16,32,64,128で順番に実行した時の結果(5回の平均、単位は秒)
Step1 | |
2 | 0.2202906 |
4 | 0.1995558 |
8 | 0.1747878 |
16 | 0.1725956 |
32 | 0.1691068 |
64 | 0.166092 |
128 | 0.1662554 |
average: | 0.1812404 |
これを基準に高速化してみる。
Step2 img->widthをwidthに変換(heightも)
forループなどで使っているimg->widthが毎回imgからwidthをたどっていて効率が悪いのではないかと考え、ループの前で値を保存する用に変更。
int width = img->width; int height = img->height; for (int i = 0; i < height; i += mSize) { for (int j = 0; j < width; j += mSize) { r = b = g = 0; p = (i * width + j) * 3; c = 0; for (int k = 0; k < mSize; k++) { if (i + k < height) { for (int l = 0; l < mSize; l++) { if (j + l < width) { pp = p + (k * width + l) * 3; b += (unsigned char)img->imageData[pp]; g += (unsigned char)img->imageData[pp + 1]; r += (unsigned char)img->imageData[pp + 2]; c++; } } } } r /= c; g /= c; b /= c; for (int k = 0; k < mSize; k++) { if (i + k < height) { for (int l = 0; l < mSize; l++) { if (j + l < width) { pp = p + (k * width + l) * 3; dst->imageData[pp] = (char)r; dst->imageData[pp + 1] = (char)g; dst->imageData[pp + 2] = (char)b; } } } } } }
実行結果
Step1 | Step2 | |
2 | 0.2202906 | 0.2058164 |
4 | 0.1995558 | 0.1892952 |
8 | 0.1747878 | 0.1648164 |
16 | 0.1725956 | 0.1648778 |
32 | 0.1691068 | 0.163123 |
64 | 0.166092 | 0.1677026 |
128 | 0.1662554 | 0.1650716 |
average: | 0.1812404 | 0.174386 |
ちょっと早くなった。
Step3 ifの削減
画像サイズがmSizeで割り切れない時のためにif (i + k < height)という形でループ内でifの判定を行っていた部分を、事前にkMax,lMaxにループの最大値を保存することで削除した。
int kMax, lMax; for (int i = 0; i < height; i += mSize) { for (int j = 0; j < width; j += mSize) { r = b = g = 0; p = (i * width + j) * 3; c = 0; kMax = i + mSize < height ? mSize : height - i; lMax = j + mSize < width ? mSize : width - j; for (int k = 0; k < kMax; k++) { for (int l = 0; l < lMax; l++) { pp = p + (k * width + l) * 3; b += (unsigned char)img->imageData[pp]; g += (unsigned char)img->imageData[pp + 1]; r += (unsigned char)img->imageData[pp + 2]; c++; } } r /= c; g /= c; b /= c; for (int k = 0; k < kMax; k++) { for (int l = 0; l < lMax; l++) { pp = p + (k * width + l) * 3; dst->imageData[pp] = (char)r; dst->imageData[pp + 1] = (char)g; dst->imageData[pp + 2] = (char)b; } } } }
実行結果
Step1 | Step2 | Step3 | |
2 | 0.2202906 | 0.2058164 | 0.1998938 |
4 | 0.1995558 | 0.1892952 | 0.1865738 |
8 | 0.1747878 | 0.1648164 | 0.1517498 |
16 | 0.1725956 | 0.1648778 | 0.1577388 |
32 | 0.1691068 | 0.163123 | 0.1486748 |
64 | 0.166092 | 0.1677026 | 0.1521254 |
128 | 0.1662554 | 0.1650716 | 0.1549348 |
average: | 0.1812404 | 0.174386 | 0.1645274 |
Step2から約0.01秒早くなった。
Step4 img->imageDataも削減&c++をc=kMax*lMaxに
width,heightと同じ考え方でimg->imageDataもimgImageDataに変換。
あと対象領域の平均値計算に使っていたcを毎回ループ内でカウントするのをやめてkMax*lMaxで計算。(最初に気付けと思ったがStep1の段階ではifの判定で数が変わるため事前には計算できなかった)
char *imgImageData = img->imageData; char *dstImageData = dst->imageData; for (int i = 0; i < height; i += mSize) { for (int j = 0; j < width; j += mSize) { r = b = g = 0; p = (i * width + j) * 3; kMax = i + mSize < height ? mSize : height - i; lMax = j + mSize < width ? mSize : width - j; c = kMax * lMax; for (int k = 0; k < kMax; k++) { for (int l = 0; l < lMax; l++) { pp = p + (k * width + l) * 3; b += (unsigned char)imgImageData[pp]; g += (unsigned char)imgImageData[pp + 1]; r += (unsigned char)imgImageData[pp + 2]; } } r /= c; g /= c; b /= c; for (int k = 0; k < kMax; k++) { for (int l = 0; l < lMax; l++) { pp = p + (k * width + l) * 3; dstImageData[pp] = (char)r; dstImageData[pp + 1] = (char)g; dstImageData[pp + 2] = (char)b; } } } }
実行結果
Step1 | Step2 | Step3 | Step4 | |
2 | 0.2202906 | 0.2058164 | 0.1998938 | 0.1796986 |
4 | 0.1995558 | 0.1892952 | 0.1865738 | 0.1683434 |
8 | 0.1747878 | 0.1648164 | 0.1517498 | 0.148594 |
16 | 0.1725956 | 0.1648778 | 0.1577388 | 0.1463726 |
32 | 0.1691068 | 0.163123 | 0.1486748 | 0.1627986 |
64 | 0.166092 | 0.1677026 | 0.1521254 | 0.1537092 |
128 | 0.1662554 | 0.1650716 | 0.1549348 | 0.151777 |
average: | 0.1812404 | 0.174386 | 0.1645274 | 0.1587564 |
ここまでで初期状態から約0.023秒速くなった。
Step5 ppの計算回数を削減
k-lループの内側で毎回現在の注目ピクセル座標ppを計算していたが、kループの内側で計算すればlループ内はインクリメントだけでいける。
for (int i = 0; i < height; i += mSize) { for (int j = 0; j < width; j += mSize) { r = b = g = 0; p = (i * width + j) * 3; kMax = i + mSize < height ? mSize : height - i; lMax = j + mSize < width ? mSize : width - j; c = kMax * lMax; for (int k = 0; k < kMax; k++) { pp = p + (k * width * 3); for (int l = 0; l < lMax; l++) { b += (unsigned char)imgImageData[pp++]; g += (unsigned char)imgImageData[pp++]; r += (unsigned char)imgImageData[pp++]; } } r /= c; g /= c; b /= c; for (int k = 0; k < kMax; k++) { pp = p + (k * width * 3); for (int l = 0; l < lMax; l++) { dstImageData[pp++] = (char)r; dstImageData[pp++] = (char)g; dstImageData[pp++] = (char)b; } } } }
実行結果
Step1 | Step2 | Step3 | Step4 | Step5 | |
2 | 0.2202906 | 0.2058164 | 0.1998938 | 0.1796986 | 0.1845956 |
4 | 0.1995558 | 0.1892952 | 0.1865738 | 0.1683434 | 0.1641452 |
8 | 0.1747878 | 0.1648164 | 0.1517498 | 0.148594 | 0.1551546 |
16 | 0.1725956 | 0.1648778 | 0.1577388 | 0.1463726 | 0.1454828 |
32 | 0.1691068 | 0.163123 | 0.1486748 | 0.1627986 | 0.147119 |
64 | 0.166092 | 0.1677026 | 0.1521254 | 0.1537092 | 0.1495658 |
128 | 0.1662554 | 0.1650716 | 0.1549348 | 0.151777 | 0.1533584 |
average: | 0.1812404 | 0.174386 | 0.1645274 | 0.1587564 | 0.15706 |
一応速くなった…かな?ちょっと誤差かもしれない微妙な差。
Step6 掛け算を削減
掛け算より足し算の方が速いと聞いたことがあったのでppの計算から掛け算をなくしてみる。
Step5でppは繰り返しの度にwidth*3を増やす処理と同値だったので毎回width*3を足すようにする。
for (int i = 0; i < height; i += mSize) { for (int j = 0; j < width; j += mSize) { r = b = g = 0; p = (i * width + j) * 3; kMax = i + mSize < height ? mSize : height - i; lMax = j + mSize < width ? mSize : width - j; c = kMax * lMax; pp = offset = p; for (int k = 0; k < kMax; k++) { for (int l = 0; l < lMax; l++) { b += (unsigned char)imgImageData[pp++]; g += (unsigned char)imgImageData[pp++]; r += (unsigned char)imgImageData[pp++]; } offset += stride; pp = offset; } r /= c; g /= c; b /= c; pp = offset = p; for (int k = 0; k < kMax; k++) { for (int l = 0; l < lMax; l++) { dstImageData[pp++] = (char)r; dstImageData[pp++] = (char)g; dstImageData[pp++] = (char)b; } offset += stride; pp = offset; } } }
実行結果
Step1 | Step2 | Step3 | Step4 | Step5 | Step6 | |
2 | 0.2202906 | 0.2058164 | 0.1998938 | 0.1796986 | 0.1845956 | 0.186088 |
4 | 0.1995558 | 0.1892952 | 0.1865738 | 0.1683434 | 0.1641452 | 0.1658398 |
8 | 0.1747878 | 0.1648164 | 0.1517498 | 0.148594 | 0.1551546 | 0.1556316 |
16 | 0.1725956 | 0.1648778 | 0.1577388 | 0.1463726 | 0.1454828 | 0.14568 |
32 | 0.1691068 | 0.163123 | 0.1486748 | 0.1627986 | 0.147119 | 0.1497236 |
64 | 0.166092 | 0.1677026 | 0.1521254 | 0.1537092 | 0.1495658 | 0.1495576 |
128 | 0.1662554 | 0.1650716 | 0.1549348 | 0.151777 | 0.1533584 | 0.1559454 |
average: | 0.1812404 | 0.174386 | 0.1645274 | 0.1587564 | 0.15706 | 0.1583524 |
遅くなった…。1行の掛け算だったのが足し算と代入の2行に別れたせいだろうか。
理由は分からないがとりあえずあまり効果はないようだ。
Step7 構造体を使って代入を減らす
出力先画像への代入処理が同じ値をRGBの3回分毎回やるのは非効率な気がするのでunsigned charのRGB値を保持する構造体を作って、1回で構造体まるごと代入するようにしてみた。
正直この方法はchar*のimageDataを無理矢理struct Rgb*にキャストしてたりするので危険だと思う。
構造体もきっちりunsigned char*3つ分になる保証はないはず。
struct Rgb *dstImageData = (struct Rgb*)dst->imageData; for (int i = 0; i < height; i += mSize) { kMax = i + mSize < height ? mSize : height - i; for (int j = 0; j < width; j += mSize) { r = b = g = 0; p = (i * width + j) * 3; lMax = j + mSize < width ? mSize : width - j; c = kMax * lMax; pp = offset = p; for (int k = 0; k < kMax; k++) { for (int l = 0; l < lMax; l++) { b += (unsigned char)imgImageData[pp++]; g += (unsigned char)imgImageData[pp++]; r += (unsigned char)imgImageData[pp++]; } offset += stride; pp = offset; } rgb.r = r / c; rgb.g = g / c; rgb.b = b / c; pp = offset = i * width + j; for (int k = 0; k < kMax; k++) { for (int l = 0; l < lMax; l++) { dstImageData[pp++] = rgb; } offset += width; pp = offset; } } }
実行結果
Step1 | Step2 | Step3 | Step4 | Step5 | Step6 | Step7 | |
2 | 0.2202906 | 0.2058164 | 0.1998938 | 0.1796986 | 0.1845956 | 0.186088 | 0.2409104 |
4 | 0.1995558 | 0.1892952 | 0.1865738 | 0.1683434 | 0.1641452 | 0.1658398 | 0.203338 |
8 | 0.1747878 | 0.1648164 | 0.1517498 | 0.148594 | 0.1551546 | 0.1556316 | 0.184218 |
16 | 0.1725956 | 0.1648778 | 0.1577388 | 0.1463726 | 0.1454828 | 0.14568 | 0.1747662 |
32 | 0.1691068 | 0.163123 | 0.1486748 | 0.1627986 | 0.147119 | 0.1497236 | 0.1759188 |
64 | 0.166092 | 0.1677026 | 0.1521254 | 0.1537092 | 0.1495658 | 0.1495576 | 0.1728034 |
128 | 0.1662554 | 0.1650716 | 0.1549348 | 0.151777 | 0.1533584 | 0.1559454 | 0.1736168 |
average: | 0.1812404 | 0.174386 | 0.1645274 | 0.1587564 | 0.15706 | 0.1583524 | 0.1893676 |
さらに遅くなった…。というかStep1より遅い。なんでこんなに遅くなるんだろうか。
そろそろアセンブラレベルでちゃんと見ないといけない気がしてきた。
とりあえずまとめ
今までなんとなくこう書いた方が速くなるかなぁと思っていたことが、実際効果があったり逆効果だったりすることがわかった。
結局この辺どれが本当に正しいかはマシン語レベルで見ないとわからなそう。まだまだコンパイラの最適化だけに頼ってちゃいけないんですね。
追記
通りすがりさんからアイディアをいただいたので組み込んでみた。
void mosaic(char *imgImageData, char *dstImageData, int width, int sX, int sY, int w, int h, int wmSize, int hmSize, int stride) { int dp = width * 3; int dpp = wmSize * 3; unsigned int r, g, b; int p, pp, c; c = wmSize * hmSize; for (int y = sY; y < h; y++) { p = y * dp * hmSize; for (int x = sX; x < w; x++, p += dpp) { r = b = g = 0; pp = p; for (int yy = 0; yy < hmSize; yy++) { for (int xx = 0; xx < wmSize; xx++) { b += (unsigned char)imgImageData[pp++]; g += (unsigned char)imgImageData[pp++]; r += (unsigned char)imgImageData[pp++]; } pp += stride; } r /= c; g /= c; b /= c; pp = p; for (int yy = 0; yy < hmSize; yy++) { for (int xx = 0; xx < wmSize; xx++) { dstImageData[pp++] = (char)r; dstImageData[pp++] = (char)g; dstImageData[pp++] = (char)b; } pp += stride; } } } } { ... int wBlockCnt = width / mSize; int hBlockCnt = height / mSize; /* 割り切れる領域 */ mosaic(imgImageData, dstImageData, width, 0, 0, wBlockCnt, hBlockCnt, mSize, mSize, (width - mSize) * 3); /* 割り切れない領域 */ if ( width % mSize > 0) { mosaic(imgImageData, dstImageData, width, wBlockCnt, 0, wBlockCnt + 1, hBlockCnt, width % mSize, mSize, (width - (width % mSize)) * 3); } if ( height % mSize > 0) { mosaic(imgImageData, dstImageData, width, 0, hBlockCnt, wBlockCnt, hBlockCnt + 1, mSize, height % mSize, (width - mSize) * 3); } if ( width % mSize > 0 && height % mSize > 0) { mosaic(imgImageData, dstImageData, width, wBlockCnt, hBlockCnt, wBlockCnt + 1, hBlockCnt + 1, width % mSize, height % mSize, (width - (width % mSize)) * 3); } ... }
Step1 | Step2 | Step3 | Step4 | Step5 | Step6 | Step7 | Step8 | |
2 | 0.2202906 | 0.2058164 | 0.1998938 | 0.1796986 | 0.1845956 | 0.186088 | 0.2409104 | 0.1631222 |
4 | 0.1995558 | 0.1892952 | 0.1865738 | 0.1683434 | 0.1641452 | 0.1658398 | 0.203338 | 0.15106 |
8 | 0.1747878 | 0.1648164 | 0.1517498 | 0.148594 | 0.1551546 | 0.1556316 | 0.184218 | 0.1435718 |
16 | 0.1725956 | 0.1648778 | 0.1577388 | 0.1463726 | 0.1454828 | 0.14568 | 0.1747662 | 0.142803 |
32 | 0.1691068 | 0.163123 | 0.1486748 | 0.1627986 | 0.147119 | 0.1497236 | 0.1759188 | 0.1379126 |
64 | 0.166092 | 0.1677026 | 0.1521254 | 0.1537092 | 0.1495658 | 0.1495576 | 0.1728034 | 0.13246 |
128 | 0.1662554 | 0.1650716 | 0.1549348 | 0.151777 | 0.1533584 | 0.1559454 | 0.1736168 | 0.1457272 |
average: | 0.1812404 | 0.174386 | 0.1645274 | 0.1587564 | 0.15706 | 0.1583524 | 0.1893676 | 0.1452366 |
最速記録更新!余計なif文がなくなったのが大きいのかな。
さらに追記
さらに改良コードを貼っていただいたので取り込んでみました。
が、Step1より遅い結果に...正確な原因はわかりませんが剰余演算とif文がループ内に増えた分と配列の要素参照が増えたところあたりがアルゴリズムの改善効果以上に効いてるのでしょうか。
Step1 | Step2 | Step3 | Step4 | Step5 | Step6 | Step7 | Step8 | Step9 | |
2 | 0.2202906 | 0.2058164 | 0.1998938 | 0.1796986 | 0.1845956 | 0.186088 | 0.2409104 | 0.1631222 | 0.297052 |
4 | 0.1995558 | 0.1892952 | 0.1865738 | 0.1683434 | 0.1641452 | 0.1658398 | 0.203338 | 0.15106 | 0.26692 |
8 | 0.1747878 | 0.1648164 | 0.1517498 | 0.148594 | 0.1551546 | 0.1556316 | 0.184218 | 0.1435718 | 0.2529658 |
16 | 0.1725956 | 0.1648778 | 0.1577388 | 0.1463726 | 0.1454828 | 0.14568 | 0.1747662 | 0.142803 | 0.2481548 |
32 | 0.1691068 | 0.163123 | 0.1486748 | 0.1627986 | 0.147119 | 0.1497236 | 0.1759188 | 0.1379126 | 0.2526098 |
64 | 0.166092 | 0.1677026 | 0.1521254 | 0.1537092 | 0.1495658 | 0.1495576 | 0.1728034 | 0.13246 | 0.248334 |
128 | 0.1662554 | 0.1650716 | 0.1549348 | 0.151777 | 0.1533584 | 0.1559454 | 0.1736168 | 0.1457272 | 0.2471866 |
average: | 0.1812404 | 0.174386 | 0.1645274 | 0.1587564 | 0.15706 | 0.1583524 | 0.1893676 | 0.1452366 | 0.2590316 |