Symfoware

Symfowareについての考察blog

Nim デバッグビルドとリリースビルドの実行速度

Nimで郵便番号と住所をデータベースに登録するプログラムを作ってみました。
Nim トランザクションを使用し、郵便番号と住所情報をMySQLに登録する

ふと、デバッグビルドとリリースビルドで実行速度が違うのか気になったので調べてみます。
また、参考として同様のPythonプログラムの実行速度も見てみます。

Python



PythonからMySQLへの接続はmysql-connector-python-rfを使用しました。
プログラムはこんな感じになりました。


  1. import codecs
  2. import mysql.connector
  3. def mystrip(s):
  4.     return s.strip('"')
  5. con = mysql.connector.connect(
  6.     host = 'localhost',
  7.     port = 3306,
  8.     user = 'admin',
  9.     password = 'P@ssw0rd',
  10.     database = 'sample',
  11. )
  12. con.autocommit = False
  13. cur = con.cursor()
  14. zipcodeSet = set()
  15. with codecs.open('KEN_ALL.CSV', 'r', 'CP932') as f:
  16.     for line in f:
  17.         # カンマで分割
  18.         data = line.strip().split(',')
  19.         # 2番目:郵便番号
  20.         zipcode = mystrip(data[2])
  21.         # 6,7,8番目:住所
  22.         address = mystrip(data[6]) + mystrip(data[7]) + mystrip(data[8])
  23.         # 郵便番号の重複チェック
  24.         if zipcode in zipcodeSet:
  25.             print("skip " + zipcode)
  26.             continue
  27.         zipcodeSet.add(zipcode)
  28.         # 登録実行
  29.         cur.execute("INSERT INTO post (zip_code, address) VALUES (%s, %s)", (zipcode, address))
  30. con.commit()
  31. cur.close()
  32. con.close()







実測



データベースへの登録が最も時間がかかりそうなので、
あまり差がでないかもしれませんがテストしてみます。

Nimのバージョンは0.18.0。
Pythonのバージョンは3.6.5です。

デバッグビルドでコンパイル。


$ nim c sample.nim



timeコマンドで時間を計測します。


$ time ./sample
real    0m22.027s
user    0m4.703s
sys    0m1.706s




次にリリースビルド。


$ nim c -d:release sample.nim
$ time ./sample
real    0m18.933s
user    0m1.613s
sys    0m1.557s



ちゃんと高速になりましたね。
※3回測定しましたが、概ね同様の実行時間でした。

最後にPython。


$ time python3 comp.py
real    0m33.222s
user    0m10.633s
sys    0m3.123s




データベースへの登録処理なんで差は出ないだろうと思っていましたが、
Nimで書くと高速に処理されますね。

テーマ:プログラミング - ジャンル:コンピュータ

  1. 2018/08/27(月) 23:32:12|
  2. nim
  3. | トラックバック:0
  4. | コメント:0
  5. | 編集

Nim トランザクションを使用し、郵便番号と住所情報をMySQLに登録する

読み仮名データの促音・拗音を小書きで表記するもの(zip形式)
NimでこちらのデータをMySQLに登録してみようと思います。

ファイルの読み込みと重複排除



ファイルの読み込みや郵便番号の重複チェックはこちら。
Nim 文字コードを指定したファイルの読み書き
Nim 文字列の結合、分割とトリム(strutils, split, strip)
Nim HashSetの使い方


データベース接続と登録



データベースへの接続、登録はこちら。
Nim MySQLに文字コードを指定して接続し、データの検索、登録を行う(db_mysql)

登録用にこんなテーブルを用意しておきました。


  1. CREATE TABLE post (
  2. zip_code CHAR(7) NOT NULL PRIMARY KEY,
  3. address VARCHAR(255) NOT NULL
  4. );





サンプル



試行錯誤の末のサンプルはこんな感じになりました。


  1. import strutils
  2. import encodings
  3. import sets
  4. import db_mysql
  5. proc mystrip(s: string):string =
  6.     return strip(s, chars={'"'})
  7. proc main() =
  8.     let enc = encodings.open("UTF-8", "CP932")
  9.     let f : File = open("KEN_ALL.CSV" ,FileMode.fmRead)
  10.     let db = open("localhost", "root", "P@ssw0rd", "sample")
  11.     discard setEncoding(db, "utf8mb4")
  12.     
  13.     defer:
  14.         close(enc)
  15.         close(f)
  16.         db.close()
  17.     var zipcodeSet = initSet[string]()
  18.     while not f.endOfFile:
  19.         # 1行読み込み
  20.         let line = enc.convert(f.readLine)
  21.         # カンマで分割
  22.         let data = split(line, ',')
  23.         # 2番目:郵便番号
  24.         let zipcode = mystrip(data[2])
  25.         # 6,7,8番目:住所
  26.         let address = mystrip(data[6]) & mystrip(data[7]) & mystrip(data[8])
  27.         # 郵便番号の重複チェック
  28.         if zipcode in zipcodeSet:
  29.             echo "skip " & zipcode
  30.             continue
  31.         zipcodeSet.incl(zipcode)
  32.         # 登録実行
  33.         db.exec(sql"INSERT INTO post (zip_code, address) VALUES (?, ?)", zipcode, address)
  34. main()



