Pages

Saturday, April 20, 2024

낯선 Rust에서 오래된 Object Pascal의 향기를 느끼다

요즘 새로운 프로그램을 만드는데, 이 참에 공부좀 해보자 해서 Rust로 만들어보고 있습니다.

아무래도 멘땅에 헤딩하다 보니까 고조할아버지 제삿날 종가집 시어머니급 잔소리를 늘어놓으며 사사건건 시시콜콜하게 간섭해대는 rust-analyzer와 싸우면서(?) 즐거운(?) 나날을 지내고 있습니다만...... 오늘 재미있는걸 하나 발견했습니다.

Rust에서는 이 구문을 오류로 보더군요.

이걸 수정하려면 이렇게 고쳐야 됩니다.

이 코드를 보니 예전 Object Pascal (Delphi) 시절이 생각나는군요. primitive type이 아니면 var 절에서 객체 변수를 선언한 뒤에 구현부에서 꼭 초기화를 시켜줘야 하고, 그렇지 않으면 바로 runtime error를 뻥뻥 토해냈는데, 구조가 완전히 똑같습니다. Rust도 마찬가지이긴 합니다만, 차이점이라면 메모리가 할당되지 않았다는걸 컴파일 타임에 발견해낼 수 있다는 정도일까요.

Pascal이란 언어는 자기 자신은 비주류인 주제에 온갖 잡다한(?) 언어들에게 무수한 영향을 끼치는군요. Java도 Python도 Javascript도 심지어는 Rust도 모두 다 Object Pascal의 구조를 일정수준 이상 차용해왔으니......

과거 해당 언어의 추종자로서, 아련한 기분이 듭니다.

See old Object Pascal from new Rust

Nowadays I'm working on a new application, and I thought it's a good chance to learn something new, I tried Rust.

As a newbie(or noob) in this area, I enjoy the time fighting against rust-analyzer that always-whining like Grouchy Smurf...... (lol) And today I found something interesting.

In Rust, the following is an error.

To fix, the first line should be changed like following.

Seeing the code reminds me of the good old days of Object Pascal (Delphi). If it's not an primitive type you've got to declare the variable in var clause and call constructor in implementation, or it'll emit runtime error. And Rust "inherited" the structure as it was, except for catching non-memory-assignment in compile time.

The programming language Pascal is a minor one as it is, it influences to too many other languages, like Java, Python, Javascript, and now Rust....... They all adopted at least some part of Object Pascal.

As a good follower of the language, I feel dim as I saw this.


Thursday, March 7, 2024

PostgreSQL vs. SQLite: read & write in multithreaded environment

The start was humble. I needed to cache some data, and I thought just push them to database table and give index, and the rest will be database's job. There were only 2 TEXT fields, and I needed to refer to only one field to search for specific row - which is some kind of key-value store -, so I thought whatever database engine should be fine.

And yes. It was a BIG mistake.

First I tried SQLite, and I found out that, in multithreaded environment some records are evaporated when trying to write to the table simultaneously, even with -DSQLITE_THREADSAFE=2 compile time option. I pushed the same data in same condition, and sometimes I have only 20 records, other times 40, and yet 26 for some others....... What drove me crazier was that the SQLite itself worked fine without any I/O problems. A good moment to shout "WHAT THE HELL?!" in real time.

So I changed the engine to PostgreSQL. Our trustworthy elephat friend saved all the records without any loss. I was satisfied with that, but...... Though I applied b-tree index to necessary field of the table, it took 100 milliseconds for just running SELECT field2 WHERE field1='something'. No, the table was small enough. There were only 680 records and data lengh was at most 30 characters for field 1 and only 4 characters for field 2. I configured the engine with some optimization, so it worked fine for bigger tables so I felt assured for its performance, but I didn't expect something like this, even in my dreams.

Elephant is tough, but as a side effect it's too slow.......

So, one last chance: I ran pg_dump to move data from PostgreSQL to SQLite, and with same condition(same index, same table structure, ......), I turned on at .timer SQLite shell and it took less than 0.001 second. Yohoo!

After some more experiments, SQLite can't fully resist from data loss by itself even with multithread support option enabled, and you need more external support like std::mutex. I guess that it's fread() call doesn't support full serialization in multithread environment, but I have neither time nor abilities to do the proper inspection. :P

Anyway, now I use the combination of SQLite + WAL mode + more SQLite internal cache + std::mutex. Still the write performance looks good, but if needed, I think I could use more files with load balancing via non-cryptographic hash.

