pretty code

顯示具有 Python 標籤的文章。 顯示所有文章
顯示具有 Python 標籤的文章。 顯示所有文章

2026年6月17日 星期三

大道至簡,唯有 Python

雖然可能也許 Maybe,我最喜歡的程式語言還是 Go?

但不得不說,Python 能找到的程式碼最多?

為了 power  domain 的關係,我得宣告不同名字的 module code。

上次的 cell 只會傻傻的複製貼上,真枉費我寫了 20 年的程式。

如今又需要一堆這樣的 cell,這次總算想到要用程式來生成。

但昨天為了趕在 license 過期的前夕,快速完成某項功能,就用最笨的 print 來做,反正配合 Vim + 現有的 Verilog 也花不了幾秒。

昨晚一夜未眠,趁現在精神好,趕緊來找一下正解。

原來這樣即可。

2025年12月13日 星期六

暗黑 2 真的要畢業了?

自從升上 99 後,玩遊戲已經不太提得起勁了?

雖說如此,去年還是練了一支火法,上個月又練了一支冰法XD

原本是想讓這支冰法用火弩 build,但是只有 87 等玩起來很怪,又很容易死?

最後改用 96 小老婆冰法玩火弩 build,原本的 87 等冰法再換回來。

目前 Bo 完,攻擊力為 13 ~ 16K,全身上下只有一顆 +- 4 的火珠以及滿變的閃爍火焰,配合米山的無限,8 人難度還是可以一玩。

目前暗黑 2 總時數來到 2525 左右,雖然全部大概買了 7 ~ 8 片的遊戲,第 2 時數多的遊戲應該也是玩不到 2 個小時?


有點考慮要買暗黑 3 了,老了不適合唸書了,想看書又會被看電視的老婆干擾,詩海石硯台的隱居處氛圍已蕩然無存XD

一直跟 AI 都相處不來,請他寫的程式或函數都不如自己寫來的快?

趁著連續兩個星期三上天龍國的機會買了兩本 LLM 相關的書,希望可以再給 AI 一次機會XD

雖說用 Python 寫 Code 很快,這一年工作寫到的程式,每個 py 也沒超過 1000 行,但怎麼看都像用 Python 寫 C code?

看著看著,心也累了,天涯何處是吾家呀?

花了一千多塊買了一本 Python 書,無聊就來翻翻吧XD

不知不覺,手上的 Python 書加起來應該已經超過 Golang 了?

2026/01/12 更新

12 月中旬購買了暗黑 3 後就沒開過暗黑 2 了!

最後一次玩暗黑 2 時,終於掉了塔拉夏漆甲,誰知道倉庫居然找不到頭盔?無奈之下查了一下掉落率,看起來應該惡夢墨菲斯托最容易掉落,打寶率 20X 左右打了好幾場都沒看到死亡面具類別的掉落,改試了一下 3C,馬上看到該類物品,再打個幾場果然順利掉落。

終於我也穿上了全套裝備,以火法來說,傷害等級大概掉落了好幾級,不過看在打寶率的份上,還是幫它打了一個洞,我也有了除了槌丁以外的打寶手段。

2025年5月14日 星期三

dcman

之前在某篇文章學到 dcman 的用法,讓我可以在沒有 DC license 的情況下還是可以查詢指令說明。

不過,對於 message ID 形式的訊息,比如 VER-970 這樣的 ID,只能在 dc_shell 下用 man 查詢。

雖然辦公室的人很少用 DC,但只是想查個東西,還要等待 DC 啟動,少說也要好幾秒,更別提為了不讓到處都是 DC 的 command.log,個人習慣還是先切換到我自己的 temp 目錄才去啟動 dc_shell。

雖然我有設定 alias 不用打那一長串指令,但還是覺得不開心XD

昨天研究了一下,終於知道為什麼原來的 dcman 不起作用!

man -M 指定的資料夾中,man database 需要符合一定的結構,就我初步看來 DC message ID 形式的 man database 多了一層資料夾,難怪都會查無資料。

解決方式也很簡單,另外寫一個簡單的 bash script,遇到像是 VER-970 這樣的查詢,使用 - 割出前後字串,前面字串就是那多了一層的資料夾名稱,接著再接上 VER-970 以及後面的副檔名即可。

str=$(echo $1 | sed -En 's/([a-zA-Z]+)-(.*)/\1 \2/p')
str=($str)

folder=${str[0]}

整個查詢會變成下面這個樣子

man -l xxxxxxx/VER/VER-970.n 

測試了一下,果然如我想像中一樣可以順利執行,也順便測了 C2 C3 C9 等 DFT 錯誤訊息,目前看來都還可以正常工作。

另外,跟這個主題無關,為了讓自己的 bash script 更有彈性,有些跟著 script 的設定檔,最好還是指定 script 所在的 folder 變成絕對路徑,這樣的好處是當你使用 alias 執行自己的 bash script,我們可以切到要傳遞檔案的那個路徑,這時就不會有找不到設定檔的錯誤發生。

還有要傳遞的檔案,最好也是使用絕對路徑,這樣傳進去 EDA Tool 裡面也不會找不到檔案。

其實不管是 Python 或是 Go 也是如此,畢竟我們會希望把工具放到某個路徑,而不是把檔案複製到工具路徑再去執行,這樣一來,執行路徑就會很清爽,工作起來也更開心XD

SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )

file1=$(realpath $1)

2025/05/15 更新

據我所知,DC 有幾份文件是關於指令、變數以及錯誤訊息,如果習慣查詢 PDF 的話,也可以不用像我一樣使用 man 來查詢。

不過,兩邊的內容都是一樣的,坦白說,都一樣爛XD