このプログラムを動かしてみましたが、びっくりするほど遅いです。
途中で実行を停止しました。



トランザクション



ちゃんとトランザクションをかけるように修正します。


  1. import strutils
  2. import encodings
  3. import sets
  4. import db_mysql
  5. proc mystrip(s: string):string =
  6.     return strip(s, chars={'"'})
  7. proc main() =
  8.     let enc = encodings.open("UTF-8", "CP932")
  9.     let f : File = open("KEN_ALL.CSV" ,FileMode.fmRead)
  10.     # データベースに接続し文字コードをセット
  11.     let db = open("localhost", "root", "P@ssw0rd", "sample")
  12.     discard setEncoding(db, "utf8mb4")
  13.     # トランザクション開始
  14.     db.exec(sql"START TRANSACTION")
  15.     defer:
  16.         close(enc)
  17.         close(f)
  18.         db.close()
  19.     var zipcodeSet = initSet[string]()
  20.     while not f.endOfFile:
  21.         # 1行読み込み
  22.         let line = enc.convert(f.readLine)
  23.         # カンマで分割
  24.         let data = split(line, ',')
  25.         # 2番目:郵便番号
  26.         let zipcode = mystrip(data[2])
  27.         # 6,7,8番目:住所
  28.         let address = mystrip(data[6]) & mystrip(data[7]) & mystrip(data[8])
  29.         # 郵便番号の重複チェック
  30.         if zipcode in zipcodeSet:
  31.             echo "skip " & zipcode
  32.             continue
  33.         zipcodeSet.incl(zipcode)
  34.         # 登録実行
  35.         db.exec(sql"INSERT INTO post (zip_code, address) VALUES (?, ?)", zipcode, address)
  36.     db.exec(sql"COMMIT")
  37. main()



これで現実的な速度でプログラムが終了するようになりました。

テーマ:プログラミング - ジャンル:コンピュータ

  1. 2018/08/27(月) 22:55:44|
  2. nim
  3. | トラックバック:0
  4. | コメント:0
  5. | 編集

Nim HashSetの使い方

Nimで重複を許さないHashSetの使い方メモです。

sets



こちらが参考になります。
Module sets

「initSet」で初期化済みのHashSetを生成。
「incl」で値を追加します。


  1. import sets
  2. var values = initSet[int]()
  3. values.incl(2)
  4. values.incl(4)
  5. values.incl(6)
  6. values.incl(2)
  7. echo values.len
  8. echo "----------------"
  9. for value in values:
  10.     echo value




実行結果


$ nim c -r sample.nim
3
----------------
2
4
6





リストからHashSetへの変換(toSet)



toSetでリストからHashSetを生成できます。


  1. import sets
  2. var values = toSet([2, 4, 6])
  3. values.incl(2)
  4. echo values.len
  5. echo "----------------"
  6. for value in values:
  7.     echo value





値の削除(excl)



追加した値を削除するには「excl」を使用します。


  1. import sets
  2. var values = toSet([2, 4, 6])
  3. values.excl(2)
  4. echo values.len
  5. echo "----------------"
  6. for value in values:
  7.     echo value




実行結果


$ nim c -r sample.nim
2
----------------
4
6






値の存在チェック(contains)



HashSetの中に指定した値が存在するかチェックするには「contains」を使用します。


  1. import sets
  2. var values = toSet([2, 4, 6])
  3. echo values.contains(2)
  4. echo values.contains(3)




実行結果


$ nim c -r sample.nim
true
false

テーマ:プログラミング - ジャンル:コンピュータ

  1. 2018/08/27(月) 22:21:07|
  2. nim
  3. | トラックバック:0
  4. | コメント:0
  5. | 編集

Nim MySQLに文字コードを指定して接続し、データの検索、登録を行う(db_mysql)

NimでMySQLに接続してみます。

db_mysql



mysql, postgresql, sqliteは接続用のモジュールが用意されているようです。
こちらを参考にしました。
Module db_mysql