PostgreSQL vs. SQLite: 멀티스레드 환경에서의 읽기-쓰기

그러니까....... 시작은 소소했습니다. 뭔가 데이터를 캐싱할 일이 있었는데, DB에 쌓아두고 index 걸면 나머지는 DB가 알아서 하지 않겠느냐 하는 거였습니다. 데이터라고 해봐야 별거 없이 그냥 TEXT 필드 두 개가 전부인데다가 실제로 데이터를 찾을 때는 둘 중 하나만 가지고 찾으면 되는 매우 간단한 key-value store 형태의 구조라, 어떤 DB를 써도 상관없겠지 하고 안일하게(.......) 생각했습니다.

옙. 그건 큰 착각이었습니다.

처음에는 SQLite를 사용해봤는데, 멀티스레드 환경에서 동시에 쓰기를 수행하니 컴파일시 -DSQLITE_THREADSAFE=2 옵션을 추가해도 일부 레코드가 유실되더군요. 동일한 데이터를 동일한 조건에서 동일하게 넣는데 어떨 때는 레코드가 20개만 있고, 어떨 때는 40개, 어떨 때는 또 26개...... 게다가 심지어 동작에는 이상이 없습니다(......). 정말이지 What the hell을 라이브로 외치기 딱 좋은 순간이죠.

그래서 DB를 PostgreSQL로 바꿨습니다. 우리의 우직하고 단단한 코끼리 친구는 레코드 유실 없이 모든 데이터를 다 받아서 잘 보관해줍니다. 그리고 만족하고 있던 그 찰나....... 인덱스까지 b-tree로 잘 걸어줬음에도 불구하고 SELECT field2 WHERE field1='something' 하나 수행하는데 무려 100밀리초가 걸립니다. 그렇다고 해서 테이블의 크기가 컸던 것도 아닌게, 레코드라고 해봐야 겨우 680여개 뿐이었고, 레코드 길이도 하나는 길어봐야 30글자, 나머지는 4글자 고정이었거든요. 나름 환경설정 최적화도 해 주었고, 그래서 대형 테이블에서는 비교적 빠르게 돌아갔던 터라 안심하고 있었는데, 이런 사소한 부분에서 성능 문제를 맞닥뜨릴줄은 꿈에도 몰랐습니다.

코끼리는 딴딴하지만 그 대신 미친듯이 느린걸로......

해서...... 혹시나 해서 PostgreSQL에서 pg_dump로 데이터를 덤프해서 SQLite로 옮긴 뒤에 동일하게 SELECT를 수행해 봤습니다. SQLite shell에서 .timer 걸고 돌려보니 0.001초가 채 안 됩니다(......).

추가로 더 시험을 해 본 결과, SQLite에서는 멀티스레드 지원 옵션이 추가된 상황에서 데이터 유실을 완벽하게 방어하지는 못하고, std::mutex 같은 별도 외부 지원을 추가해야 되더군요. 아마 fread() call이 멀티스레드 상황에서의 serialization을 제대로 지원하지 않지 않는 것 아닐까 하고 추측하고 있습니다만, 거기까지 추적하기엔 시간도 없거니와 능력도 안 되어서...... :P

하여간, 지금은 SQLite + WAL mode + 내부 캐시 증량 + std::mutex 조합을 사용하고 있습니다. 쓰기 속도는 아직 충분하다고 여겨집니다만, 만일 더 필요하다면 파일을 여러개로 늘린 뒤에 non-cryptographic hash로 load balancing을 하면 될 것 같습니다.

Monday, February 19, 2024

Haste makes waste

Sometimes I find that the old saying always gives me wisdom. Nowadays I had a chance to reaffirm it. Haste makes waste, so I detoured, and I could save time eventually.

The detail is as follows: I'm assigned to develop a feature to extract some data, which looks a piece of cake but not actually. I've got to extract both summary and "body" from single raw data, and it should be faster if I do it in one loop. However, considering existing data storage process I had to separate this to two separate thread - or, that was my first impression. Such that, I intentionally delayed the implementation being busy with whatever not associated with this for three days, which was expected to be done in one day, and I found far better alternative: use only one loop, and make subsections.

Personally I consider "incubation effect" as of the most importance. Already confirmed by the academia of psychology, you can see more and better for the given question when you encountered difficult question by doing something unrelated to the given issue for a while. The actual mechanism is not in agreement yet, but it is the job of the academia, and my job is using it with my thanks to those psychologists. :D

It's nothing, but I could reinforce my behavior with "I'm not wrong!", so I drop a line here.