個人覺得御三家中,文件寫得最好的是 Cadence,至少 LEC 是我覺得讀起來最順的,其次是 Simens,至於 Synopsys 我就不予置評了!

2025年4月4日 星期五

Python 工匠案例、技巧與開發實戰

雖然我是很想直接用 Tcl 單幹寫出現在要寫的 EDA Flow Tool?

不過考量到之後可能要一起協同合作,還是使用最大公約數的 Python 語言?

個人不是寫 Python 的,於是便看了一本買了很久但還沒看的書,也就是如標題所述的書。

不得不說,只要幾百塊的金額,便可以學到原本沒接觸過的知識實在是很划算。

據我之前理解,要稱得上是 Python 進階使用者,大概要知道下面兩個技巧:

01. 生成式的使用
02. 裝飾器的使用

不過,我到現在一個都沒用過。

個人覺得只要能把程式用不同的函數來清楚表達,這樣就已經屌打很多人了XD

因此,我們需要一個方法來檢查我們的函數是否有達到上列目的,剛好這本書在函數這一章節,介紹了一個 package - radon,它可以給函數一個分數,這樣我們就知道函數是否可以改進?

之前做財委時,為了將新補上的歷史資料填上我 Excel 前面的總表分析 sheet,於是便用 Python 寫了一支程式,當然這支程式還有別的功能就是。

總之,目前程式碼為 469 行,下面是我執行 radon cc -s xxx.py 的結果。


從上面可以看到,我有 5 支函數稍嫌複雜,只拿到 B 的分數,原則上最好都拿到 A 的分數是比較好的。


在這 469 行的程式中,扣掉 main 用了 17 個函數,一個函數平均大概不會超過 50 行,即使這樣,也不能保證我每個函數都能拿到 A 的分數。


其中以 adjust_cell_format 最為明顯,雖然行數只有 23 行,但我這支函數拿到的分數最差。

正常來說,應該比照 columns,將對齊方式也用 list 記錄,這樣就可以少掉 42 行與 45 行的 if statement。

但要說這支函數不好懂嗎?我也不太確定,至少這個程式已經是一年多前寫的,當我需要新增功能時,回過頭來再看一次函數也不會有多大困難就是了。

在不斷的思考與自我對話中,程式設計之道也會越來越清楚吧?我想…

2024年7月3日 星期三

我果然是 C++ 白癡

想看的 EDA 文章都看得差不多了,今天下午想說來試試將 Python netlist parser porting 到 yosys command。

大概花了 5 個小時,才只完成 80 %,Python code 其實只有 701 行而已。

目前只剩下 save_netlist 相關 code 待移植以及修改 std::getline 呼叫方式。

不想用 namespace std 就必須多打好多 code(std::xxx),有些東西 Standard C++ 也沒有。

至少 regex, read_file line by line 有現成的可用,不過貌似 gcc std::regex constructor 很慢?待確認中。


2024/07/04 更新

早上改完 std::getline 行為後,原本以為可以順風順水,沒想到搞到 12 點多還是有一個莫名的 Segmentation Fault 問題。

下午動用了 GDB,還是看不出是死在自己程式碼的哪個地方,只能看到呼叫一堆的 std::regex 函數?

使用萬能 printf 大法,終於找到出問題的地方。搞到快 4 點多,突然想要 Google 看看,才發現居然是 std::regex 自己的問題,詳此處。 

簡單來說,要做 std::regex_search 的字串不能太長,不然 regex 因為使用遞迴實作,會有 stack 爆掉的問題,難怪 bt 指令看到的都是 regex 函數,Bug 回報的是 100 K 左右就會有問題了,最好的解決方式是改用 boost::regex。

而我手上的 netlist 居然會有 1.5 M 長度的 wire 宣告字串,還不只一條,難怪會死得一塌糊塗!

為了這個不是自己的問題,save_netlist 的功能都還沒 porting,只好之後再說了。 

順便記錄一下 C++ 與 Python 版的差異,為了快速驗證,直接將 netlist 檔案裡 3 條過長的 wire 宣告拿掉。

C++  - 106 seconds, 4G RAM.
Python - 255 seconds, 10G RAM, 離開 Python 約需 7 秒才能回到 shell 提示字元。

因為偷懶,我的 C++ 版本都還沒優化,果然 C++ 還是有其效能上的優勢,不過只是照本宣科 porting 所花的時間都快到 Python 開發時間的一半了。

2024/07/08 更新

早上花了快一個小時將 save_netlist 沒 porting 完的補完,實測結果 yosys C++ 大概也要花個 22 秒左右。

C++ 版本在 save_netlist 函數裡是直接寫檔,除了 instance 的是回傳字串再寫。

Python 版本則是得到一整個 design 字串再一口氣寫檔。

不確定 Python 處理好幾百 MB 的字串效率如何?這應該是一個可以再優化的方向(60 seconds vs 22 seconds)!

下午試了一下,事情跟我想的不一樣,Python 如果只是幾百 MB 的串接字串,一口氣寫檔似乎比一個字串寫一次還來的快,單獨寫的小程式測試結果也是如此?不確定是跟 list 中 iteration 有關還是跟寫檔 buffer I/O 有關?

另外,我測試 yosys command 時繼承的是 Backend,exexute 傳進來的 filename 只有空字串,還需要呼叫 extra_args 才會將檔名從 args 抓出來寫到 filename。

我也順便改了 code,直接用 std::string function 取代 wire regex,也算是避開了 std::regex bug 了。

我是不是全台灣最懂 yosys 的人了XD

2024年6月12日 星期三

關於 Verilog parser 這件事

連假都在盡最後努力讓自己的聖騎升上 97 級,再加上手上沒有那樣大的 netlist 檔案,所以放假三天就在打電動中渡過了XD