登録のサンプルです。


  1. import db_mysql
  2. let db = open("localhost", "root", "P@ssw0rd", "sample")
  3. db.exec(sql"INSERT INTO t (id, value) VALUES (?, ?)", 1, "登録テスト")
  4. db.close()



登録したデータを検索すると文字化けしています。


mysql> select * from t;
+------+-----------------------------------+
| id | value                             |
+------+-----------------------------------+
|    1 | 登録テスト                 |
+------+-----------------------------------+
1 row in set (0.00 sec)




「setEncoding」で文字コードを指定してリトライ。


  1. import db_mysql
  2. let db = open("localhost", "root", "P@ssw0rd", "sample")
  3. discard setEncoding(db, "utf8mb4")
  4. db.exec(sql"INSERT INTO t (id, value) VALUES (?, ?)", 1, "登録テスト")
  5. db.close()




これで文字化けせずに登録できました。


mysql> select * from t;
+------+-----------------+
| id | value         |
+------+-----------------+
|    1 | 登録テスト     |
+------+-----------------+
1 row in set (0.00 sec)





データの検索



登録したデータを検索してみます。


  1. import db_mysql
  2. let db = open("localhost", "root", "P@ssw0rd", "sample")
  3. discard setEncoding(db, "utf8mb4")
  4. for row in db.fastRows(sql"select * from t"):
  5.     echo row[0]
  6.     echo row[1]
  7. db.close()



実行結果


$ nim c -r sample.nim
1
登録テスト




ドキュメントを見ると、fastRowsは途中でループをbreakして抜けると壊れるので、
全件フェッチするときのみ利用してねとのこと。

フェッチの途中でループを抜けるときは、instantRowsを使用するか、


  1. import db_mysql
  2. let db = open("localhost", "root", "P@ssw0rd", "sample")
  3. discard setEncoding(db, "utf8mb4")
  4. for row in db.instantRows(sql"select * from t"):
  5.     echo row[0]
  6.     echo row[1]
  7. db.close()



rowsを利用すれば良いようです。


  1. import db_mysql
  2. let db = open("localhost", "root", "P@ssw0rd", "sample")
  3. discard setEncoding(db, "utf8mb4")
  4. for row in db.rows(sql"select * from t"):
  5.     echo row[0]
  6.     echo row[1]
  7. db.close()



テーマ:プログラミング - ジャンル:コンピュータ

  1. 2018/08/23(木) 23:15:18|
  2. nim
  3. | トラックバック:0
  4. | コメント:0
  5. | 編集

Nim リスト内包表記

Pythonに似た言語Nim
リスト内包表記が使えないか調べてみました。

future



こちらが参考になりました。
nim内包表記

「future」というモジュールを使えば良いようです。
https://nim-lang.org/docs/future.html


簡単なサンプル



リスト内の値を2倍したリストを作成してみます。


  1. import future
  2. let a = [1, 2, 3]
  3. let b = lc[x*2 | (x <- a), int]
  4. echo b



実行結果


$ nim c -r sample.nim
@[2, 4, 6]



ちょっとハマったのが演算子の位置。

OK

  1. let b = lc[x*2 | (x <- a), int]
  2. let b = lc[x* 2 | (x <- a), int]



変数(スペース)*2だとエラーになります。
NG

  1. let b = lc[x *2 | (x <- a), int]




$ nim c -r sample.nim
sample.nim(4, 14) Error: ']' expected






条件式付き



偶数のみのリストを作成してみます。


  1. import future
  2. let a = [1, 2, 3, 4, 5, 6]
  3. let b = lc[x | (x <- a, x mod 2 == 0), int]
  4. echo b



実行結果


$ nim c -r sample.nim
@[2, 4, 6]





やりたかったこと



これで郵便番号CSVの加工が楽になりました。


  1. import future
  2. import strutils
  3. # 郵便番号CSV1行文
  4. let line = "37201,\"760 \",\"7600000\",\"カガワケン\",\"タカマツシ\",\"イカニケイサイガナイバアイ\",\"香川県\",\"高松市\",\"以下に掲載がない場合\",0,0,0,0,0,0"
  5. # カンマで分割
  6. let data = lc[strip(s, chars={'"'}) | (s <- split(line, ',')), string]
  7. # 2番目:郵便番号
  8. let zipcode = data[2]
  9. echo zipcode
  10. # 6,7,8番目:住所
  11. let address = data[6] & data[7] & data[8]
  12. echo address



実行結果


$ nim c -r sample.nim
7600000
香川県高松市以下に掲載がない場合




テーマ:プログラミング - ジャンル:コンピュータ

  1. 2018/08/23(木) 22:19:56|
  2. nim
  3. | トラックバック:0
  4. | コメント:0
  5. | 編集
次のページ