급할수록 돌아가라

간혹 옛 성현들의 말씀 중에 틀린게 없다는걸 새삼 실감할 때가 있습니다. 최근에 그런걸 다시 한 번 느꼈는데...... 급할수록 돌아가라 하셔서 돌아갔더니만 궁극적으로 시간을 더 절약하게 되더군요.

상황은 대충 이렇습니다. 특정 데이터를 추출하는 새로운 기능을 만들어야 하는데,  겉으로 보기엔 간단하지만 실제로는 간단하지 않겠다는 생각이 들었습니다. 하나의 raw data에서 요약본과 메인 데이터 두 개를 추출해야 하는데, 반복문을 한 번만 돌리면 전체적으로는 수행이 더 빠르겠다는 생각이 들더군요. 하지만 기존의 데이터 저장 체계와 맞물려서 생각해보면 이걸 두 개의 스레드로 분리해야 할 수밖에 없는 상황이었는데...... 하루면 끝낼 일을, 일부러 사흘 정도 딴짓(?)을 하면서 시간을 질질 끌어보니 답이 나왔습니다. loop를 한 개로 놓고 데이터를 subsection 형태로 나누면 어떻게든 되곘더군요.

제가 평소 업무중 중요시하는 것 중 하나가 부화 효과입니다. 어려운 문제가 주어졌을 때 그 문제에 무작정 집중하지 말고, 그 문제와 전혀 관련없는 다른 일을 수행하다가 원래의 문제로 돌아오면 기존에는 보지 못했던 새로운 지평이 보인다는, 무려 심리학 실험을 통해 검증된 효과입니다. 왜 이러한 효과가 발생하느냐에 대해서는 아직 학계에서 갑론을박 중인 것으로 알고 있습니다만, 블랙박스의 내부 구조를 밝히는 것은 학계의 영역이고, 전 그저 쓰기만 하면 될 뿐이죠. :D

소소합니다만, 나는 틀리지 않았어! 하고 다시 한 번 확인하게 되는, 강화 요인 중 하나가 되었기에 끼적여봅니다.

Wednesday, September 6, 2023

Journey to the Vim IDE, and what I learned

It's a bit late, but I learned that Bram Mooleanaar, the very developer of Vim, has passed away last month. I sincerely show my humble respect to him and his legacy, the great text editor. Rest in peace.

I'm not sure whether this is a side effect, but I spent a few days on providing IDE-like environment in Vim. Since it is the second trial the result was satisfactory, but I conclduded that I'd continue to use Visual Studio Code(VS Code) and postponed to apply the environment to the production. There are some reasons:

  1. My hands got WAY TOO DIRTY on customization. Plugins collide here are there. There are some features which should be provided in plugins in Vim, while in VS Code that are embedded, and more plugins mean more breaking points, since they change the settings internally without considering the others.
  2. It's most people look over, but you can use VS Code without mouse for more than 95% of actions. It's not only hot keys but also command palette with fuzzy search support. Yes. It's not the only "gift" to Vimmers motto, "we don't need mouse!"

And personally, I recognized that I tried to make Vim-IDE as similar as VS Code. It's not only behave mswin but also tab movements, file explorer, etc. If that's the case, I'd prefer to remaining in VS Code, rather than make efforts to make Vim resemble VS Code.

 In my experience, the strength of of Vim is in editing, but not insertion. For example, you have commands like da", di', dt(, or the difference of O and o. And this is the very strength of modal editor, which provides a "special mode" only for editing. Yet for me, I spend most of the time in insertion, not in editing. And in doing that, what I use most is at best autocompletion. Vim is certainly wonderful job, but considering my use case most of them are unwanted. Jumping between normal mode and insert mode and enduring the inconvenience only for features which I'd use once a month at best is quite a nonsense.

Wrapping up, I'm sure that Vim is quite a treasure, but it doesn't fit to me, like someone said in Reddit, "VS Code versus Vim is not about what's better. They're just different and appeal to different type of people."

To my dear heavy Vim users, if you read this post, please don't be angry, but think of it as a practical case of "there's nothing like a shirt that fits to all."

So everyone, happy Vimming!

P.S:

Yet, I'm dissatisfied with performance drops due to the limit of VS Code brought by its structure(Javascript.......). Vim is a bit better, but Vimscript is still a script also, so they're anyway about the same to me. :P

Regarding this, finding an alternative for VS Code, I found Lapce(https://lapce.dev/), which is still pre-alpha but promising. If I have a chance, let me post for this.