昨天一早就用 cProfile 確定最慢的執行函數是什麼?果不其然,前三名是 PLY yacc.py - parseopt_notrack、 PLY lex.py - token 以及 re.match,第四名 pyverilog parser.py - p_items 看起來貌似 constructor 沒錯?


光前三個就跑了 25 分鐘以上(cProfile 會增加程式執行時間,跑起來要更有耐心)。

要細究是哪個段落花時間只能 print message 了,但我想幫助不大,因為 PLY 應該已經是 Python 最有名的 lex/yacc solution 了,要優化應該也不是一天兩天的事。

順便實測其他 EDA tool 的花費時間,原來也不是我想的秒等級!

雖然這樣比有點不公平,比如 DC 還需要知道 cell library,還有不論是 DC or yosys,貌似 read_verilog 不只是 parsing 都還順便建立了 RTLIL 中介層,應該是比 pyverilog 只用一般資料結構儲存花時間,但也只能這樣比較了,畢竟我已經無法再細拆 command 了。

同樣的 netlist 檔案,花費秒數如下:

DC - 191
yosys - 380
pyverilog (建完語法樹) - 1546,離開程式 Python 釋放記憶體又花了至少 100 秒。

嗯,果然還是有一段差距。

2024/06/18 更新

透過 cProfile 分析得知,parser 花了很多時間做 match,撇開 C vs Python 語言差異不談,最快的解決方式就是只要處理 netlist 語法即可,不需要處理整個 Verilog 語法。

稍微查了一下,只有看到 C 語言版本的 netlist parser,再加上看 EDA 文件真的很 borning(EDA 文件都要真的操作才有感覺,還沒碰過的光看文件,坦白說收益不大),所以還是自己來吧。

從上星期五開始,扣掉家裡有事請假外,總共花了 16.5 小時使用 Python 實作。

概念也是非常簡單,我有一個函數負責取得以分號為結尾的字串或是 endmodule 關鍵字,這裡我稱呼它為工作字串,再來就是對工作字串做比對,只要分別針對 module,port,net,cell instance 處理並儲存結果即可。

目前暫時還沒想到 assign 要儲存啥以及偷懶不想處理一維陣列以上的情況,其餘我知道的語法都已處理完畢。

開發時間分析如下:

得到可以工作的字串 - 2.5 小時
使用 Rex 處理所有語法 - 8 小時
將儲存的資料結構轉成 pyverilog 的輸出格式 - 6 小時

程式碼行數:

得到工作字串 - 89 行
輸出 Verilog - 149 行
Rex 處理及其他 - 455 行

程式執行時間:

單純 parsing 及儲存 - 225 秒
輸出 Verilog - 56 秒

下面則是我的 netlist parser cProfile 結果:


目前還算滿意XD

雖然從結果來看,還是有進一步優化的可能性,但我覺得最多不可能再減少超過 50 秒?

就算再減少 50 秒也沒意義!我只有處理 netlist,不像 EDA Tool 處理整個 Verilog 語法,這樣的 parser 跟商用軟體比還是有段距離,但貌似我的工作大部分應該還是針對 netlist 做事?

個人還是覺得 lex/yacc 的方法比較正統,不像我的 code 為了解決縮排,使用者 Code 亂以及空白字元等問題,需要一些 if/else 來處理(取工作字串 89 行程式碼中,大部分都是在處理這些事情),這樣的 code 看起來也不開心XD

2024/06/21 更新

前天進公司後,想了一下還是加上多維陣列的功能,大概花了不到 5 分鐘吧。

沒想到這兩天拿它來試試其他手上有的 netlist,陸續又發現了一些問題,也順便修了 pin net 中,有 range 及 array 情況下,Rex 沒下好導致 capture group 抓取不正確的問題,昨天下午看了 Verilog 2001 Spec 後還發現我搞錯順序了,應該是先陣列才是 range,目前應該都沒問題了吧?

為了這個多維陣列,code 多了 30 幾行,程式執行時間也多了 20 幾秒,目前 parsing 大概要跑 245 秒左右。

單純 parsing


單純 parsing 加上處理多維陣列結果


最後想嘗試一下把我這個 parser 用到之前拔電路的小程式中,一般來說,我有兩個選擇:

1. 將我目前用的簡單資料結構轉成 pyverilog ast 結構再加上個別 class visit function,這樣我不用去改原本的 code。

2. 配合我 parser 用的簡單資料結構改寫我原本的 code。

目前是嘗試使用方法二,今天應該就能知道結果了。

對了,這個 600 多萬行的 Verilog,我的 parser 執行時會用掉 10G 多一點的記憶體,離開好像也是要花個幾秒等記憶體釋放。

跟之前用 pyverilog 的小程式比較,大概快了 35 分鐘,也不枉費我這幾天的努力XD


我的程式總共建了 9 條 Rex。

因為我的程式把行打散了,故我無法得知行數資訊,先前簡單用個 group 概念好讓自己的輸出接近 pyverilog 格式。

之前是用 yosys read_verilog + write_verilog 方式來驗證我的 parser 正確性,我們可以用 design -reset command 將兩個要比較的檔案寫在同一個 script 讓 yosys 幫忙轉檔,同一個檔案,讀加寫要花 30 分鐘(沒記錯的話,寫檔就花了 20 幾分鐘)。

由於之前就知道要怎麼改,故最後還是把 module.port_list 再包一層,裡面儲存另一個 list,這樣我就能讓同 group 的在同一行輸出,加上這個功能之後,我的輸出基本上就跟 pyverilog 一致,除了多維陣列還未驗證過 pyverilog 格式長怎樣。

也因為這樣輸出 verilog 函數行數再度減少,最終版本 parser 包含測試 main 函數只有 701 行(先前的行數統計也是包含 main)。

