Nim 3.0 (= Nimony) についての情報
ご無沙汰しております。前回の投稿から約半年、間が空いてしまいました。 このブログは、Nim に関するニュースや最新情報をお届けする場として続けてきました。しかし、この半年ほど、Nim公式での動きがバージョンアップくらいの情報しかないですね。(言い訳です。) 一方開発者のAraqのサイトは更新されていたことに最近気づきました。
Nim3.0としてNimonyと名称されるプロジェクトが遂行中らしいです。 2025年秋リリースとのことで大幅バージョンアップに期待しましょう!
Nimony: 設計方針まとめ
2025年5月3日(マイナー編集:2025年5月10日)に公開された Araq 氏による記事「Nimony: Design principles」より、Nimony の設計思想と意図を以下に整理しました。
背景と目的
Nimony は Nim の変種向け新コンパイラで、将来的には Nim 3.0 になる予定のプロジェクトです。ただし、Nim は大規模な言語であるため、完全な実装には時間がかかります。その一方で、現在の Nimony は シンプルで美しく、独立した用途にも十分使える言語 として構築されています。
主な設計方針
1. ハードリアルタイムおよび組込みシステムのサポート
組込み環境での動作を重視することで、より広範な用途での汎用性を得ることを狙っています。機械語が予測可能で固定時間の演算が行えるよう、JIT やトレース型ガベージコレクションは排除されています。
2. 自動メモリ管理(MM)
スコープベースのメモリ管理とムーブセマンティクスを採用し、Nim 2 の複雑な mm スイッチを廃止し、mm:atomicArc のみを残しています。循環参照には .cyclic、それ以外には .acyclic を明示する仕組みが導入されています。
3. エラー処理の思想
エラー処理には例外に加え、ErrorCode 列挙型を利用した型安全な方式を導入しています。{.raises.} アノテーション付きのルーチンは例外または ErrorCode でエラーを返すよう設計されています。
メモリ不足(OOM)時には、oomHandler によって再試行やスレッドチャネルへの通知などを行える仕組みを提供します。nil によるエラー処理も廃止方向で、nil を Option 型のように扱わせ、{.raises.} に応じて戻り型を切り替える柔軟な設計です。
4. ジェネリクスの強化
Nimony ではジェネリックコード全体の静的型チェック(型パラメータのインスタンス化前のチェック)を行います。これは早期エラー検出や IDE のコード補完の向上につながります。また、Nim の concepts によって型要件の明示も可能です。
5. 並行性と並列処理
並行・並列を spawn という単一構文で統一し、実行時にスケジューラーが適切にスレッド処理か非同期かを判断します。内部的には継続渡しスタイル(CPS)で変換されますが、ユーザーには透明にされています。
また、parallel for 構文により自然な並列ループ処理が可能で、構造化された並列性を簡潔に表せます。
6. メタプログラミングとプラグイン
Nimony のプラグインはマクロの進化形として、機械語にコンパイルされ、完全な型情報が利用可能なタイピング段階の API を持ちます。インクリメンタルかつ並列に動作する設計です。
spawn や parallel for、行列の一時オブジェクト削減(avoidtemps)などもプラグイン・テンプレートとして実現される予定です。
Nim製float高速集計ライブラリ『nseqops』公開
SIMDを使ったNimの記事を2つほど書きましたが、この度はライブラリを作ってみました。
ライブラリ nseqops です。
nseqops - 高性能ベクトル演算ライブラリ
このライブラリは、float64 値のシーケンスに対して AVX2 および SSE4.2 命令を使用した高性能なベクトル演算を提供します。計算負荷の高いタスク向けに設計されており、高効率な算術および線形代数演算を実現します。
特徴
- AVX2 および SSE4.2 対応: AVX2 および SSE4.2 命令セットの両方に最適化され、互換性と性能を確保。
- ベクトル計算: ベクトル化による高速な数値計算。
- 包括的な API: シーケンス同士やスカラーとの計算操作をサポート。
- 高精度:
float64型に対応。
インストール
必要要件
- Nim >= 2.2.0: Nim をインストールする
- nimsimd >= 1.3.1:
nimsimdがインストールされていることを確認してください。 - AVX2 または SSE4.2 対応 CPU: 使用する CPU が AVX2 または SSE4.2 命令をサポートしている必要があります。
セットアップ
nimble を使用してライブラリをインストール:
nimble install https://github.com/nappworks/nseqops
使用方法
ライブラリのインポート
AVX2 を使用する場合:
import nseqops/avx2
SSE4.2 を使用する場合:
import nseqops/sse4
サンプル
シーケンスの合計
let data = @[1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0] echo sumSeq(data) # 出力: 28.0
要素ごとの演算
let a = @[1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0] let b = @[7.0, 6.0, 5.0, 4.0, 3.0, 2.0, 1.0] # 要素ごとの加算 echo addSeqs(a, b) # 出力: @[8.0, 8.0, 8.0, 8.0, 8.0, 8.0, 8.0] # ドット積 echo dotSeqs(a, b) # 出力: 84.0
スカラー演算
# 各要素にスカラー値を加算 echo addSeqScalar(a, 3.0) # 出力: @[4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0] # 各要素をスカラー値で乗算 echo mulSeqScalar(a, 2.0) # 出力: @[2.0, 4.0, 6.0, 8.0, 10.0, 12.0, 14.0]
API リファレンス
シーケンス操作
sumSeq(data: seq[float64]): float64prodSeq(data: seq[float64]): float64addSeqs(a, b: seq[float64]): seq[float64]subSeqs(a, b: seq[float64]): seq[float64]mulSeqs(a, b: seq[float64]): seq[float64]divSeqs(a, b: seq[float64]): seq[float64]dotSeqs(a, b: seq[float64]): float64
スカラー操作
addSeqScalar(a: seq[float64], b: float64): seq[float64]subSeqScalar(a: seq[float64], b: float64): seq[float64]mulSeqScalar(a: seq[float64], b: float64): seq[float64]divSeqScalar(a: seq[float64], b: float64): seq[float64]
ライセンス
このプロジェクトは MIT ライセンスの下でライセンスされています。詳細は LICENSE ファイルを参照してください。
追加情報
- このライブラリは現在、
float64型のみをサポートしています。 - SSE4.2 の場合、計算は 128 ビットレジスタ(
float64x2)で実行されます。 - AVX2 の場合、計算は 256 ビットレジスタ(
float64x4)で実行されます。 - AVX-512 への対応は今後のアップデートで予定されています。
コンパイル指示
AVX を使用する場合
nim c -r --passC:"-mavx" hogehoge.nimSSE4 を使用する場合
nim c -r --passC:"-msse4" hogehoge.nim
注意: 適切なフラグを含めないとコンパイルエラーが発生します。
問題や追加機能の要望がある場合はお知らせください。
Nim version 2.2.2 リリース
Nimの2.2.2にリリースされました。
Nimチームは、安定版リリースである Nim 2.2 の最初のパッチリリースとなる バージョン 2.2.2 を発表できることを嬉しく思います。
このリリースは 2.2.0 リリースから 4 か月後 に行われ、203 件のコミット が含まれており、バグ修正や改善が施されています。
もし Nim 1.6 をまだ使用している場合は、バージョン 2.0 のリリース記事 をチェックして、どのような機能が追加されているかをご確認ください。また、Nim 2.0 をすでに使用している 場合は、バージョン 2.2 のリリース記事 をご覧いただくことで、Nim 2.2 での改善点を確認できます。
Nim 2.2.2 のインストール方法
お使いの OS のパッケージマネージャ がすでにバージョン 2.2.2 を提供しているかどうかを確認するか、以下の方法でインストールできます。
すでに choosenim を使用して Nim の以前のバージョンをインストールしている場合、Nim 2.2.2 へのアップデートは簡単です:
$ choosenim update self $ choosenim update stable
注意: 使用している choosenim のバージョンが 0.8.5 以上 であることを確認してください。もしそれより古い場合は、choosenim のリポジトリを確認し、まず choosenim を再インストールしてから Nim を更新してください。
また、Nim 2.2.2 は ナイトリービルド からダウンロードすることも可能です。
バグ修正一覧
lenをvarargs[untyped]に適用するとコンパイラがクラッシュする問題を修正 (#24258)- コンパイラが型の宣言を強制的に生成するように修正 (#19585)
importcでエイリアスされた型がdistinctで正しく動作しない問題を修正 (#18896)dequeに要素を追加するとセグメンテーションフォルトが発生する問題を修正 (#24319)- ネストされたイテレータ が
proc内で正しく動作しない問題を修正 (#3824) - closure オブジェクト型の一部ではない という内部エラーを修正 (#19984)
- 無順序列挙型 (unordered enums) のサイズや符号問題 を修正 (#23952)
- ネストした
ref/deref構造体を渡すと 無効なコードが生成される 問題を修正 (#18081) - VM の問題: const にバインドされた proc で
dest registerが設定されていない エラーを修正 (#24359) - Cコンパイラエラー: オブジェクトフィールド関数をデフォルト初期化するとエラーが発生 する問題を修正 (#23545)
nim c -cやnim eでmacro w(): untyped = result = genSym(nskType)を使うと SIGSEGV が発生する問題を修正 (#24379)lib/pure/selectorsのビルド失敗 (MinGW) を修正 (#24371)macroのコンテキストでsupportsCopyMemが失敗する 問題を修正 (#24378).ndi ファイルを書き込まないように修正 (#24395)- コピー保護付き配列フィールドでデフォルト値が設定された場合にエラーが発生 する問題を修正 (#24369)
- Arc/Orc メモリ管理下でネストした
seqを持つインラインイテレータがメモリリークを起こす 問題を修正 (#24402) - 新しいスタイルの
conceptが正しくマッチしない 問題を修正 (#24451) fcloseに対するエラーチェックがない 問題を修正 (#19600)- テンプレートで生成された
letシンボルがnimvmブランチ内で再利用される 問題を修正 (#24472) - 冗長な型キャストが VCC (Visual C++) でエラーを引き起こす 問題を修正 (#24476)
asyncdispatch.drainが未完了の操作があるにも関わらずタイムアウト前に戻る 問題を修正 (#24506)ensureMoveの使用がJsonNodeに対してメモリリークを引き起こす 問題を修正 (#24504)allocCStringArray([""])を--mm:refcで使用すると未定義動作 (UB) が発生 する問題を修正 (#22153)std/pegsがnim cpp --mm:orc --exceptions:gotoで無効な C++ コードを生成する 問題を修正 (#22101)- 単語エラー を修正 (#24538)
- 不明な
hint/warning名を許可するが、警告を表示する ように修正 (#20908) nimble 0.16.4のビルドがbuild_all.sh実行時に失敗する 問題を修正 (#24536)exportcプラグマが付いた未使用の定数がコンパイラによって最適化される 問題を修正 (#17681)asyncdispatchが--mm:arcでメモリリークを引き起こす 問題を修正 (#23212)- Nim 2 における
emit/asmのvarパラメータ参照の不整合 を修正 (#23114) - 配列の境界が大きい場合に誤ったエラーメッセージが出る 問題を修正 (#24599)
static openArrayをseqでバックする場合、他の関数に渡せない 問題を修正 (#24630)sizeプラグマを持つ型が間接的にインポートされると、C の型サイズ宣言が不整合を起こす 問題を修正 (#24623)nimsuggest "outline"の出力にテンプレートがリストされない 問題を修正 (#21923)- Nim 2.2.x で C++ コンパイルが失敗する 問題を修正 (#24658)
NimにおけるSIMDの使い方メモ②
前回の記事の続きの②です。
今回のソースコード例はSSE4の128bitレジスタに2つのfloat64ですが、AVX2の256bitレジスタについてもライブラリと命令が変わるので同様です。 ただ、ライブラリと命令を探するにはnimsimdのリポジトリのソースコードとintelのガイドと見比べて使用可能なTypeとプロシージャを探す必要があります。 SSE2に関してはnimsimdの例があるのですが、整数型の足し算だけなので基本のキにような使い方だったので、floatを使用するだけで結構骨が折れました…。 C/C++のSIMDについてはChatGPTに聞けば教えてくれたので助かりました。 (ちなみにいきなりnimでのsimdの実装例をChatGPTに聞いたら、存在しないライブラリを使用させたりエラーがばかりのコードを吐き出しました…)
ベクトル化の前提知識
ベクトル化(SIMD: Single Instruction, Multiple Data)は、1つの命令で複数のデータを同時に処理する技術です。SIMD命令は、現代のCPUが持つ「ベクトルレジスタ」を利用します。
SIMDのベクトルレジスタ
SIMD演算では、ベクトルレジスタ(例えば、128ビット)を使用します。このレジスタには複数のデータを格納し、一度に操作します。
- 128ビットレジスタ:IntelのSSE命令セットで導入され、
XMMレジスタとも呼ばれます。 - レジスタの分割:ビット数に応じて複数のデータが格納されます。
| データ型 | レジスタ内容の分割 | 格納される数(128ビット) |
|---|---|---|
| 64ビット | 2つのfloat64 or int64 |
2 |
| 32ビット | 4つのfloat32 or int32 |
4 |
| 16ビット | 8つのint16 |
8 |
| 8ビット | 16個のint8 |
16 |
64ビットデータの格納方法
SSEの128ビットレジスタに64ビットデータ(float64やint64)を格納する場合、2つの64ビット値がレジスタに並びます。
例えば、float64配列 @[1.0, 2.0, 3.0, 4.0] を考えます。
| 128ビットレジスタ内容 |
|---|
[1.0, 2.0] | 1つ目の128ビットレジスタ |
[3.0, 4.0] | 2つ目の128ビットレジスタ |
このレジスタは __m128d型(ダブル精度)で扱います。
32ビットデータの格納方法
SSEの128ビットレジスタに32ビットデータ(float32やint32)を格納する場合、4つの32ビット値がレジスタに並びます。
例えば、float32配列 @[1.0f, 2.0f, 3.0f, 4.0f] を考えます。
| 128ビットレジスタ内容 |
|---|
[1.0f, 2.0f, 3.0f, 4.0f] |
このレジスタは __m128型(単精度)で扱います。
ベクトル化の仕組み
- SIMD命令は、複数の要素を一度に処理できるため、通常のループ演算より高速です。
__m128d型は、2つのfloat64を1つの128ビットレジスタで処理します。- 演算関数例:
mm_add_pd:2つの128ビットレジスタの加算mm_mul_pd:2つの128ビットレジスタの乗算mm_div_pd:2つの128ビットレジスタの除算
使い方:配列の要素を合計の例
import nimsimd/sse42 proc sumSeq*(data: seq[float64]): float64 = const VecSize = 2 # __m128dは2つのfloat64を持つ var sumVec: M128d = mm_setzero_pd() # 合計用のベクトルを初期化 var i = 0 let alignedLen = data.len - (data.len mod VecSize) # ベクトル部分の合計 while i < alignedLen: let vec = mm_loadu_pd(addr data[i]) # 配列データをロード sumVec = mm_add_pd(sumVec, vec) # ベクトル加算 i += VecSize # ベクトルの合計結果を配列に格納 var sumArray: array[VecSize, float64] mm_storeu_pd(addr sumArray[0], sumVec) # 合計をスカラーに変換 var totalSum: float64 = sumArray[0] + sumArray[1] # 残りの要素を加算 for k in i ..< data.len: totalSum += data[k] return totalSum
ポイント:
1. ベクトル化:mm_loadu_pdでデータをロードし、mm_add_pdでベクトル演算。
2. 端数処理:配列サイズがVecSizeの倍数でない場合、残り要素はスカラ演算で処理。
3. 合計の格納:ベクトルの合計を一度arrayに保存し、個々の要素を合算。
主なベクトル操作
- ロード:
mm_loadu_pd - 加算:
mm_add_pd - 乗算:
mm_mul_pd - 除算:
mm_div_pd - スカラ値のブロードキャスト:
mm_set1_pd - 結果の保存:
mm_storeu_pd
NimにおけるSIMDの使い方メモ①
しばらくブログの更新が滞ってしまいました・・・。子育ての忙しさにかまけて書けずにいましたが、また少しずつ投稿を再開していこうと思います!
今回は、NimにおけるSIMD(Single Instruction, Multiple Data)の使い方について、自分用の備忘録として書き残しておきます。。
SIMDとは?
まず、SIMDについて簡単に説明しておきます。SIMDは「Single Instruction, Multiple Data」の略で、一つの命令で複数のデータを同時に処理する技術です。
これにより、特定の処理を並列化し、高速化することが可能になるらしいです。
例えば、画像処理やデータ分析、ゲーム開発のような場面で、膨大なデータを高速に処理したい場合に大きな効果を発揮します。
HANA DBなどのカラムナ(列指向)RDBMSの高速化において使用されている技術とのことで興味を持ちました。
SIMDを活用することで、複数の要素に対する加算や乗算などの基本的な演算を、通常よりも効率的に行えるようになります。CPUにはSSE(Streaming SIMD Extensions)やAVX(Advanced Vector Extensions)といったSIMD命令セットが用意されており、これらを使うことでその恩恵を受けられます。
NimでSIMDを使う方法
Nimでは、SIMD命令を活用する際にいくつかの選択肢があります。1つは、C言語のコードを直接書き込む方法です。
2つ目の方法はnimsimd というライブラリです。後者の方法で実際に動かしてみました。
nimsimdの使い方
nimsimdでは、命令セットごとにモジュールが分かれています。たとえば以下のようにインポートします:
- AVX2を使う場合
nim import nimsimd/avx2 - SSE4.2を使う場合
nim import nimsimd/sse42
このように目的に応じて必要な命令群をインポートします。
コンパイルオプションの注意点
SIMDを使うには、コンパイル時に対応するオプションを指定する必要があります。たとえば以下のように設定します:
- AVXを使用する場合
nim c -r --passC:"-mavx" hogehoge.nim - SSE4を使用する場合
nim c -r --passC:"-msse4" hogehoge.nim
これを忘れると、コンパイルエラーになりますので注意。
また別な記事で具体的なコード例を交えながら、SIMDを使った処理の記述方法について触れていきたいと思います。
ChatGPT Plusに加入した件
最近めっきり東京が寒くなりましたね。 そんな中で育児休暇を取りました。実は最近子供が生まれまして、今日でちょうど2か月になります。そして、その機会を活かして以前から気になっていたChatGPT Plusに加入してみました。
育児休暇を取っている中で、今回はそのChatGPT Plusに加入してみて感じた良い点について書いてみたいと思います。
良かった点
まず、何といってもGPTのモデルを選べるのは魅力的でした。 標準のモデルでも十分便利でしたが、より高性能なモデルに切り替えることができるのは、思った以上に快適です。
それから、好きな時に回数を気にせずにChatGPTを使えるのも安心感がありますね。 実際のところ、今のところ回数制限に引っかかったことはないのですが、それでも自由に使えるという安心感は大きいです。
そして、心なしかレスポンスが早くなった気がします。 特に複雑なリクエストをしたときの応答速度に違いを感じていて、作業の効率が上がったと感じています。
あと、"with Canvas"という機能がかなり快適です。 文章やコードを書いて、それをもう少し深掘りしたい時に、視覚的に整理できるのが助かります。 Plusと直接関係があるかはわかりませんが、Nimのコーディングについてもより精度が上がったように感じていて、これは個人的にうれしいところです。
これからやってみたいこと
今後は、with Canvasを使ってもう少しコーディング支援に力を入れてみたいと思います。 コードを整理しながら進めるのは自分のスタイルに合っているので、引き続き試していこうと考えています。
また、カスタムGPTの作成にも興味があります。 自分だけのカスタマイズされたAIを作ることができるのはとても面白そうですよね。 これも時間を見つけて挑戦してみたいと思っています。
そんなわけで、ChatGPT Plusを使い始めてからの気づきをざっと書いてみました。 これからも引き続き色々試してみて、さらに便利な使い方を見つけられたらと思います。
Nim version 2.2.0 リリース
Nimの2.2.0がリリースされましたね。 前回は2024/07に2.0.8がリリースされたので、大体3ヶ月くらいですね。 Nim Blogはいつもチェックしているのですが、昔は月イチくらいでライブラリ紹介をしていたのですが、最近は専らバージョンアップですね…。 Nimの頻繁なリリースは嬉しいのですが、ライブラリ紹介が少ないのは残念です。まだまだNimユーザが少ないってことでしょうか。(むしろ、昔よりも減っている?)
Nim 2.2.0
リリースハイライト
このリリースでは、ジェネリクス、型記述(typedesc)、および静的型に関する多くの長年のコンパイラバグが修正され、これらの機能が実質的に安定しました。特に、ジェネリック型内の型や値に対する任意の式が、これまで特定のケースでのみ動作していたものが、今ではほぼ正常に機能します。このリリースは全体的にバグ修正に焦点を当てており、多くの他の問題も幅広く修正されています。このリリースでは、C++との相互運用性に関する改善が続けられています:
noInitを型やフィールドで使用し、C++バックエンドでメンバ初期化子を無効にできるようになりました。- C++のカスタムコンストラクタ初期化子が追加されました。
memberを使用して、手続き(procedure)をC++型に関連付けることができます。- C++のコンストラクタは、新しいオブジェクトを作成するのではなく、既存の結果を再利用するようになりました。
- JavaScriptバックエンドは、クロージャとクロージャイテレータに対してラムダリフティングをサポートするようになりました。
メソッドは、
--experimental:vtablesオプションを使用してVTableに基づく実装をサポートするようになりました。メソッドは、その型が定義された同じモジュール内に制限されます。タプルのアンパッキング機能が強化されました:
- タプルのアンパッキング代入で、値を破棄するためにアンダースコア(
_)を使用できるようになりました。
var a, c: int
(a, _, c) = (1, 2, 3)
- タプルのアンパッキング変数宣言で、タプル全体に対してのみ型アノテーションをサポートするようになりました。
let (a, b): (int, int) = (1, 2)
let (a, (b, c)): (byte, (float, cstring)) = (1, (2, "abc"))
ジェネリックルーチンやテンプレート内でシンボルキャプチャの問題に対処するため、実験的なスイッチ
--experimental:openSymが追加されました。マクロ作成者向けに、新しいノード種類nnkOpenSymが追加され、これを別途処理する必要があるかもしれません(nnkOpenSymChoiceに似ています)。詳細は完全な変更ログをご覧ください。Nim 2.2.0には、オプトインオプションとしてSATソルバーを導入するNimble 0.16.1が同梱されています。