目前只剩一個謎題待解?

我能理解 pyverilog 因為比對緣故所以花了很多時間,但為什麼單純輸出 verilog 也要花上 10 來分鐘,對比我的 60 秒確實是有不小的差距!我猜 pyverilog 應該是使用 template file 方式產生 verilog,故浪費了很多時間在 File IO 。

其實 yosys 寫檔時間比 pyverilog 還更誇張,我能想到的就是它又二次轉了資料結構以利 verilog 輸出?

剛剛看了一下,輸出是在 yosys/backends/verilog/verilog_backend.cc 這支檔案,看起來是用 RTLIL::Design *design 來儲存整個 netlist,並直接用 std::ostream *&f 寫到 output,看來想知道事情緣由還是要把整個程式碼看過一遍才行。

2024/06/23 更新

快速的看了一下 yosys code,backend - write_verilog 呼叫順序為:

yosys/kernel/driver.cc - main 函數入口
中間未看…
yosys/kernel/register.cc - Backend::execute(這個應該沒有,因為被 verilog_backend 繼承了)
yosys/backends/verilog/verilog_backend.cc - execute,這裡會呼叫 Backends::extra_args,裡面會把 
原本是 NULL 的 std::ostream 在這裡開檔,之後便能寫檔了,這也是為什麼 execute 會接受一個 std::ostream *&f 的緣故,因為需要在這裡 new instance。

2024/06/25 更新

yosys 可以吃 tcl script file,故有用到 Tcl 提供的介面,script 裡面要執行的 command 其 callback 在 kernel/yosys.cc - tcl_yosys_cmd。

在 Pass::call 的過程中,便會呼叫到繼承 Pass 的相關 struct。

2024/06/26 更新

沒用 C++ 寫過大型程式,別說你懂 C++!

從以前到現在沒有認真記過 C++ 語法,導致看 yosys code 很吃力!很多執行順序不是現在的我看的出來的!

The virtual function of base class means inheritance class can override it, you could use override to tell compiler the function is overrided.

baes - virtual void function( )
inheritance - void function( ) override {}

The virtual function of base class equals 0 means the base class can't be created. The inheritance class must override this function.

base - virtual void function ( ) = 0
inheritance - void function ( ) override {}

另外,如果 class 有 static function,其 各個 Pass constructor 會在 main 之前就被呼叫,可能要看 C++11 3.6 節,這是我用 gdb 單步執行才發現的(我的理解可能有誤,至少我另外寫支小程式看不到這個現象?)。

kernel/register.cc - struct Pass 有一個成員 next_queued_pass,在 constructor 時會被設定,然後透過 register.cc global variable - first_queued_pass 記住最後一個 constructor 的 Pass。

接著 main 函數中會呼叫 yosys_setup,裡面會再呼叫 Pass::init_register(執行順序會倒著回來,之前的 first_queued_pass 記住的是最後一個 Pass constructor,沒記錯的話是 techlibs/sf2 - SynthSf2Pass)。

Pass::init_register 裡面會再呼叫 Pass::run_gister。

然後就是對 global variable - pass_register 設值,map 用的 key 就是 pass_name。

之後假設是執行 script 裡面的 command,最後就是在 Pass::call 裡面利用 pass_register 去執行該指令(pre_execute、execute、post_execute),這樣又會回到繼承 Pass 的 struct 裡的 execute 函數(backends/verilog/verilog_backend.cc - execute),因為已經被 override 了。


看到這邊對 yosys 怎麼呼叫有感覺了,應該不會再看下去了?

另外,今天針對同一個 netlist 呼叫 yosys read_verilog and write_verilog,真的要花 31 分鐘左右,這樣看來寫檔真的要 20 幾分鐘跑不掉。

接著把 yosys 儲存的 netlist 再用我的 parser 測試,parsing 時間又增加了 20 幾秒,寫 netlist 還是差不多 60 秒左右,總花費時間大概是在 360 秒內吧?

pyverilog 總執行時間則是暴增到 54 多分鐘(3275 seconds)。

其實這也很容易解釋,yosys write_verilog 對每個 wire declaration 都是單獨一行,故增加了 parsing 時間也是尚屬合理。

原本 netlsit - 340M,  618 萬行。
yosys netlist - 390M, 2166 萬行。

2024/06/27 更新

終於知道為什麼繼承 Pass 的 struct 都會在 main 之前 call constructor 了!跟 static member function沒有關係。

yosys 在每個繼承 Pass 的 struct 最後面宣告處宣告了一個 global static variable,所以才會在 main 之前 call constructor。

以工作站的 yosys 版本來說,總共有 235 Pass 被初始化了。

也就是下面圖片 Line 9 做的事。


下面是 GDB 下中斷的結果


我們可以用 grep -E -r '^\}\s*[a-zA-Z0-9]+\s*;' 找出所有這樣宣告的 Pass,這裡以 backends 為例。


2024/07/08 更新


parsing 106 秒
save netlist 22 秒

對比 Python 版

parsing 268 秒
save netlist 60 秒

搞定,收工XD

2024年6月8日 星期六

2024 week 23 新玩意

01. DC log level information

這兩天突然發現自己移植的 DC script 居然都沒原始專案的 log 輸出?明明該有的 output 檔案都在,但不論是螢幕還是 log 都看不到執行該 command 的顯示!

本來以為是自己多下了一個 tee -i 的參數,從 man 中又看不出有什麼關聯?

終於在昨天中午想通了,一般來說,我們會用 -f script 的方式來執行 DC,如果該 script 裡面有 DC command 便會顯示執行結果。

但我的 script 裡面又帶了一層 script,我真正的 command 都是位在第二層,所以當然什麼都看不到,也算是學了一課。

02. MBFF 

2010 我就有看到台大學生的論文在研究這個,所以應該不是新東西?難道 IC 產業是學術跑在實務前面?

03. Verilog Parser 待釐清事項

之前有簡單寫個用 Rex 來 parsing netlist 的 parser,雖然我只是測試抓取的部分,並未實際去產生對應的資料結構來儲存資料,實測的結果都不需要 3 分鐘。

昨天拿最近用 DC 跑完的 netlist 測試一下,速度依然很快,但在我只建 3 條表示式的 rule 中,就有兩條有些 statement 語句判斷不到,印象中我有處理 escaped identifier 這塊,DC 又有 rule command 可以避免使用奇怪的命名規則,理論應該會比 yosys netlist 來得好才對?改天 EDA 文件看煩了再來試試。

回到 pyverilog 本身,使用 lex/yacc 的方式應該是最正規了,問題是出在 match token 速度才那麼慢嗎?還是在 Python 建資料結構時 constructor 拖慢了速度?

明明其他 EDA parsing 都只花了不到 5 分鐘,難道只是 C/C++ vs Python 的差異?

昨天沒想到,現在 Profile 功能應該已經內建在 Python3,跑一下就知道原因發生在哪?

04. Windows 11 升級

最近微軟不知又發什麼神經,Team Classic 又要改版,偏偏新版又跟著作業系統,而我的電腦就是屬於需升級的那批。

之前為了加速開發,自己買了一顆 SSD 當作業碟,才發現空間已經用了快一半!既然現在已經轉部門半年了,很多軟體跟資料都用不到了,便趁此機會把軟體都移除,目前已用空間還有 15X G,但已經比之前好多了。

不過貌似 Windows 11 升級檢查軟體在 IT DC 環境下不能執行?所以我也不知道是否能升級,但單以 CPU 來看我的 10 年前電腦應該是不在支援清單中。

算了不升級也好,還要浪費半天時間不能做事,抱電腦回來又要再花半天整理工作環境,幸好目前部門我只需要 Vim,Python 跟遠端登入軟體即可,應該不用像以前一樣一堆東西要裝。

下次線上上課只能用 Team Web App,但要記得使用 FireFox,不要用 Chrome,不然就會像某年上資安課一樣一直斷線,被人事判斷沒有上課,導致一樣的課上了兩次,追根究柢都是微軟寫的軟體不好XD

2024年6月7日 星期五

AT 叛客

昨天差點被當成不法分子,真的是好氣又好笑,以後不那麼認真了XD

有鑒於 EDA 或是跟 IC Desing 有關的工具使用,動輒都是好幾個小時或是天起跳,連個 Verilog Parser 都要快半小時!

昨天不知為什麼突然想到(應該是一直看 EDA 文件很容易分心XD),既然純 Python code 很慢,用像是 PyInstaller 的工具包成執行檔後,執行速度是不是會比較快?

印象中之前有寫個等公車的小程式,裡面就有使用 PyInstaller,我就不用再去找參數怎麼用了,下載下來後,一執行安裝 batch,Windows Defender 就一直跳出警告,說是判斷有病毒,稍微查了一下應該是被當成惡意程式,調整了參數,還是會被誤判,後來去忙別的事我也就忘了XD

下午 4 點多時突然接到 IT 郵件,說是發現在執行惡意程式,於是寫信回復事情發生原由(突然想到,郵件裡有 CC 主管嗎?昨天沒特別注意,沒有的話要報告一下)。

原來我已經轉型成駭客我都不知道了XD

對了,昨天是自己耍白癡,PyInstaller 只是包成執行檔,並沒有重新編譯,應該是對執行時間沒有幫助,昨天不知道為什麼會突然覺得有用?一定是太久沒寫程式,腦袋都僵化了,來到新部門後,似乎還沒發現寫程式在這邊有甚麼用?可能是我還太嫩了吧,目前只感覺這些東西很吃經驗,至於能不能寫程式幫助其他同事,我還真的不太肯定XD

突然好懷念 AT 叛客這個暱稱,離開會計本業也快 20 年了,現在的路也不知道到底走得對不對?

後記

用家裡訂閱的小紅傘測試了一下,也是跳出差不多的警告,這樣我就放心了XD

俗話說的好,寧願錯殺也不要錯放,畢竟不管是家裡還是公司電腦,都是拿來做事的,最怕的是什麼都偵測不出來,這樣我才會害怕XD


話雖如此,昨天下班前還是不放心地針對整個 Python 安裝目錄掃瞄了一下,理所當然的是回報沒有問題?

唉,程式設計師不可能永遠自己造輪子,現在第三方函式庫又常有漏洞,除了有專門的團隊每天去追蹤這些資安資訊,一般程式設計師還記的自己用過什麼函式庫已經很厲害了,怎麼可能還定期去看是否有更新,自己沒待過像是 Google 這樣的大公司,不知道他們都是怎樣解決的?

2024年3月25日 星期一

ImportError: No module named site

新工作似乎脫離不了 GDB?

今天為了找到有問題的 cell library,又要動用 gdb 動態 hook process,指令如下:
gdb attach process_id

然後便可以下 breakpoint,接著使用 c 執行程式。 

但我在啟動 GDB 時就有問題,錯誤訊息如標題。

查了一下,原來 GDB use python for scripting,但是因為工作站有安裝不同的 Python,故會造成版本衝突。

如果新版本 Python 使用 Path or PYTHONHOME 的方式來啟動,把對應的值先拿掉即可。

總之就是要讓 GDB 可以使用到他想用的 Python。

另外,display string 時,又會有下面錯誤:
Python Exception <class 'gdb.error'> There is no member named _M_dataplus.

似乎跟 compiler 有關,詳下面連結。

2024年3月17日 星期日

Try Dataflow analyzer of pyverilog

pyverilog 有一個 Dataflow analyzer 的功能,此功能需要 pygraphviz module。

安裝時有一些錯誤,試了一下,按照下面步驟即可成功(網路上的解答適用舊版 pip,pip 23 以後需要這裡的步驟)。

我的環境在 Windows 下,故我要先安裝 graphviz,預設安裝路徑在 C:\Program Files\Graphviz,裡面已經包含相關的 header file and library。

接著打開 "x64 Native Tools Command Prompt for VS 2019",然後輸入底下指令即可。

pip install pygraphviz --config-settings="--build-option=build_ext" --config-settings="--build-option=-IC:\Program Files\Graphviz\include" --config-settings="--build-option=-LC:\Program Files\Graphviz\lib"

另外,example_graphgen.py 100 行有錯,需改成 graphgen.generate(target, walk=options.walk, identical=options.identical, step=options.step, do_reorder=options.reorder, delay=options.delay)

試了一下 OpenROAD-flow 裡面的 gcd.v,把 walk 選項設成 True 後,結果如下圖。

2024年3月15日 星期五

這個星期學到的新東西

01. 當 Python code 裡面有用到很大的記憶體,當你程式離開時,會去釋放這些記憶體,故感覺花 1 分鐘以上才回到 linux shell 是正常的。

02. pyverilog 使用 lex & yacc 的模式來 parsing Verilog 檔案,當檔案很大時,比如 600 多萬行,建立 AST 就要花 20 幾分鐘;同樣的,當你要把 AST 再還原成 Verilog code 時,也是要花個 10 分鐘以上。

那個 600 多萬行的檔案,純用 Python 讀只要 0.6秒,讀一行寫一行到檔案則是 2.X 秒。

03. vim 要使用 regex 搜尋時,某些符號前要打 "\",或是使用 "/\v" 告訴 vim 要使用 regex。

2024/03/21 更新

簡單使用 re 並只 parse instance 及 port arg 的情況下,同一個檔案大概要花 72 秒。 我猜 pyverilog lex 只考慮 netlist 相關的語法下,應該可以更快沒錯。

2023年8月15日 星期二

忙了一天,就為了這個心累的 Bug

我是個很喜歡整理 Code 的人,為了程式碼美觀,我習慣把 Code 整理成一個又一個小函數。

今天遇到一個奇怪的 Bug,坦白說我還不知道為什麼,但我知道如何避免。

我有一個小函數是把作完運算的 np array 透過 np.save 儲存起來,下次 main 函數如果有帶 arg,就會改以 np.load 的方式取得原本 array,這樣可以節省我的開發時間避免重覆運算。

我的問題就是如果使用這種方式來取得之前計算好的 array,感覺我的值都是錯的?故我後續的計算也都是錯的。

解決方式就是把取值 Code 寫在 main 函數,不要透過函數存取 np array。

2023/08/17 更新

搞了半天,原來是自己犯了個低級錯誤,我總共回傳了 4 個 numpy array,其中一個名字尾碼重覆多打了一遍,由於我這些 numpy array 是電腦視覺相關的又有分左右不同,故名字都會取特別長以利區分,一眼望去不容易找到這種錯誤,害我一直往 stack overflow 或是需要轉換型別的地方去想。

不知道換一個 IDE 就能避免這種低級錯誤嗎?但我還是覺得 UltraEdit 最好用!我可是傳說中在十二年間買了兩套 UltraEdit 的男人呀XD

2023年6月2日 星期五

Python 優化小經驗

這幾天看數學看到有點煩,剛好之前有寫一個函數實作 warpPerspective 的功能,只是一個很簡單的做插值計算並將數值填回去 numpy array 的程式,Python 居然要花到好幾秒鐘,為了轉換一下心情,底下是我有嘗試過的一些優化技巧,做個記錄先,有些我也不知如何解釋?

01. 陣列運算一次做比在 two for loop 中做還快。

比如說將一張圖像的齊次座標乘上 Homography matrix,將 3 x (width x height) 的座標先填上 ndarray,之後在一起乘 H matrix,並除以第 3 個分量,比在 loop 中每個 pixel 單獨計算還來得快。

這個好理解,numpy 底層應該有對 matrix 運算優化,可能是 multiprocess 之類的,故有吃到 numpy 的 buffer 靈氣。

02. image[h, w, :] = xx1 * yy1[h, w, :] + xx2 * yy2[h, w, :] + xx3 * yy3[h, w, :] + xx4 * yy4[h, w, :] 優化。

上句右邊計算結果也是一個 ndarray,將上句拆成 3 句可以優化。

image[h, w, 0] = xx1 * yy1[h, w, 0] + ...
image[h, w, 1] = xx1 * yy1[h, w, 1] + ...
image[h, w, 2] = xx1 * yy1[h, w, 2] + ...

這個也好理解,可能是少掉中間那個臨時變數的消耗?

03. 使用 cProfile 做 profile 追蹤,看是否能看出端倪?

---------
command line

py -m cProfile -o xxx.prof xxxx.py
py -m cProfile -s tottime xxx.py

---------
In xxx.py

import cProfile, pstats, io
from pstats import SortKey

pr = cProfile.Profile()
pr.enable()

warpPerspective(img, M, (maxWidth, maxHeight))

pr.disable()
s = io.StringIO()
sortby = 'tottime'
ps = pstats.Stats(pr, stream=s).sort_stats(sortby)
ps.print_stats(20)
print(s.getvalue())
---------

cProfile 可以在程式碼中直接呼叫相關 class 也可以在 command line 中觸發,如果在 command line 觸發時有儲存檔案 xxx.prof,可以用 SnakeViz 這個第三方模組開啟並作分析,SnakeViz 會另外開啟一個 browser window 方便看 call stack,會比原來的 cProfile 結果好看。

---------
pip install snakeviz
snakeviz xxx.prof
---------

但因為我已經知道是那個函數花時間而我的函數又都是計算賦值的工作(純 Python code,沒有呼叫 other functions),故這個工具幫助不大。

04. 改用 multiprocess 並配合 SharedMemory 分享 read data 和 write data。

share read data 沒有什麼問題,但 share write data 一直沒成功,後來才知道要如何寫,如果照文件說的應該同時讀寫是沒問題的?至少我在 Process 和 Thread 中都沒有使用 lock 來保護 share write data。

---------
This style of shared memory permits distinct processes to potentially read and write to a common (or shared) region of volatile memory.
---------

分享 write data 的用法,重點是在 main process 中複製建立的 blank_ 而不是建立 Process 前將 blank 指給 blank_(在個別的 Process 中寫到的還是 blank_ 而不會是 blank)。

---------
blank = np.ones((max_h, max_w, 3), np.uint8)

share_blank = shared_memory.SharedMemory(name='share_blank', create=True, size=blank.nbytes)
blank_ = np.ndarray((max_h, max_w, 3), dtype=np.uint8, buffer=share_blank.buf)

p_count = 1
p = Process(target=warpPerspective_task, args=(p_count, i, H_Inv, img.shape[1], img.shape[0], max_w, max_h))
p.start()
p.join()

blank = blank_.copy()

p.close()
p.unlink()
---------

另外,我電腦有 4 核心,開 4 個 process 分區塊寫 blank_ 對加速並沒有幫助,跟沒有開 Process 時運行時間差不了太多?

05. 將 01 步驟中的大 matrix 計算,配合 4 個 process,各個 process 在 task 中各自計算自己那部份的 matrix。

看起來跟 04 步驟很像,但這樣卻可以再省個 300 ~ 400 毫秒?

06. 如果在迴圈中有存取 class variable,用一個 local variable 來接它,可以減少執行時間。


總之,在不懂 Python 底層的情況下,我目前也只能先試到這樣﹍

2023年5月3日 星期三

Create requirements.txt from current Python code.

pip install pipreqs.

pipresq --encoding utf-8

requirements.txt will be in current working folder.

2023年3月7日 星期二

numpy 一些操作記錄

>>> np.zeros(1)
array([0.])
>>> np.zeros(5)
array([0., 0., 0., 0., 0.])

-------------------------------
>>> np.zeros((1,5))
array([[0., 0., 0., 0., 0.]])
>>> np.zeros((2,5))
array([[0., 0., 0., 0., 0.],
       [0., 0., 0., 0., 0.]])

-------------------------------
>>> a = np.zeros((2,5))

>>> a[:, 0:2] = 3
array([[3., 3., 0., 0., 0.],
       [3., 3., 0., 0., 0.]])

>>> a[0:1, 0:2] = 5
array([[5., 5., 0., 0., 0.],
       [3., 3., 0., 0., 0.]])

-------------------------------
>>> a.ravel()
array([5., 5., 0., 0., 0., 3., 3., 0., 0., 0.])
>>> a.reshape(2, 5)
array([[5., 5., 0., 0., 0.],
       [3., 3., 0., 0., 0.]])

-------------------------------
>>> np.arange(5)
array([0, 1, 2, 3, 4])
>>> np.arange(6).reshape((2, 3))
array([[0, 1, 2],
       [3, 4, 5]])

-------------------------------
Multidimensional arrays can have one index per axis. These indices are given in a tuple separated by commas:

之前一直很少用 Python,趁此機會把 numpy 搞懂,numpy 的邏輯還是從 Python 的切片運算子來的,只是 numpy 的目的是為了科學或者說數學運算,故常用它來表示矩陣運算。在一些論文中,常常可能會有讓 A 矩陣的某行向量等於 B 矩陣的某行向量,如果是直接使用一個一個元素來指派數值,看來就不專業也很煩瑣。

結論就是上句英文就是操作多維陣列的技巧,故在操作時要先掌握住一個原則,這個陣列或是說矩陣是多少的維度,我想改變的又是那個維度上的資料,是整個向量還是向量裡的某一個元素,這時就可以清楚知道 [xx, xx, ...] 裡面要如何下了,正常來說 3 維就應該有 3 個索引,但要改的是某個特定維度的整個元素,這時後免的索引就可以省略。

>>> A = np.zeros((3, 2, 5))
array([[[0., 0., 0., 0., 0.],
        [0., 0., 0., 0., 0.]],

       [[0., 0., 0., 0., 0.],
        [0., 0., 0., 0., 0.]],

       [[0., 0., 0., 0., 0.],
        [0., 0., 0., 0., 0.]]])

>>> A[:, 0] = 1
array([[[1., 1., 1., 1., 1.],
        [0., 0., 0., 0., 0.]],

       [[1., 1., 1., 1., 1.],
        [0., 0., 0., 0., 0.]],

       [[1., 1., 1., 1., 1.],
        [0., 0., 0., 0., 0.]]])

>>> A[:, 1, -1] = 999
array([[[  1.,   1.,   1.,   1.,   1.],
        [  0.,   0.,   0.,   0., 999.]],

       [[  1.,   1.,   1.,   1.,   1.],
        [  0.,   0.,   0.,   0., 999.]],

       [[  1.,   1.,   1.,   1.,   1.],
        [  0.,   0.,   0.,   0., 999.]]])

2023年1月13日 星期五

nest2D on Windows

nest2D 是一個解決 2D 裝箱問題的 Python module,PrusaSlicer 或是 Cura 在擺放模型位置時似乎都有用到?

官方雖然說使用 pip insall 方式就可以順利安裝,但在 Windows 上似乎還有些相依性的問題要先解決,如果是 Linux 或是 Mac 可以去 github issue 列表就會看到解決方式,這裡就不多說了。

首先來說明一下 nest2D 的工作原理,它底層會使用 C++ 的 libnest2D,而 libnest2D 又有用到 Clipper 的裁剪功能、Boost 的 Geometry 等,因此我們也需要編譯這些函式庫。另外,nest2D 是使用 pybind11 的方式來跟 C++ 的 libnest2D 做介接。

如果一開始這些相關的函式庫沒有先準備好,可能在 cmake 過程中就會失敗,另外,如果是透過 pip install 過程中 setup.py 的觸發方式,其程式碼裡面有用到 pypandoc.convert 去讀取 README.md 的內容,因為 pypandoc 有改版,官方已經不建議使用 convert,故還是要下載 nest2D 程式碼來修改,才能避開這個問題,其實不只改這個,還要改 setup.py 裡面的 cmake 參數,由於不知還有多少要改,我這裡不採這個方式編譯。

這裡 nest2D 的編譯方式是直接使用 cmake 而沒透過 setup.py 來觸發 cmake,如果那些相依性的函式庫沒有先準備好的話,過程中大概會遇到一些問題。

底下是我建議的編譯方式,細節我就不說了,但是原則有把握住應該不會有太多問題。

使用 vcpkg 配合 VC 來安裝這些相依性 library,Clipper 在 vcpkg 的名字為 PolyClipping,第一次我不知道,是自己另外下載 code 來編譯,寫此文章的同時,才發現這個方式更快。

另外,從 github 下載 nest2D 時,記得要包含 submodule,才能順便下載 libnest2D 和 pybind11 的原始碼。

假設是指定 Release 編譯(cmake 在產生專案檔時也要指定 Release  -DCMAKE_BUILD_TYPE=Release,否則 link 時也會出錯),在 Release 資料夾便會有 build 好的 pyd 檔案及相關的 DLL,直接在 Python import 使用即可。

2022年5月6日 星期五

Python Regex Capturing Groups

用括弧圈起來的 Regex pattern 是 Regex group,如果 group 裡面需要 group,這時順序從最外層的開始,舉例來說:

content = CHA Rank0 NF ...
pattern = (CH[AB] Rank[0-3] (NF|OF) ...)

使用 Python code -> res = re.findall(pattern, content) 後

res[0][0] = CHA Rank0 NF
res[0][1] = NF

其他語言我想應該也是由左至右?

2022/05/17 更新

如果要搜尋的字串本身就有括弧,使用 '\\('  and '\\)'  跳脫字元即可,JavaScript  字串裡的括弧也是比照辦理。

2021年8月27日 星期五

Can't find a default Python

今天在更新 Python 到 3.9.6 時,原本同事由後端 Node.js 呼叫 py 執行的功能變得無法使用,在 Server  log 可以看到 Can't find a default Python 的錯誤訊息,但我在本機直接執行 py 是可以進到互動式界面的。

上網查了一下,有人說跟系統檔案關連設定有關,有人說跟登錄檔有關,我的系統檔案關連設定看起來跟網路上一樣,但網路上的登錄檔是在 Local Machine 路徑下,我的則是在 CURRENT_USER 下,印象中安裝預設選項似乎只針對目前使用者而已?

懶得繼續追查下去,先把目前登錄檔匯出成檔案,變更檔案裡面 Key 路徑到 Local Machine,接著再匯入修改過的登錄檔,這個問題好像就順利解決了?

暫時就先這樣吧,手上還有 Bug 待解決,不想花太多時間在上面XD

2021年7月16日 星期五

膠水語言

印象中 Perl 是最早有膠水之稱的程式語言,意思是它很容易與其他程式語言結合。但我覺得任何一種 script 語言其實都可以當作膠水語言。

最近為了結合我自己的 Node.js 程式與别人寫好的 Python 程式,我選擇以 Windows Batch 來當中介,由於我需要將 Python 程式中得到的字串傳遞出來,原本考慮使用 os.putenv or os.environ 這兩種方式來傳遞環境變數,但在我的 Windows Batch 中卻無法接到此變數,本來在不同 script 中傳遞變數便有些東西須考量,不論是 Windows Batch 或是 Linux bash script 皆是如此。最終我決定使用檔案來傳遞就好,反正只要使用下面這招,就可以重導向去環境變數。

os.system("echo {0} > name.txt".format(outname))

SET /p BOOK=<name.txt

下面就是我的最終結果,終於有了好用的工具可以嘿嘿了XD


2021/08/03 更新

晚上打完下班卡後,馬上快速的替 CSS filter 加上去除 font 指定字型的功能,經實測還可以,希望可以一勞永逸的解決 Kindle 指定中文字型的問題。

2021年4月20日 星期二

Com port log 解析小技巧

人不舒服智商果然會下降?

上星期請假前的程式修改,到昨天才發現為什麼跟預期結果不符。

今天又再接再勵的想要加上錯誤行數顯示,但執行結果非常奇怪,行數與我在 UltraEdit 看到的行數不一致?

在只有平常三成功力的狀態下,辛苦的奮戰許久終於讓我找到原因。

Windows 下的 UltraEdit 在遇到 \r (0x0D) 時,不會把它當做換行字元,但 Python 在 call readline 時,預設會把 \r or \n or \r\n 都當做換行字元,故導致顯示行數不一致。

當然最根本的原因還是使用 com port 抓 log 時,很容易會掉資料,導致只剩下一個 \r。故這實在不是一個好的除錯方式,也增加了後續開發工具的潛在 bug。

解決方式也很簡單,指定換行字元即可。

f = open(filename, 'r', errors='ignore', newline='\n')