tag:blogger.com,1999:blog-35942886310913636652024-03-07T12:34:58.155+09:00Code{nested}Life of a programmer with business major, with no formal programming education experience at all. :PT.Robinhttp://www.blogger.com/profile/12834494724018712162noreply@blogger.comBlogger131125tag:blogger.com,1999:blog-3594288631091363665.post-23252151703381344982024-03-07T10:12:00.004+09:002024-03-07T10:17:52.450+09:00PostgreSQL vs. SQLite: read & write in multithreaded environment<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiFVF-tSf-JSRQuj9x20WKWcVFa5KWoqA2JEbn-VvWhFB_ZOQYax_eeu6pex9xo1MCg18XMoCRntPOs2zulbRNNwGzS5oX4BIwWfn42RvR9Dee-s42BkeFHhUpRnng5m0GS-2qUWgKhgtLlLY2AmQgAxcUEVxuNnoOXP3O2Ljo_b5uGhyphenhyphen3UoiNOu0W-mYo/s874/B4rVC4ICQAAr65m.jpeg" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="451" data-original-width="874" height="206" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiFVF-tSf-JSRQuj9x20WKWcVFa5KWoqA2JEbn-VvWhFB_ZOQYax_eeu6pex9xo1MCg18XMoCRntPOs2zulbRNNwGzS5oX4BIwWfn42RvR9Dee-s42BkeFHhUpRnng5m0GS-2qUWgKhgtLlLY2AmQgAxcUEVxuNnoOXP3O2Ljo_b5uGhyphenhyphen3UoiNOu0W-mYo/w400-h206/B4rVC4ICQAAr65m.jpeg" width="400" /></a></div><p>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.</p><p>And yes. It was a BIG mistake.</p><p>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 <b><span style="color: #2b00fe;"><span style="font-family: courier;">-DSQLITE_THREADSAFE=2</span> </span></b>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.</p><p>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 <b>100 milliseconds </b>for just running <b style="font-family: courier;"><span style="color: #2b00fe;">SELECT field2 WHERE field1='something'</span></b>. No, the table was small enough. There were only <b>680 records </b>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.</p><p>Elephant is tough, but as a side effect it's too slow.......</p><p>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 <b><span style="color: #2b00fe;">.timer </span></b>SQLite shell and it took <b>less than 0.001 second.</b> Yohoo!</p><p>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</p><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.</p>T.Robinhttp://www.blogger.com/profile/12834494724018712162noreply@blogger.com0tag:blogger.com,1999:blog-3594288631091363665.post-38031136413009112152024-03-07T09:53:00.004+09:002024-03-07T10:15:55.008+09:00PostgreSQL vs. SQLite: 멀티스레드 환경에서의 읽기-쓰기<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhGJjQorQBgD5gHjeNs2MfdjZaHnEOOxMybsCas2KvbafxEoLhCC7A3lbrnda9gjTrPxb69hDe3m8EBOMYEgtp2R3waLOeRai2rc2t-5OzfMVheMSSvDmxjSbjwX3-rZWq-6esr38JUviwl_L3sJidDKXo1YxhHW00PiIB86v3PWEbb4UZR3A4Z6NgWEcA/s874/B4rVC4ICQAAr65m.jpeg" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="451" data-original-width="874" height="206" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhGJjQorQBgD5gHjeNs2MfdjZaHnEOOxMybsCas2KvbafxEoLhCC7A3lbrnda9gjTrPxb69hDe3m8EBOMYEgtp2R3waLOeRai2rc2t-5OzfMVheMSSvDmxjSbjwX3-rZWq-6esr38JUviwl_L3sJidDKXo1YxhHW00PiIB86v3PWEbb4UZR3A4Z6NgWEcA/w400-h206/B4rVC4ICQAAr65m.jpeg" width="400" /></a></div><p>그러니까....... 시작은 소소했습니다. 뭔가 데이터를 캐싱할 일이 있었는데, DB에 쌓아두고 index 걸면 나머지는 DB가 알아서 하지 않겠느냐 하는 거였습니다. 데이터라고 해봐야 별거 없이 그냥 TEXT 필드 두 개가 전부인데다가 실제로 데이터를 찾을 때는 둘 중 하나만 가지고 찾으면 되는 매우 간단한 key-value store 형태의 구조라, 어떤 DB를 써도 상관없겠지 하고 안일하게(.......) 생각했습니다.</p><p>옙. 그건 큰 착각이었습니다.</p><p>처음에는 SQLite를 사용해봤는데, 멀티스레드 환경에서 동시에 쓰기를 수행하니 컴파일시 <b><span style="color: #2b00fe;"><span style="font-family: courier;">-DSQLITE_THREADSAFE=2</span> </span></b>옵션을 추가해도 일부 레코드가 유실되더군요. 동일한 데이터를 동일한 조건에서 동일하게 넣는데 어떨 때는 레코드가 20개만 있고, 어떨 때는 40개, 어떨 때는 또 26개...... 게다가 심지어 동작에는 이상이 없습니다(......). 정말이지 What the hell을 라이브로 외치기 딱 좋은 순간이죠.</p><p>그래서 DB를 PostgreSQL로 바꿨습니다. 우리의 우직하고 단단한 코끼리 친구는 레코드 유실 없이 모든 데이터를 다 받아서 잘 보관해줍니다. 그리고 만족하고 있던 그 찰나....... 인덱스까지 b-tree로 잘 걸어줬음에도 불구하고 <span style="font-family: courier;"><b><span style="color: #2b00fe;">SELECT field2 WHERE field1='something' </span></b></span>하나 수행하는데 무려 <b>100밀리초</b>가 걸립니다. 그렇다고 해서 테이블의 크기가 컸던 것도 아닌게, 레코드라고 해봐야 겨우 <b>680여개 </b>뿐이었고, 레코드 길이도 하나는 길어봐야 30글자, 나머지는 4글자 고정이었거든요. 나름 환경설정 최적화도 해 주었고, 그래서 대형 테이블에서는 비교적 빠르게 돌아갔던 터라 안심하고 있었는데, 이런 사소한 부분에서 성능 문제를 맞닥뜨릴줄은 꿈에도 몰랐습니다.</p><p>코끼리는 딴딴하지만 그 대신 미친듯이 느린걸로......</p><p>해서...... 혹시나 해서 PostgreSQL에서 pg_dump로 데이터를 덤프해서 SQLite로 옮긴 뒤에 동일하게 SELECT를 수행해 봤습니다. SQLite shell에서 <b><span style="color: #2b00fe;">.timer </span></b>걸고 돌려보니 <b>0.001초</b>가 채 안 됩니다(......).</p><p>추가로 더 시험을 해 본 결과, SQLite에서는 멀티스레드 지원 옵션이 추가된 상황에서 데이터 유실을 완벽하게 방어하지는 못하고, std::mutex 같은 별도 외부 지원을 추가해야 되더군요. 아마 fread() call이 멀티스레드 상황에서의 serialization을 제대로 지원하지 않지 않는 것 아닐까 하고 추측하고 있습니다만, 거기까지 추적하기엔 시간도 없거니와 능력도 안 되어서...... :P</p><p>하여간, 지금은 SQLite + WAL mode + 내부 캐시 증량 + std::mutex 조합을 사용하고 있습니다. 쓰기 속도는 아직 충분하다고 여겨집니다만, 만일 더 필요하다면 파일을 여러개로 늘린 뒤에 non-cryptographic hash로 load balancing을 하면 될 것 같습니다.</p>T.Robinhttp://www.blogger.com/profile/12834494724018712162noreply@blogger.com0tag:blogger.com,1999:blog-3594288631091363665.post-11737809533096453622024-02-19T11:16:00.002+09:002024-02-19T11:16:19.966+09:00Haste makes waste<p>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.<br /></p><p>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.<br /></p><p>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<br /></p><p>It's nothing, but I could reinforce my behavior with <b>"I'm not wrong!", </b>so I drop a line here.<br /></p>T.Robinhttp://www.blogger.com/profile/12834494724018712162noreply@blogger.com0tag:blogger.com,1999:blog-3594288631091363665.post-84644301815150517652024-02-19T11:02:00.001+09:002024-02-19T11:02:07.879+09:00급할수록 돌아가라<p>간혹 옛 성현들의 말씀 중에 틀린게 없다는걸 새삼 실감할 때가 있습니다. 최근에 그런걸 다시 한 번 느꼈는데...... 급할수록 돌아가라 하셔서 돌아갔더니만 궁극적으로 시간을 더 절약하게 되더군요.</p><p>상황은 대충 이렇습니다. 특정 데이터를 추출하는 새로운 기능을 만들어야 하는데, 겉으로 보기엔 간단하지만 실제로는 간단하지 않겠다는 생각이 들었습니다. 하나의 raw data에서 요약본과 메인 데이터 두 개를 추출해야 하는데, 반복문을 한 번만 돌리면 전체적으로는 수행이 더 빠르겠다는 생각이 들더군요. 하지만 기존의 데이터 저장 체계와 맞물려서 생각해보면 이걸 두 개의 스레드로 분리해야 할 수밖에 없는 상황이었는데...... 하루면 끝낼 일을, 일부러 사흘 정도 딴짓(?)을 하면서 시간을 질질 끌어보니 답이 나왔습니다. loop를 한 개로 놓고 데이터를 subsection 형태로 나누면 어떻게든 되곘더군요.</p><p>제가 평소 업무중 중요시하는 것 중 하나가 부화 효과입니다. 어려운 문제가 주어졌을 때 그 문제에 무작정 집중하지 말고, 그 문제와 전혀 관련없는 다른 일을 수행하다가 원래의 문제로 돌아오면 기존에는 보지 못했던 새로운 지평이 보인다는, 무려 심리학 실험을 통해 검증된 효과입니다. 왜 이러한 효과가 발생하느냐에 대해서는 아직 학계에서 갑론을박 중인 것으로 알고 있습니다만, 블랙박스의 내부 구조를 밝히는 것은 학계의 영역이고, 전 그저 쓰기만 하면 될 뿐이죠. :D</p><p>소소합니다만, <b>나는 틀리지 않았어! </b>하고 다시 한 번 확인하게 되는, 강화 요인 중 하나가 되었기에 끼적여봅니다.<br /></p>T.Robinhttp://www.blogger.com/profile/12834494724018712162noreply@blogger.com0tag:blogger.com,1999:blog-3594288631091363665.post-52982204048306792252023-09-06T10:36:00.006+09:002023-09-06T10:36:56.707+09:00Journey to the Vim IDE, and what I learned<p>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.<br /></p><p>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:</p><ol style="text-align: left;"><li>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.<br /></li><li>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!"</li></ol><p>And personally, I recognized that I tried to make Vim-IDE as similar as VS Code. It's not only <i><b>behave mswin</b> </i>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.<br /></p><p> In my experience, the strength of of Vim is in editing, but not insertion. For example, you have commands like <i><b>da"</b>, <b>di'</b>, <b>dt(</b></i>, or the difference of <i>O</i> and <i>o</i>. And this is the very strength of modal editor, which provides a <b>"special mode" </b>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 <i>normal mode </i>and <i>insert mode </i>and enduring the inconvenience only for features which I'd use once a month at best is quite a nonsense.<br /></p><p>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."<br /></p><p>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."</p><p>So everyone, happy Vimming! <br /></p><p><b>P.S: <br /></b></p><p>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<br /></p><p>Regarding this, finding an alternative for VS Code, I found Lapce(<a href="https://lapce.dev/">https://lapce.dev/</a>), which is still pre-alpha but promising. If I have a chance, let me post for this.</p>T.Robinhttp://www.blogger.com/profile/12834494724018712162noreply@blogger.com0tag:blogger.com,1999:blog-3594288631091363665.post-1013815581457920522023-09-06T10:15:00.000+09:002023-09-06T10:15:01.522+09:00Vim IDE로의 여행. 그리고 내가 배운 것<p>좀 늦었습니다만, 만으로 한달만에 Vim의 개발자인 Bram Moolenaar가 타개했다는 소식을 들었습니다. 위대한 텍스트 편집기의 개발자였던 그를 기억합니다. 편히 잠드시길.</p><p>그 여파인가......는 모르곘습니다만, 한 며칠정도를 Vim을 IDE화하는 작업에 쏟아붓고, 개인적으로 개발하던 프로젝트에 이를 적용해 보았습니다. 두 번째 시도라 그런지 결과는 꽤나 만족스러웠습니다만, 그럼에도 불구하고 Vim을 IDE로 사용하는 것은 보류하고, 기존에 쓰던 Visual Studio Code(이하 VS Code)를 계속 사용하기로 결정했습니다. 이유인즉슨:</p><ol style="text-align: left;"><li>커스터마이징에 손이 너무 많이 갑니다. 플러그인끼리의 충돌이 큽니다. VS Code에서는 기본적으로 지원하는 기능들조차 플러그인으로 적용해야 되는데, 이렇게 플러그인들이 쌓이다 보면 자기들끼리 설정을 변경하다가 충돌해서 프로그램이 오동작하는 경우가 꽤 생깁니다</li><li>사람들이 간과하는 부분 중 하나인데, VS Code도 95% 이상의 동작을 마우스 없이 사용할 수 있습니다. 단축키도 단축키이거니와, command pallette는 폼으로 있는게 아닐 뿐더러, fuzzy search까지 지원합니다. 요컨데, Vim의 "우리는 마우스 필요 없음!"이라는 주장이 VS Code에서도 똑같이 적용될 수 있다는 이야기지요.</li></ol><p>그리고 개인적으로는, Vim을 IDE로 만들면서 VS Code와 유사하게 만들려고 시도를 하고 있더군요. <b>behave mswin</b>은 기본이고, 탭 이동이라던가, 파일 탐색기라던가 해서 진짜로 VS Code처럼 만들고 있었습니다. 이럴거면 그냥 VS Code 쓰고 말지 뭐하러 어렵게 Vim을 쓸까 하는 자괴감이 왔습니다.</p><p>제 생각에, Vim이 강점을 가지는 부분은 텍스트의 입력보다는 편집입니다. da", di', dt(같은 명령어라던가, O와 o의 차이라던가 하는 부분들이 있죠. 그리고 이건 modal editor가 가지는 필연적인 장점이기도 합니다. 이를테면, 텍스트 편집을 위한 전용 모드를 제공하기 때문에 편집에 강점을 가지죠. 그런데...... 저같은 경우 텍스트의 편집보다는 단순 입력에 훨씬 더 많은 시간을 쏟아붓습니다. 그리고 그 때 필요한 기능은 잘해봐야 자동완성 정도에요. Vim이 대단한 편집기인건 맞습니다만, 제 use case만 놓고 보면그 모든 기능 중 상당수는 쓸 일이 거의 없습니다. 한달에 한두번 쓰면 많이 쓸 것 같은 기능을 위해서 normal mode와 insert mode를 왔다갔다하면서 불편함을 감수하는건 주객이 전도된 일이 아닌가 합니다.</p><p>요컨데, Vim은 대단한 편집기이긴 하지만, 제 사용 성향에는 맞지 않는 것 같습니다. Reddit의 누군가가 <b>"VS Code와 Vim은 누가 더 낫냐의 문제가 아니다. 그냥 다를 뿐이야. 다른 성향의 사람들에게 어필하는 것 뿐이라고"</b>라고 말한게 잊혀지지 않는군요.</p><p>Vim heavy user 여러분, 만일 이 글을 보신다면 노여워하지 마시고, 그냥 "모든 사람에게 다 꼭 맞는 셔츠따위는 없다"는 오래된 격언의 적용 사례라고 생각해 주시면 좋겠습니다.</p><p>그럼 모두들, happy Vimming! <br /></p><p><b>P.S: <br /></b></p><p>다만, VS Code가 가지는 태생적 한계(Javascript 기반)로 인해 비교적 성능이 떨어지는 부분은 확실히 불만이 있습니다. Vim이 그런 의미에선 좀 더 낫긴 하지만, Vimscript 또한 스크립트 기반인걸 생각해보면 어떤 측면에서는 돗진이 갯진인지라......</p><p>관련해서 VS Code의 대체품을 찾던 중, Lapce라는, 아직 pre-alpha지만 가능성이 보이는 프로젝트를 발견했습니다(<a href="https://lapce.dev/">https://lapce.dev/</a>). 기회가 되면 추후 이 프로그램에 대해서도 포스팅해 보도록 하지요.</p>T.Robinhttp://www.blogger.com/profile/12834494724018712162noreply@blogger.com0tag:blogger.com,1999:blog-3594288631091363665.post-84273138109328315752023-06-03T23:51:00.007+09:002024-01-31T16:00:11.394+09:00Vim vs. Neovim: it's yet immature<p>I had a chance to try Neovim, which is quite a hot potato among Vim users. As far as I know, Neovim is started as a fork of Vim to include some features rejected by Vim maintainers. What I know about Neovim is only two: they adopted Lua as their script language along with VimScript, and LSP client is included(after v0.4).</p><p><span face="sans-serif"><span style="font-size: 15px;">I was really not satisfied with LSP support from Vim, so I was curious about how Neovim native LSP works. Conclusion? Well...... Before jumping into LSP, it looks like their big file management is still living the era of Vim 7. The memory consumption of Vim 9 and Neovim 0.9 are quite similar, but while in Vim you could easily navigate from here to there without any delay from Vim 8, Neovim showed quite formiddable delay when I tried to jump from the start to end at once. Also, the initial loading was a bit slow....... Though others are unanimously saying that "it's GREAT!", for me it lacks some basics as a text editor. Also, Windows installer was a bit premature compared with vim-win32-installer.</span></span></p><p>Though LSP is important for me too, but my job frequently requires processing of multi-GB size text files, I think I'll reside on Vim for some time being.</p><p><b>P.S.1: My Main Development Environment<br /></b>For your reference, I use Visual Studio Code as my main development environment. Vim is mainly used to edit some texts.</p><p><b>P.S.2: About Vim's LSP Plugins</b><br />Well, for me, CoC<span face="sans-serif" style="font-size: 15px;">(<a href="https://github.com/neoclide/coc.nvim">https://github.com/neoclide/coc.nvim</a>)</span>, the final boss(?), is isolating itself outside Vim ecosystem(if I have to run Node.js inside Vim, then I'd use Visual Studio Code instead), vim-lsp(<span face="sans-serif" style="font-size: 15px;"><a href="https://github.com/prabirshrestha/vim-lsp">https://github.com/prabirshrestha/vim-lsp</a>) lacks support on how to show the diagnosis results, and ALE(</span><span face="sans-serif" style="font-size: 15px;"><a href="https://github.com/dense-analysis/ale">https://github.com/dense-analysis/ale</a>) doesn't show function signatures and code formatting is not supported....... Everything loses at least one core value from their stuff.</span></p><p><span face="sans-serif" style="font-size: 15px;">And today, I found this: </span><a href="https://github.com/yegappan/lsp">https://github.com/yegappan/lsp</a></p><p></p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEhhBKLnaK3sk3uFq6drkkEgaF4qqsmZ9aaY-e577cjYsAKrLMbEwMFlBt8UXF2nn01Q9eTz5UVtvekZEN2MxRlWBfmwcDyVKn_tgVLhXSs3sBJsDBQNo4XTi7APCw9pD5sp6qr6ls3Z_bEPjLfcngIK1FbHTTvrnyEOA-Pg7Y0kIodMVMLHYLQk7jPK" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="279" data-original-width="918" height="97" src="https://blogger.googleusercontent.com/img/a/AVvXsEhhBKLnaK3sk3uFq6drkkEgaF4qqsmZ9aaY-e577cjYsAKrLMbEwMFlBt8UXF2nn01Q9eTz5UVtvekZEN2MxRlWBfmwcDyVKn_tgVLhXSs3sBJsDBQNo4XTi7APCw9pD5sp6qr6ls3Z_bEPjLfcngIK1FbHTTvrnyEOA-Pg7Y0kIodMVMLHYLQk7jPK" width="320" /></a></div><p style="text-align: left;">You've got to make your hands a bit dirty to configure(even you've got to register your LSP servers manually), but anyway it's editing some text files(:P). And with propre configuration it provides useful information as you see in the above screenshot. And one more thing: this is developed with Vim9Script - in other words, it doesn't work in Neovim.</p><div><div><p><b>P.S.3: Vim vs. Neovim - a Bridge You Can't Turn Back<br /></b>While Neovim concentrates on Lua Vim made Vim9script, and Neovim announced they won't support Vim9script. I think this will be the marker of separation between these two projects. Personally it reminds me of the end of "full compatiblity" between MySQL 8 and MariaDB 10 due to differences in JSON support. The difference? In MySQL / MariaDB case the fork, MariaDB, shows better performance than MySQL, while in Vim / Neovim case the original, Vim, is better(at least to me).</p></div></div>T.Robinhttp://www.blogger.com/profile/12834494724018712162noreply@blogger.com0tag:blogger.com,1999:blog-3594288631091363665.post-81133745081533851032023-06-03T23:35:00.002+09:002023-06-03T23:55:17.964+09:00Vim vs. Neovim: 아직은 좀 설익은 과일<p><span face="sans-serif"><span style="font-size: 15px;">최근 몇년간 Vim 사용자들에게 최고의 화두였던 Neovim을 잠깐 만져봤습니다. 제가 듣기로 Neovim은 </span></span><span face="sans-serif" style="font-size: 15px;">Vim에서 이것좀 구현해주세요 했다가 "야 Vim은 지금 이 상태로 완전하거든?" 이란 말과 함께 퇴짜맞은 기능들을 넣은 fork로 출발한 프로젝트로 알고 있습니다. 주요한 특징이라면 스크립트 언어로 Lua를 채용했다거나, LSP를 내장했다거나(v0.4 이후) 정도로군요.</span></p><p><span face="sans-serif"><span style="font-size: 15px;">그러잖아도 Vim의 LSP 지원이 다들 영 마음에 안 들어서 좀 고민하던 차에, 내장 LSP가 어떻게 생겨먹었나 궁금해서 Neovim을 써봤습니다. 결론은 뭐....... LSP는 고사하고 대용량 파일 처리가 Vim 7 시절 그대로인 것 같더군요. </span></span><span face="sans-serif" style="font-size: 15px;">메모리 사용량은 Neovim 0.9나 Vim 9이나 비슷한데, Vim 8은 큰 파일도 날라댕기면서(......) 왔다갔다 하는게 가능한 반면 Neovim은 파일 처음에서 끝까지 가려면 한참을 기다려야 하더군요. 초기 로딩도 슬쩍 느리고...... 뭐랄까요. 써본 사람들은 다들 좋다 좋다 하는데 제게는 기본기가 좀 어설프다는 느낌이었습니다. Windows용 설치파일도 Vim Win32 Installer에 비하면 여러가지로 부족한 점이 많아 보였고요.</span></p><p>LSP도 LSP지만, 전 아무래도 대용량 텍스트(수 GB 이상)를 처리해야 되는 경우가 꽤 되어서, <span face="sans-serif" style="font-size: 15px;">당분간은 Vim에 계속 남아있게 될 것 같습니다.</span></p><p><span face="sans-serif" style="font-size: 15px;"><b>P.S.1: 나의 메인 개발환경<br /></b>아, 참고로 전 제 메인 개발환경으로 Visual Studio Code를 씁니다. Vim은 텍스트 편집이 주 용도에요.</span></p><p><b>P.S.2: Vim의 LSP 플러그인 이야기<br /></b><span face="sans-serif"><span style="font-size: 15px;">일단 대충 끝판왕(......)인 CoC(<a href="https://github.com/neoclide/coc.nvim">https://github.com/neoclide/coc.nvim</a>)는 Vim의 ecosystem 바깥에서 따로 놀고 있고(Vim 안에서 Node.js 돌릴거면 그냥 Visual Studio Code 쓰고 말죠), </span></span><span face="sans-serif" style="font-size: 15px;">vim-lsp(<a href="https://github.com/prabirshrestha/vim-lsp">https://github.com/prabirshrestha/vim-lsp</a>)는 화면 표시 기능이 빈약하고, </span><span face="sans-serif" style="font-size: 15px;">ALE(</span><span face="sans-serif" style="font-size: 15px;"><a href="https://github.com/dense-analysis/ale">https://github.com/dense-analysis/ale</a>)는 function signature가 표시가 안 될 뿐더러 code formatting 기능이 없고....... 뭐 다들 뭔가 한가지씩 필요한게 빠진 느낌이었습니다.</span></p><p>그러다가 오늘 괜찮은걸 발견했습니다. 이겁니다: https://github.com/yegappan/lsp</p><p></p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEhhBKLnaK3sk3uFq6drkkEgaF4qqsmZ9aaY-e577cjYsAKrLMbEwMFlBt8UXF2nn01Q9eTz5UVtvekZEN2MxRlWBfmwcDyVKn_tgVLhXSs3sBJsDBQNo4XTi7APCw9pD5sp6qr6ls3Z_bEPjLfcngIK1FbHTTvrnyEOA-Pg7Y0kIodMVMLHYLQk7jPK" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="279" data-original-width="918" height="97" src="https://blogger.googleusercontent.com/img/a/AVvXsEhhBKLnaK3sk3uFq6drkkEgaF4qqsmZ9aaY-e577cjYsAKrLMbEwMFlBt8UXF2nn01Q9eTz5UVtvekZEN2MxRlWBfmwcDyVKn_tgVLhXSs3sBJsDBQNo4XTi7APCw9pD5sp6qr6ls3Z_bEPjLfcngIK1FbHTTvrnyEOA-Pg7Y0kIodMVMLHYLQk7jPK" width="320" /></a></div><br />설정이 좀 번거롭긴 한데(LSP 서버도 수동으로 등록해야 됩니다), 그래봐야 텍스트 파일 편집이 전부이기도 하고, 잘 만져주면 위쪽 화면처럼 표시도 해줘서 보기가 엄청 편합니다. 그리고 결정적으로....... Vim9script로 만들었더군요. 옙. Neovim에서는 못 씁니다(......).<p></p><p><b>P.S.3: Vim vs. Neovim - 돌아올 수 없는 다리<br /></b>Neovim이 Lua를 미는 동안 Vim은 Vim9script를 만들었고, Neovim은 Vim9script를 지원하지 않겠다고 선언했지요. 저는 이게 아마 둘의 갈림길이 될 것 같습니다. 개인적으로는 JSON 지원 방식의 차이로 인해 MySQL 8과 MariaDB 10 사이의 호환성이 갈라진게 떠오르는군요. 차이라면 MySQL / MariaDB는 fork본인 MariaDB가 MySQL보다 여러모로 더 좋은 성능을 보여주는 반면 Vim / Neovim은 원본인 (최소한 제 환경에서는) Vim이 Neovim보다 더 좋아보이는 것 같군요.</p>T.Robinhttp://www.blogger.com/profile/12834494724018712162noreply@blogger.com0tag:blogger.com,1999:blog-3594288631091363665.post-69493047195899121422023-01-31T19:02:00.002+09:002023-01-31T19:02:20.188+09:00std::move = when std::optional should be launched<p>Recently I had a chance to take a look at Rust. When returning from a function Rust uses std::option to return either class A in success or class B in exception.<br /></p><p>And I found out something similar in C++, namely <i>std::optional. </i>Most of C++ users argued that "why use <i>std::optional</i> when we can fully make use of null pointers?" According to C++ Committee, it was due to <b>minimize human errors. </b>Then we can ask one thing: what kind of errors, then?<br /></p><p>Let's take a look at the code below:<br /></p><div style="background-color: whitesmoke; color: #333333; font-family: Consolas, 'Courier New', monospace; font-size: 14px; font-weight: normal; line-height: 19px; white-space: pre;"><div><span style="color: #7a3e9d;">struct</span><span style="color: #333333;"> </span><span style="color: #7a3e9d; font-weight: bold;">Insider</span><span style="color: #333333;"> </span><span style="color: #777777;">{</span></div><div><span style="color: #aaaaaa; font-style: italic;"> /* whatever great data structure */</span></div><div><span style="color: #777777;">};</span></div><br /><div><span style="color: #7a3e9d;">struct</span><span style="color: #333333;"> </span><span style="color: #7a3e9d; font-weight: bold;">anti_memory_leak</span><span style="color: #333333;"> </span><span style="color: #777777;">{</span></div><div><span style="color: #333333;"> Insider </span><span style="color: #777777;">*</span><span style="color: #333333;">insider</span><span style="color: #777777;">=</span><span style="color: #9c5d27;">nullptr</span><span style="color: #777777;">;</span></div><div><span style="color: #333333;"> </span><span style="color: #aa3731; font-weight: bold;">~anti_memory_leak</span><span style="color: #777777;">()</span><span style="color: #333333;"> </span><span style="color: #777777;">{</span></div><div><span style="color: #333333;"> </span><span style="color: #4b69c6;">if</span><span style="color: #777777;">(</span><span style="color: #333333;">insider</span><span style="color: #777777;">)</span><span style="color: #333333;"> </span><span style="color: #777777;">delete</span><span style="color: #333333;"> insider</span><span style="color: #777777;">;</span></div><div><span style="color: #333333;"> </span><span style="color: #777777;">}</span></div><div><span style="color: #777777;">};</span></div></div><p>There's nothing special in this code. Since the class Insider can be used optionally, it can be allocated to heap. When <i>anti_memory_leak </i>is removed from memory, Insider object will be also removed in destructor so we have means for memory leak. We proved that we can do it without <i>std::optional</i>.<br /></p><p>...... Did we?</p><p>Then let's investigate the code below:</p><div style="background-color: whitesmoke; color: #333333; font-family: Consolas, 'Courier New', monospace; font-size: 14px; font-weight: normal; line-height: 19px; white-space: pre;"><div><span style="color: #7a3e9d;">void</span><span style="color: #333333;"> </span><span style="color: #aa3731; font-weight: bold;">doSomethingGreat</span><span style="color: #777777;">()</span></div><div><span style="color: #777777;">{</span></div><div><span style="color: #333333;"> anti_memory_leak object1</span><span style="color: #777777;">;</span></div><div><span style="color: #333333;"> </span><span style="color: #7a3e9d;">object1</span><span style="color: #777777;">.</span><span style="color: #7a3e9d;">insider</span><span style="color: #777777;">=new</span><span style="color: #333333;"> </span><span style="color: #aa3731; font-weight: bold;">Insider</span><span style="color: #777777;">();</span></div><br /><div><span style="color: #333333;"> </span><span style="color: #7a3e9d; font-weight: bold;">std</span><span style="color: #777777;">::</span><span style="color: #333333;">vector</span><span style="color: #777777;"><</span><span style="color: #333333;">anti_memory_leak</span><span style="color: #777777;">></span><span style="color: #333333;"> vector1</span><span style="color: #777777;">;</span></div><div><span style="color: #333333;"> </span><span style="color: #7a3e9d;">vector1</span><span style="color: #777777;">.</span><span style="color: #aa3731; font-weight: bold;">push_back</span><span style="color: #777777;">(</span><span style="color: #7a3e9d; font-weight: bold;">std</span><span style="color: #777777;">::</span><span style="color: #aa3731; font-weight: bold;">move</span><span style="color: #777777;">(</span><span style="color: #333333;">object1</span><span style="color: #777777;">));</span></div><div><span style="color: #333333;"> </span><span style="color: #7a3e9d;">vector1</span><span style="color: #777777;">.</span><span style="color: #aa3731; font-weight: bold;">back</span><span style="color: #777777;">().</span><span style="color: #7a3e9d;">insider</span><span style="color: #777777;">-></span><span style="color: #7a3e9d;">value1</span><span style="color: #777777;">=</span><span style="color: #9c5d27;">20</span><span style="color: #777777;">;</span><span style="color: #aaaaaa; font-style: italic;"> // CRASH!</span></div><div><span style="color: #777777;">}</span></div></div> <p>This function crashed in the last line. Why? The reason is in the one line above. When you call <i>vector1.push_back()</i>, even though you use <i>std::move()</i> <i>object1 </i>is destructed and recreated. And when destructing, the destructor of <i>anti_memory_leak </i>is called, and it surely remove <i>insider </i>from the heap. In other words, <i>vector1.back().insider </i>becomes a dangling pointer. It's kind of unfortunate, the result is the same if you use <i>emplace_back() </i>instead of <i>push_back()</i>. Anyway the application crash.<br /></p><p>Now is the time <i>std::optional </i>should be used. If you declare an object with <i>std::optional</i>, the memory is initialized without allocating that optional object, and it is initialized when the optional object is explicitly created. Of course there's a small overhead, but say, it's also same for other similar(?) classes like <i>std::shared_ptr</i>. We have raw pointers, but to manage our precious heap more safely, we can automate some of the management so that we can solve potential incidents(including both memory leak and dangling pointer) more easily.<br /></p><p>If we refactor the code above using <i>std::optional</i>, it will be like this:</p><div style="background-color: whitesmoke; color: #333333; font-family: Consolas, 'Courier New', monospace; font-size: 14px; font-weight: normal; line-height: 19px; white-space: pre;"><div><span style="color: #7a3e9d;">struct</span><span style="color: #333333;"> </span><span style="color: #7a3e9d; font-weight: bold;">Insider</span><span style="color: #333333;"> </span><span style="color: #777777;">{</span></div><div><span style="color: #333333;"> </span><span style="color: #7a3e9d;">int</span><span style="color: #333333;"> value1</span><span style="color: #777777;">;</span></div><div><span style="color: #aaaaaa; font-style: italic;"> /* whatever great data structure */</span></div><div><span style="color: #777777;">};</span></div><br /><div><span style="color: #7a3e9d;">struct</span><span style="color: #333333;"> </span><span style="color: #7a3e9d; font-weight: bold;">anti_memory_leak</span><span style="color: #333333;"> </span><span style="color: #777777;">{</span></div><div><span style="color: #333333;"> </span><span style="color: #7a3e9d; font-weight: bold;">std</span><span style="color: #777777;">::</span><span style="color: #333333;">optional</span><span style="color: #777777;"><</span><span style="color: #333333;">Insider</span><span style="color: #777777;">></span><span style="color: #333333;"> insider</span><span style="color: #777777;">;</span></div><div><span style="color: #333333;"> </span><span style="color: #aa3731; font-weight: bold;">~anti_memory_leak</span><span style="color: #777777;">()</span><span style="color: #333333;"> </span><span style="color: #777777;">{</span></div><div><span style="color: #333333;"> </span><span style="color: #4b69c6;">if</span><span style="color: #777777;">(</span><span style="color: #333333;">insider</span><span style="color: #777777;">)</span><span style="color: #333333;"> </span><span style="color: #777777;">delete</span><span style="color: #333333;"> insider</span><span style="color: #777777;">;</span></div><div><span style="color: #333333;"> </span><span style="color: #777777;">}</span></div><div><span style="color: #777777;">};</span></div><br /><div><span style="color: #7a3e9d;">void</span><span style="color: #333333;"> </span><span style="color: #aa3731; font-weight: bold;">doSomethingGreat</span><span style="color: #777777;">()</span></div><div><span style="color: #777777;">{</span></div><div><span style="color: #333333;"> anti_memory_leak object1</span><span style="color: #777777;">;</span></div><div><span style="color: #333333;"> </span><span style="color: #7a3e9d;">object1</span><span style="color: #777777;">.</span><span style="color: #7a3e9d;">insider</span><span style="color: #777777;">=</span><span style="color: #aa3731; font-weight: bold;">Insider</span><span style="color: #777777;">();</span></div><br /><div><span style="color: #333333;"> </span><span style="color: #7a3e9d; font-weight: bold;">std</span><span style="color: #777777;">::</span><span style="color: #333333;">vector</span><span style="color: #777777;"><</span><span style="color: #333333;">anti_memory_leak</span><span style="color: #777777;">></span><span style="color: #333333;"> vector1</span><span style="color: #777777;">;</span></div><div><span style="color: #333333;"> </span><span style="color: #7a3e9d;">vector1</span><span style="color: #777777;">.</span><span style="color: #aa3731; font-weight: bold;">push_back</span><span style="color: #777777;">(</span><span style="color: #7a3e9d; font-weight: bold;">std</span><span style="color: #777777;">::</span><span style="color: #aa3731; font-weight: bold;">move</span><span style="color: #777777;">(</span><span style="color: #333333;">object1</span><span style="color: #777777;">));</span></div><div><span style="color: #333333;"> </span><span style="color: #7a3e9d;">vector1</span><span style="color: #777777;">.</span><span style="color: #aa3731; font-weight: bold;">back</span><span style="color: #777777;">().</span><span style="color: #7a3e9d;">insider</span><span style="color: #777777;">.</span><span style="color: #7a3e9d;">value1</span><span style="color: #777777;">=</span><span style="color: #9c5d27;">20</span><span style="color: #777777;">;</span><span style="color: #aaaaaa; font-style: italic;"> // OK</span></div><div><span style="color: #777777;">}</span></div></div><p></p><p>If the flow of the code is simple it won't be a big problem. However, if the flow becomes anyway compilcated(e.g. multithreading), there should be chances to free memory or miss the chance when we have to, regardless of my intention. Let's think of <i>std::optional </i>as some kind of insurance policy; though we all agree that insurance fee is somewhat "waste of money"(lol), but we spend money to prepare for the worst? I think it's the same for <i>std::optional</i>.</p>T.Robinhttp://www.blogger.com/profile/12834494724018712162noreply@blogger.com0tag:blogger.com,1999:blog-3594288631091363665.post-48562216142749771222023-01-31T18:46:00.001+09:002023-01-31T18:46:37.488+09:00std::move = std::optional이 출동할 때<p>최근 Rust를 살펴볼 기회가 있었습니다. Rust는 function이 결과를 반환할때 std::option이라는걸 사용해서 정상이면 A 클래스를, 비정상이면 B 클래스를 반환하는 형태를 취하고 있더군요.<br /></p><p>그래서...... 혹시나 해서 찾아봤더니, C++에도 std::optional이 있는 것을 발견했습니다. 다만 대부분의 사용자들은 std::optional을 왜 쓰느냐는 입장이었습니다. 우리에겐 (밉고도 고운) nullptr이 있는데 뭐하러 저런걸 쓰느냐...... 하는 거였습니다. C++ Committee의 입장은 <b>실수를 줄이기 위해서</b>라고 하던데, 그렇다면 대체 무슨 실수가 나올 수 있을까요?</p><p>우선, 다음의 코드를 살펴봅시다.</p><div style="background-color: whitesmoke; color: #333333; font-family: Consolas, 'Courier New', monospace; font-size: 14px; font-weight: normal; line-height: 19px; white-space: pre;"><div><span style="color: #7a3e9d;">struct</span><span style="color: #333333;"> </span><span style="color: #7a3e9d; font-weight: bold;">Insider</span><span style="color: #333333;"> </span><span style="color: #777777;">{</span></div><div><span style="color: #aaaaaa; font-style: italic;"> /* whatever great data structure */</span></div><div><span style="color: #777777;">};</span></div><br /><div><span style="color: #7a3e9d;">struct</span><span style="color: #333333;"> </span><span style="color: #7a3e9d; font-weight: bold;">anti_memory_leak</span><span style="color: #333333;"> </span><span style="color: #777777;">{</span></div><div><span style="color: #333333;"> Insider </span><span style="color: #777777;">*</span><span style="color: #333333;">insider</span><span style="color: #777777;">=</span><span style="color: #9c5d27;">nullptr</span><span style="color: #777777;">;</span></div><div><span style="color: #333333;"> </span><span style="color: #aa3731; font-weight: bold;">~anti_memory_leak</span><span style="color: #777777;">()</span><span style="color: #333333;"> </span><span style="color: #777777;">{</span></div><div><span style="color: #333333;"> </span><span style="color: #4b69c6;">if</span><span style="color: #777777;">(</span><span style="color: #333333;">insider</span><span style="color: #777777;">)</span><span style="color: #333333;"> </span><span style="color: #777777;">delete</span><span style="color: #333333;"> insider</span><span style="color: #777777;">;</span></div><div><span style="color: #333333;"> </span><span style="color: #777777;">}</span></div><div><span style="color: #777777;">};</span></div></div><p> 확실히, 이 코드는 별 특이사항이 없어 보입니다. Insider는 쓰일 때도 있고 안 쓰일때도 있어서 필요한 경우에만 heap에서 생성하도록 해 두었습니다. anti_memory_leak이 삭제될 경우 Insider 객체가 존재하면 같이 지우도록 해서 메모리 누수 방지도 해 두었습니다. 우리는 std::optional이 없어도 아무런 문제가 없음을 증명했습니다.</p><p>...... 과연 그럴까요?</p><p>다음의 코드를 봅시다.</p><div style="background-color: whitesmoke; color: #333333; font-family: Consolas, 'Courier New', monospace; font-size: 14px; font-weight: normal; line-height: 19px; white-space: pre;"><div><span style="color: #7a3e9d;">void</span><span style="color: #333333;"> </span><span style="color: #aa3731; font-weight: bold;">doSomethingGreat</span><span style="color: #777777;">()</span></div><div><span style="color: #777777;">{</span></div><div><span style="color: #333333;"> anti_memory_leak object1</span><span style="color: #777777;">;</span></div><div><span style="color: #333333;"> </span><span style="color: #7a3e9d;">object1</span><span style="color: #777777;">.</span><span style="color: #7a3e9d;">insider</span><span style="color: #777777;">=new</span><span style="color: #333333;"> </span><span style="color: #aa3731; font-weight: bold;">Insider</span><span style="color: #777777;">();</span></div><br /><div><span style="color: #333333;"> </span><span style="color: #7a3e9d; font-weight: bold;">std</span><span style="color: #777777;">::</span><span style="color: #333333;">vector</span><span style="color: #777777;"><</span><span style="color: #333333;">anti_memory_leak</span><span style="color: #777777;">></span><span style="color: #333333;"> vector1</span><span style="color: #777777;">;</span></div><div><span style="color: #333333;"> </span><span style="color: #7a3e9d;">vector1</span><span style="color: #777777;">.</span><span style="color: #aa3731; font-weight: bold;">push_back</span><span style="color: #777777;">(</span><span style="color: #7a3e9d; font-weight: bold;">std</span><span style="color: #777777;">::</span><span style="color: #aa3731; font-weight: bold;">move</span><span style="color: #777777;">(</span><span style="color: #333333;">object1</span><span style="color: #777777;">));</span></div><div><span style="color: #333333;"> </span><span style="color: #7a3e9d;">vector1</span><span style="color: #777777;">.</span><span style="color: #aa3731; font-weight: bold;">back</span><span style="color: #777777;">().</span><span style="color: #7a3e9d;">insider</span><span style="color: #777777;">-></span><span style="color: #7a3e9d;">value1</span><span style="color: #777777;">=</span><span style="color: #9c5d27;">20</span><span style="color: #777777;">;</span><span style="color: #aaaaaa; font-style: italic;"> // CRASH!</span></div><div><span style="color: #777777;">}</span></div></div> <p>이 코드는 맨 마지막줄에서 프로그램이 터지는 결과를 가져옵니다. 왜 그럴까요? 이유는 바로 윗줄에 있습니다. vector1.push_back()을 호출할 경우, std::move()를 쓴다고 하더라도 object1은 파괴 후 재생성됩니다. 이 때 anti_memory_leak의 파괴자가 호출되고, 파괴자는 insider를 heap에서 제거합니다. 뒤집어서 말하면, vector1.back().insider는 dangling pointer가 되어버리는 셈이죠. 아울러 유감스럽지만, push_back()이 아니라 emplace_back()을 사용하더라도 동작은 마찬가지입니다. 어쨌든 터지는 것은 매한가지 되겠습니다.</p><p>바로 이 때가 std::optional이 출동할 차례입니다. std::optional을 사용하여 객체를 선언하면 일단 메모리는 사용되지 않은 채로 초기화되고, 추가 메모리는 실제로 해당 객체를 선언할 때 사용을 시작합니다. 물론 객체 생성에 따른 overhead가 좀 더 있긴 합니다만, 사실 그건 유사한(?) 역할을 수행하는 std::shared_ptr 같은 클래스도 마찬가지지요. raw pointer가 있지만 해당 heap 영역을 좀 더 안전하게 사용하기 위해서 운영의 일부를 자동화하여 개발자가 자칫 놓칠 수 있는 메모리 관리상의 문제점들(메모리 누수와 dangling pointer 모두)을 쉽게 해결하고자 하는 것입니다.</p><p>위의 코드를 std::optional을 사용하여 다시 작성한다면 이렇게 작성할 수 있겠습니다.</p><div style="background-color: whitesmoke; color: #333333; font-family: Consolas, 'Courier New', monospace; font-size: 14px; font-weight: normal; line-height: 19px; white-space: pre;"><div><span style="color: #7a3e9d;">struct</span><span style="color: #333333;"> </span><span style="color: #7a3e9d; font-weight: bold;">Insider</span><span style="color: #333333;"> </span><span style="color: #777777;">{</span></div><div><span style="color: #333333;"> </span><span style="color: #7a3e9d;">int</span><span style="color: #333333;"> value1</span><span style="color: #777777;">;</span></div><div><span style="color: #aaaaaa; font-style: italic;"> /* whatever great data structure */</span></div><div><span style="color: #777777;">};</span></div><br /><div><span style="color: #7a3e9d;">struct</span><span style="color: #333333;"> </span><span style="color: #7a3e9d; font-weight: bold;">anti_memory_leak</span><span style="color: #333333;"> </span><span style="color: #777777;">{</span></div><div><span style="color: #333333;"> </span><span style="color: #7a3e9d; font-weight: bold;">std</span><span style="color: #777777;">::</span><span style="color: #333333;">optional</span><span style="color: #777777;"><</span><span style="color: #333333;">Insider</span><span style="color: #777777;">></span><span style="color: #333333;"> insider</span><span style="color: #777777;">;</span></div><div><span style="color: #333333;"> </span><span style="color: #aa3731; font-weight: bold;">~anti_memory_leak</span><span style="color: #777777;">()</span><span style="color: #333333;"> </span><span style="color: #777777;">{</span></div><div><span style="color: #333333;"> </span><span style="color: #4b69c6;">if</span><span style="color: #777777;">(</span><span style="color: #333333;">insider</span><span style="color: #777777;">)</span><span style="color: #333333;"> </span><span style="color: #777777;">delete</span><span style="color: #333333;"> insider</span><span style="color: #777777;">;</span></div><div><span style="color: #333333;"> </span><span style="color: #777777;">}</span></div><div><span style="color: #777777;">};</span></div><br /><div><span style="color: #7a3e9d;">void</span><span style="color: #333333;"> </span><span style="color: #aa3731; font-weight: bold;">doSomethingGreat</span><span style="color: #777777;">()</span></div><div><span style="color: #777777;">{</span></div><div><span style="color: #333333;"> anti_memory_leak object1</span><span style="color: #777777;">;</span></div><div><span style="color: #333333;"> </span><span style="color: #7a3e9d;">object1</span><span style="color: #777777;">.</span><span style="color: #7a3e9d;">insider</span><span style="color: #777777;">=</span><span style="color: #aa3731; font-weight: bold;">Insider</span><span style="color: #777777;">();</span></div><br /><div><span style="color: #333333;"> </span><span style="color: #7a3e9d; font-weight: bold;">std</span><span style="color: #777777;">::</span><span style="color: #333333;">vector</span><span style="color: #777777;"><</span><span style="color: #333333;">anti_memory_leak</span><span style="color: #777777;">></span><span style="color: #333333;"> vector1</span><span style="color: #777777;">;</span></div><div><span style="color: #333333;"> </span><span style="color: #7a3e9d;">vector1</span><span style="color: #777777;">.</span><span style="color: #aa3731; font-weight: bold;">push_back</span><span style="color: #777777;">(</span><span style="color: #7a3e9d; font-weight: bold;">std</span><span style="color: #777777;">::</span><span style="color: #aa3731; font-weight: bold;">move</span><span style="color: #777777;">(</span><span style="color: #333333;">object1</span><span style="color: #777777;">));</span></div><div><span style="color: #333333;"> </span><span style="color: #7a3e9d;">vector1</span><span style="color: #777777;">.</span><span style="color: #aa3731; font-weight: bold;">back</span><span style="color: #777777;">().</span><span style="color: #7a3e9d;">insider</span><span style="color: #777777;">.</span><span style="color: #7a3e9d;">value1</span><span style="color: #777777;">=</span><span style="color: #9c5d27;">20</span><span style="color: #777777;">;</span><span style="color: #aaaaaa; font-style: italic;"> // OK</span></div><div><span style="color: #777777;">}</span></div></div><p></p><p>사실 객체를 다루는 코드의 흐름이 단순하다면 크게 문제가 없겠지만, 멀티스레딩이라던가, 아니면 코드가 어떤 식으로든 복잡해지게 되면 분명히 나도 모르게 메모리를 해제하게 되거나, 내지는 해제해야 할 때 못하는 경우가 생겨나게 됩니다. std::optional은 이런 경우에 대비한 일종의 보험이라 하겠습니다. 보험료를 내는 사람들은 (뭐 좀 아깝긴 하지만) 그래도 만일의 경우에 대비해서 보험료를 내는 거잖아요? std::optional도 마찬가지라고 봅니다.<br /></p><p></p>T.Robinhttp://www.blogger.com/profile/12834494724018712162noreply@blogger.com0tag:blogger.com,1999:blog-3594288631091363665.post-29567986412056283692022-08-09T09:25:00.003+09:002022-08-20T20:57:50.330+09:00Handshake One: SYN, DoS 공격지점, 또는 접속하는 IP들의 목록을 알아보자<p>코로나19가 한창 여름 내내 감염 신기록을 갈아치우던 뜨거운 여름이었습니다. 저는 한 작은 온라인 커뮤니티의 관리자들이 DoS(Denial of Service, 서비스 거부 공격)에 대항하는걸 지켜보고 있었습니다. 그들은 열정적이었지만, 작업은 그다지 효율적이지 못했습니다. 그들이 쓸 수 있는 도구라고는 웹서버 로그와 그들의 오래된 친구, <i>iptables </i>뿐이었지요.</p><p>사실 보안에 투자할만한 여력이 없는 환경에서는, 이 돌대가리같은(......), 하지만 꽤나 효율적인 <b>공격</b>에 대항해 의존할만한 도구라고는 <i>iptables</i>나 <i>nftables</i>같은 것들 밖에는 없습니다. 하지만 일단 공격을 받고 나면, 사람들은 머리가 새하얘지죠. 로그는 너무 길고 복잡하고, 공격자 IP를 사용자 IP로부터 '발라내는' 작업은 꽤나 어렵습니다.</p><p>해서, 이런 분들을 도와드리고자 하는 순수한 목적에서 Handshake One이라고 이름붙인 작은 프로그램을 하나 만들었습니다. 이 프로그램을 이용하면 DoS 공격의 원천 IP를 빠르게 확인하거나, 아니면 최소한 현재 서비스에 어떤 IP들이 접속하고 있는지를 파악할 수 있습니다. 이 프로그램은 최근 60초동안 클라이언트 IP에서 발생하는 SYN 패킷을 수집하여 아래와 같은 보고서를 만들어냅니다:</p><div><div></div><blockquote><div><i>Handshake One TCP SYN counter report</i></div><div><i>(C)Copyright 2022 Robert Teminian.</i></div><div><i>This application is provided free of charge, and provided AS IS: though the developer hopes that this would help the user in any way, the software does NOT guarantee anything at all.</i></div><div><br /></div><i>Stop by the developer's blog and leave a comment!</i><i> Visit http://codenested.blogspot.com</i><div><i><br /></i></div><div><i>====================</i></div><div><i>At 1660000053</i></div><div><i>IP<span style="white-space: pre;"> </span>Hits</i></div><div><i>192.168.1.26<span style="white-space: pre;"> </span>14</i></div><div><i>Total<span style="white-space: pre;"> </span>14</i></div><div><i><br /></i></div><div><i>====================</i></div><div><i>At 1660000054</i></div><div><i>IP<span style="white-space: pre;"> </span>Hits</i></div><div><i>192.168.1.26<span style="white-space: pre;"> </span>11</i></div><div><i>Total<span style="white-space: pre;"> </span>11</i></div><div><i><br /></i></div><div><i>====================</i></div><div><i>At 1660000055</i></div><div><i>IP<span style="white-space: pre;"> </span>Hits</i></div><div><i>192.168.1.26<span style="white-space: pre;"> </span>1</i></div><div><i>Total<span style="white-space: pre;"> </span>1</i></div></blockquote><p>현재 세 가지 운영체제에 대한 실행파일이 제공되고 있습니다만, 만일 다른 운영체제를 위한 실행파일이 필요하신 경우 제게 말씀해주세요. 아래 링크를 클릭하면 파일을 다운로드받을 수 있습니다.</p><p></p><ul><li><a href="https://drive.google.com/open?id=12SK-eMwt-hyzf5Ujyyh2dQBMOPXy3F8D&authuser=teminian%40gmail.com&usp=drive_fs" target="_blank">Windows</a></li><li><a href="https://drive.google.com/open?id=12RwxESzg9d_3nfG9RuhKolf1f8t6b1gT&authuser=teminian%40gmail.com&usp=drive_fs" target="_blank">Ubuntu 20.04 LTS</a></li><li><a href="https://drive.google.com/open?id=12Rfg01WnD4VoiyFNmyiaLen-cRIt6A-2&authuser=teminian%40gmail.com&usp=drive_fs" target="_blank">CentOS 7</a></li></ul><div>위 파일은 모두 압축파일입니다. 원하시는 곳에 압축을 풀어서 사용해주시면 됩니다.</div><div><br /></div><div>사용법은 아래와 같습니다</div><div><ul><li><b>설치: Windows</b></li><ul><li>원하는 곳에 ZIP 파일의 압축을 풉니다</li><li>Windows 환경에서, Handshake One은 패킷 캡쳐를 위해 <b><a href="https://npcap.com/">npcap(https://npcap.com/)</a> </b>을 사용하고 있습니다. 라이브러리를 직접 설치하시거나, <a href="https://www.wireshark.org/">Wireshark(https://www.wireshark.org/)</a>를 설치하면 분석프로그램과 npcap을 같이 설치합니다</li><li>npcap을 설치한 뒤, <b><i style="font-weight: 400;">C:\Windows\Systems32\Npcap</i><span style="font-weight: 400;">에서 </span>Packet.dll</b>과 <b>wpcap.dll</b>을 프로그램 디렉토리에 복사합니다</li></ul><li><b>설치: Linux</b></li><ul><li>원하는 곳에 TGZ 파일의 압축을 풉니다</li><li>Linux 환경에서, Handshake One은 <b style="font-style: italic;">libpcap</b>을 이용합니다. 일반적으로 <i>libpcap</i>은 tcpdump를 설치하면서 함께 설치되는 경우가 대부분입니다만, 만일 설치되지 않았다면 해당 리눅스 배포본의 패키지 관리자(apt, yum, ......)의 설명을 참고하여 설치해 주세요</li></ul><li><b>환경 설정</b></li><ul><li>Handshake One의 환경을 설정할 수 있는 유일한 방법은 <i>HandshakeOne.json </i>파일을 편집하는 것입니다. 현재 지원되는 설정은 아래와 같습니다</li><ul><li><b>resultpath: </b>보고서 파일을 저장할 디렉토리를 설정합니다. Handshake One은 해당 디렉토리에 <b>"HandshakeOneReport.txt" </b>파일을 생성하고, 매 30초마다 갱신(덮어쓰기)을 수행합니다</li><ul><li>리눅스를 사용하시는 경우, RAM disk 파티션(예: /tmp)으로 설정하여 시스템의 I/O 부하를 줄일 것을 권장합니다</li></ul><li><b>sniffer: </b>패킷을 수집할 디바이스 이름입니다. Linux에서는 일반적으로 <i>ip link</i>나 <i>ifconfig </i>같은 명령어에 나오는 이름을 사용하면 됩니다만, Windows에서는 일반적으로 보이는 이름이 아닌 내부 디바이스 이름(예: <i>\Device\NPF_{12345678-9ABC-DEF0-1234-567890ABCDEF}</i>)을 사용하여야 해서 설정이 조금 어려울 수 있습니다. 만일 디바이스 이름과 그에 해당하는 설명(예: <i>Realtek PCIe GbE Family Controller</i>)을 함께 보시려면, Handshake One을 "show" 파라미터와 함께 실행해 주세요(예: <b style="font-style: italic;">HandshakeOne show</b>)</li><li><b>reportsizelimit: </b>보고서 내용을 갱신할 때, 보고서 파일의 크기를 지정된 크기로 제한합니다(바이트 단위). 만일 지정된 크기를 넘어서게 될 경우, 현재 작성되는 timestamp까지의 데이터만을 작성합니다. 이를테면, 10:00:00(09:00:00~09:00:59의 데이터를 포함)에 작성되는 데이터가 1.5MB였다면 갱신시 모든 내용이 저장되고, 10:01:00(10:00:00~10:00:59의 데이터를 포함)에 작성되는 과거 1분간의 데이터가 총 2.5MB이고 10:00:47초 데이터를 쓰던 도중 보고서의 크기가 2MB를 넘었다면 10:00:47까지의 내용만을 작성 완료하고 보고서 쓰기작업을 종료한 후 10:00:02에 보고서를 갱신하게 됩니다.</li></ul></ul><li><b>프로그램 실행</b></li><ul><li>Windows: <b>HandshakeOne.exe</b>를 실행해 주시면 됩니다</li><ul><li>만일 <i>Handshake One</i>을 Windows Service로 실행하시고 싶으시다면, <a href="http://nssm.cc/">nssm(http://nssm.cc/)</a>을 이용하실 수 있을 것으로 생각됩니다. 직접 써 본 적은 없지만 평가가 꽤 좋은 프로그램이었습니다</li></ul><li>Linux: 두 가지 방법이 있습니다</li><ul><li><b>HandshakeOne</b>을 직접 실행합니다. 참고: libpcap이 <b>root 권한을 필요</b>로 하므로, HandshakeOne은 sudo나 su 등을 이용하여 root 계정으로 실행되어야 합니다</li><li>Handshake One을 systemd service로 설정할 수 있습니다. <i>HandshakeOne.service </i>파일을 필요에 따라 편집하시고(최소한 <i>ExecStart </i>와 <i>WorkingDirectory</i>가 현재 프로그램의 위치를 반영하게끔 조정되어야 합니다), 아래의 명령을 참조하여 프로그램을 systemd service로 등록해 주세요</li><ul><li><i>sudo cp systemd.service /etc/systemd/system</i></li><li><i>sudo systemctl enable HandshakeOne</i></li><li><i>sudo systemctl start HandshakeOne</i></li></ul></ul></ul></ul><div>프로그램에 대한 설명은 여기까집니다! 유용하게 쓰실 수 있으셨으면 좋겠습니다. 제언, 의견, 또는 질문이 있으시면 아래에 코멘트를 남겨주세요. 감사합니다.</div></div></div>T.Robinhttp://www.blogger.com/profile/12834494724018712162noreply@blogger.com0tag:blogger.com,1999:blog-3594288631091363665.post-29976895329349071172022-08-09T09:00:00.005+09:002022-08-20T20:58:15.706+09:00Handshake One: Know Your SYN, source of DoS attack, or client IP profiles at leastIt was a hot summer in the middle of COVID-19 renewing its infection top day by day. I saw managers of a small online community defend the server against a Dos(Denial of Service) attack. They were enthusiastic, but I found out the work is quite inefficient. The only thing they could rely on was some web server logs and our good old friend <i>iptables</i>.<div><br /></div><div>Well, being too small to invest in some security, we know the only viable option we have is either <i>iptables </i>or <i>nftables </i>against those dull and stupid, yet quite efficient <b>attacks</b>. However, when we encounter the attacks, usually we're puzzled and stuck; log is too long and complicated to read so that we have difficulties on distinguishing attacker IPs against user IPs.</div><div><br /></div><div>So, I developed a small utility named Handshake One to help server engineers who want to find out sources of DoS attacks as early as possible, or, learn IP profiles for your service at least. This small utility collects SYN packets from clients IPs for past 60 seconds to generate reports as you see below:</div><div><div></div><blockquote><div><div><i>Handshake One TCP SYN counter report</i></div><div><i>(C)Copyright 2022 Robert Teminian.</i></div><div><i>This application is provided free of charge, and provided AS IS: though the developer hopes that this would help the user in any way, the software does NOT guarantee anything at all.</i></div><div><i><br /></i></div><div><i>Stop by the developer's blog and leave a comment! Visit http://codenested.blogspot.com</i></div></div><div><i><br /></i></div><div><i>====================</i></div><div><i>At 1660000053</i></div><div><i>IP<span style="white-space: pre;"> </span>Hits</i></div><div><i>192.168.1.26<span style="white-space: pre;"> </span>14</i></div><div><i>Total<span style="white-space: pre;"> </span>14</i></div><div><i><br /></i></div><div><i>====================</i></div><div><i>At 1660000054</i></div><div><i>IP<span style="white-space: pre;"> </span>Hits</i></div><div><i>192.168.1.26<span style="white-space: pre;"> </span>11</i></div><div><i>Total<span style="white-space: pre;"> </span>11</i></div><div><i><br /></i></div><div><i>====================</i></div><div><i>At 1660000055</i></div><div><i>IP<span style="white-space: pre;"> </span>Hits</i></div><div><i>192.168.1.26<span style="white-space: pre;"> </span>1</i></div><div><i>Total<span style="white-space: pre;"> </span>1</i></div></blockquote><p>Currently binaries for three operating systems are supported, but you can contact me to build binaries for other OSes. Click the link below to download the executable binary for each:</p><p></p><ul style="text-align: left;"><li><a href="https://drive.google.com/open?id=12SK-eMwt-hyzf5Ujyyh2dQBMOPXy3F8D&authuser=teminian%40gmail.com&usp=drive_fs" target="_blank">Windows</a></li><li><a href="https://drive.google.com/open?id=12RwxESzg9d_3nfG9RuhKolf1f8t6b1gT&authuser=teminian%40gmail.com&usp=drive_fs" target="_blank">Ubuntu 20.04 LTS</a></li><li><a href="https://drive.google.com/open?id=12Rfg01WnD4VoiyFNmyiaLen-cRIt6A-2&authuser=teminian%40gmail.com&usp=drive_fs" target="_blank">CentOS 7</a></li></ul><div>They're just compressed files. You can just decompress the file in any directory you want to use.</div><div><br /></div><div>The usage is as follows:</div><div><ul style="text-align: left;"><li>Install: Windows</li><ul><li>Decompress the ZIP file in any location you want</li><li>On Windows, Handshake One depends on <b><a href="https://npcap.com/">npcap(https://npcap.com/)</a> </b>for capturing packets. Please install the separate binary, or install <a href="https://www.wireshark.org/">Wireshark(https://www.wireshark.org/)</a> which installs both packet analysis tool and <i>npcap</i>.</li><li>After installing npcap, copy <b>Packet.dll </b>and <b>wpcap.dll </b>from <i>C:\Windows\Systems32\Npcap</i> to the executable directory</li></ul><li>Install: Linux</li><ul><li>Decompress the TGZ file in any location you want</li><li>On Linux, Handshake One depends on <b><i>libpcap</i>.</b> Usually it is installed alongside with tcpdump but if it's not, consult your Linux distribution's package manager(apt, yum, ......) to install the package</li></ul><li>Configuration</li><ul><li>The only way to configure Handshake One is via its configuration file, <i>HandshakeOne.json</i>. Currently there are only two keys</li><ul><li><b>resultpath: </b>the directory to save report file. Handshake One will automatically generate and update(overwrite) file named <b>"HandshakeOneReport.txt"</b>on that directory every 30 seconds.</li><ul><li>In Linux, I recommend to set the directory to some RAM disk(e.g. /tmp) so that the </li></ul><li><b>sniffer: </b>device name to capture the packets. In Linux its the device name shown on commands like <i>ip link </i>or <i>ifconfig</i>, but in Windows it's a bit tricky since the name <i>npcap </i>refers to is NOT the "human readable name" for the interface, but internal device name like <i>\Device\NPF_{12345678-9ABC-DEF0-1234-567890ABCDEF}</i>. To see the device names and corresponding human readable descriptions(e.g. <i>Realtek PCIe GbE Family Controller</i>), run Handshake One with "show" parameter, e.g. <i><b>HandshakeOne show</b></i></li><li><b>reportsizelimit: </b>when updating(actually overwriting) the report file, limit the size of report file in bytes. If the file size is bigger than designated size, the application will write until the last data for the timestamp currently being written and finish. For example, if the report size at 10:00:00(including data for 09:00:00~09:00:59) was 1.5MB the report will contain everything, yet at 10:01:00(including data for 10:00:00~10:00:59)the actual size is 2.5MB and it hits the set limit (i.e. 2MB) around writing data for 10:00:47, it'll complete write data up to 10:00:47, finish writing, and refresh the report at 10:00:02</li></ul></ul><li>Run the application</li><ul><li>In Windows, just run <b>HandshakeOne.exe</b></li><ul><li>If you're interested in using <i>Handshake One</i> as Windows Service, I think you can use <a href="http://nssm.cc/">nssm(http://nssm.cc/)</a>. Though I have no experience using it, I find very positive reviews in many places.</li></ul><li>In Linux, you have two choices</li><ul><li>Run <b>HandshakeOne </b>directly. Caution: since <i>libpcap </i>needs root privilege, <b>HandshakeOne </b>must be run with commands like <i>sudo </i>or <i>su</i>.</li><li>You can register Handshake One as systemd service. Edit <i>HandshakeOne.service </i>as needed(at least <i>ExecStart </i>and <i>WorkingDirectory </i>must be changed to match exact path for the binary) and refer to following command to register the binary as systemd service</li><ul><li></li><li><i>sudo cp systemd.service /etc/systemd/system</i></li><li><i>sudo systemctl enable HandshakeOne</i></li><li><i>sudo systemctl start HandshakeOne</i></li></ul></ul></ul></ul><div>So...... That's all, folks! I hope you enjoy the application. If you have any comments, opinions, or questions, please leave a comment.</div></div><p></p><div></div></div><div style="text-align: left;"><div></div></div>T.Robinhttp://www.blogger.com/profile/12834494724018712162noreply@blogger.com0tag:blogger.com,1999:blog-3594288631091363665.post-58052939674607023162022-01-16T18:14:00.001+09:002022-01-16T18:14:09.556+09:00Flutter/DART: First Impression<p>All of sudden, I had a chance to briefly review Flutter. Covering all desktop, mobile, web in single programming language, and for web, compiled to Javascript instead of WebAssembly which takes a forever-log real time compilation time - isn't it lovely and beautiful? (Oh, Qt has WebAssembly port, but it takes forever to compile the code, which may be because of that big fat core library).<br /></p><p>Someone said that the biggest weakness of Flutter is DART, but I don't agree. The structure of DART is quite interesting, and considering the accessibility from other programming languages it is both understandable(I'm looking at you, Rust). The only thing I dislike is that darn garbage collector, but I think I should admit if DART doesn't want to manually control pointers in any way. Or, if they force RAII in whatever way everyone will be angry. :P<br /></p><p>I think, the biggest drawback of Flutter is state management. For other frameworks you don't have to distinguish stateful against stateless widgets, but in Flutter you must do it verbosely. What's more, if build() is called it automatically finds only changes and applies only found changes. For someone the word "automatic" will say "yeah so it's Google!". But as a C++ developer myself, I throw a question like "why do you compare everything when you can recognize changed area?" For me, it looks like that the Flutter developers designed to change everything, yet distinguishing stateful widgets against stateless ones to minimize overhead to GC(the garbage collector). I think, for me, the property binding of Qt is better than this.<br /></p><p>I'm afraid the approach of Google in this way would drop the readability of the code. If you read <a href="https://docs.flutter.dev/development/ui/widgets-intro" target="_blank">Introduction to widgets</a>, one of the official documentation for Flutter, you see <i>"the separation of responsibility allows greater complexity to be
encapsulated in the individual widgets, while maintaining simplicity in
the parent"</i>. For me, it sounds like a warning to prepare for the hellish burning(......) hell, when you have to change the stateless widget. What if you have to separate some part of stateless to stateful side? And there's no absolute way to prevent you from doing it. Most of the people select Python or Javascript because they provide very flexible development environment for requirements changed in real time, but I think, for Flutter that kind of flexibility would be a bit hard.<br /></p><p>I'm not sure about others' opinions, but for me DART is okay but I'm a bit against Flutter. It's like...... it forces specific development process and design concepts. Do I really have to do it this way? I'm not sure.<br /></p>T.Robinhttp://www.blogger.com/profile/12834494724018712162noreply@blogger.com0tag:blogger.com,1999:blog-3594288631091363665.post-69233621411321781982022-01-16T17:55:00.003+09:002022-01-16T18:14:46.652+09:00Flutter/DART를 훑어보다<p>어쩌다 보니 요즘 뜨고 있는 개발환경인 Flutter를 살펴보게 되었습니다. 단일 프로그래밍 언어에 단일 환경으로 desktop과 mobile과 web을 모두 커버할 수 있는데다가, 심지어 web의 경우 컴파일하다가 날 샐지도 모르는(......) WebAssembly 대신 Javascript로 직접 컴파일된다 하니, 이 어찌 신박하다 하지 않을 수 있겠습니까(Qt도 WebAssembly 포트가 있습니다만, Qt 자체가 덩치가 크기 때문인지 프로그램을 로딩하다가 날밤을 샐 정도로 답답합니다. -_-). <br /></p><p>혹자는 Flutter의 최대 단점은 DART다(......)라는 희대의 명언(?)을 내놓기도 했습니다만, 전 이 부분에 대해서는 그다지 동의하지 않습니다. DART의 구조는 매우 흥미로우며, 타 언어 사용자들로부터의 접근성이나 생산성 향상이라는 측면에서 보면 DART의 구조는 최소한 납득할만하다고 생각합니다(Rust 개발자 여러분, 보고 계십니까). 단, 딱 한 가지 제가 싫어하는 부분이라면 역시 그놈의 쓰레기 수집기(garbage collector)인데, 이 부분은 DART가 메모리 수동 제어를 수행하지 않는다는 것을 고려한다면 어쩔 수 없는 한계라고 봅니다. 그렇다고 RAII라던가 하는걸 문법상으로 강제했다간 다들 들고 일어날거고...... -_-</p><p>제가 생각하기에 Flutter의 최대 단점은 상태 관리라고 봅니다. 다른 프레임워크들의 경우 stateful과 stateless widget을 따로 구분할 필요가 없는 반면, Flutter는 이를 명시적으로 구분하도록 되어 있습니다. 그리고 build()를 호출할 경우 현재 상태와 이전 상태를 비교해서 변경된 부분을 자동 인식하여 화면을 빠르게 재구성한다고 되어 있습니다. 변경된 부분을 자동으로 인식한다는 이러한 접근 방법은 한편으로는 '오오 구글'이라는 탄성을 가져오게 할 수도 있지만, 지나가던 C++ 개발자의 눈에는 '그냥 변경된 것만 고치지 왜 쓸데없이 모든 부분을 다 비교하지?'라는 생각이 들게 만들더군요. 무조건 전체를 다 고치도록 해놓고, 그 와중에 GC의 오버헤드를 최대한 줄이기 위해 수동으로 stateful과 stateless widget을 나눈게 전부가 아닌가 싶은 생각이 듭니다. 이 부분은 오히려 Qt의 property binding 구조가 훨씬 더 나은 것 같아요.</p><p>특히나 구글의 이러한 접근방식은 프로그램이 복잡해질 경우의 가독성을 크게 떨어뜨릴 것 같습니다. Flutter의 공식 문서 중 하나인 <a href="https://docs.flutter.dev/development/ui/widgets-intro" target="_blank">Introduction to widgets</a>를 보면 <i>"the separation of responsibility allows greater complexity to be encapsulated in the individual widgets, while maintaining simplicity in the parent"</i>라는 문장이 나오는데, 제게는 저 문장이 나중에 저 stateless widget을 고쳐야 할 경우가 생긴다면 불지옥이 펼쳐질 수도 있다는 소리처럼 들립니다. 이를테면, stateless widget의 일부는 stateless로 남겨두는 대신 일부는 stateful로 가야 하는 상황이 발생한다면 어떻게 될까요? - 그리고 솔직히 그러지 말라는 법도 없습니다. 사람들이 Python이나 Javascript로 가는 이유 중 하나가 실시간으로 발생하는 요구사항에 대한 유연한 대처가 가능하기 때문인데, Flutter는 구조상 이러한 유연성을 가지기가 어렵지 않을까 하는 생각이 듭니다.</p><p>다른 분들은 어떻게 생각하실지 모르겠습니다만, 전 DART는 괜찮은데 Flutter는 조금 거부감이 드는군요. 뭐랄까...... 특정한 개발 프로세스와 디자인 컨셉을 무조건적으로 강제하는 듯한 느낌입니다. 굳이 이렇게까지 해야 할까 하는 생각이 드네요.<br /></p>T.Robinhttp://www.blogger.com/profile/12834494724018712162noreply@blogger.com0tag:blogger.com,1999:blog-3594288631091363665.post-64951672110392161712021-10-28T17:43:00.004+09:002021-10-28T17:43:49.912+09:00Performance Comparison in multithread environment: robin_hood::unordered_map vs. tbb::concurrent_hash_map<p> </p><h1 style="text-align: left;">robin_hood::unordered_map <br /></h1><p><a href="https://github.com/martinus/robin-hood-hashing" target="_blank">robin_hood::unordered_map</a> is said to be among the fastest in replacements for std::unordered_map. Personally I found that both in VS2019 and Ubuntu 20.04 the map reduces about half of the time spent against std::unordered_map. However, considering its birth and origin, in multithreaded environment we need to serialize the access to the hashmap using something like mutex. Or, the angry Lord of Segmentation Fault will fire at you(......).<br /></p><h1 style="text-align: left;">tbb::concurrent_hash_map</h1><p>Well, if you're in multithreading environment, you can't avoid this: Intel's proud production, tbb::concurrent_hash_map from <a href="https://github.com/oneapi-src/oneTBB">oneTBB (Threading Building Block)</a>. In multithreaded environment container classes from TBB is quite decent. Certainly, I'm sure Intel devoted the best efforts to TBB, as its performance is about similar to std::unordered_map, if I compare time spent for the operation.<br /></p><h1 style="text-align: left;">Fastest in Single Thread vs. Optimized for Multithread<br /></h1><p>One shows the best speed in single thread environment, and the other is optimized for multithreading. Then one thing: what if we apply something like std::shared_mutex to robin_hood::unordered_map? Wouldn't it be faster if there are more read than write?<br /></p><p>OK OK. I know it's nerdy, but you know, ....... a lot of programmers are nerds. Right? So I applied that to my current development. The program's read-write ratio is about 3:1, and I applied std::shared_mutex.</p><p>The winner? <b>TIE(oops). </b>The time spent for both libraries was about the same or similar.<br /></p><h1 style="text-align: left;">What's Your Choice?</h1><div style="text-align: left;"><div style="text-align: left;"><p style="text-align: left;">Anyway it depends(tm), I concluded tbb:concurrent_hash_map is better. I think tbb::concurrent_has_map will show better performance if there are more requests, and you can avoid any mistakes on implementation with with its own syntax for forced data lock.</p><p style="text-align: left;">For some time being, I'd reside on tbb::concurrent_hash_map.<br /></p><h1 style="text-align: left;">One More Thing: QReadWriteLock vs. std::shared_mutex (C++17)<br /></h1></div><div style="text-align: left;">Oh yeah. Another nerdy test. For this I just had to swap lock/mutex only so I did it too. And unexpectedly, <span style="color: #38761d;"><b>std::shared_mutex won</b></span> this time. Though the Qt Company and the community did a lot of efforts to optimize QReadWriteLock(e.g. <a href="https://codereview.qt-project.org/c/qt/qtbase/+/140322/">https://codereview.qt-project.org/c/qt/qtbase/+/140322/</a> and <a href="https://woboq.com/blog/qreadwritelock-gets-faster-in-qt57.html">https://woboq.com/blog/qreadwritelock-gets-faster-in-qt57.html</a>), after some years there seems to be better(and standard) alternative.</div><div style="text-align: left;"> </div><div style="text-align: left;">Yet anyway, thanks for your hard work, Qt team!<br /></div></div>T.Robinhttp://www.blogger.com/profile/12834494724018712162noreply@blogger.com0tag:blogger.com,1999:blog-3594288631091363665.post-58824270262266721222021-10-28T17:24:00.003+09:002021-10-28T17:46:54.179+09:00멀티스레드 환경에서의 성능 비교: robin_hood::unordered_map vs. tbb::concurrent_hash_map<p></p><h1 style="text-align: left;">robin_hood::unordered_map <br /></h1><p><a href="https://github.com/martinus/robin-hood-hashing" target="_blank">robin_hood::unordered_map</a>은 std::unordered_map을 대체하는 hashmap 중 가장 속력이 빠른 라이브러리로 알려져 있습니다. 제 개인적인 테스트로는 VS2019 및 Ubuntu 20.04 환경 모두에서 std::unordered_map에 대비하여 수행시간이 약 절반 수준으로 줄어들더군요. 하지만 이것도 태생이 태생이라, 멀티스레드에서 사용하기 위해서는 mutex 등을 사용해서 접근을 제어해야 합니다. 안 그랬다간 바로 segmentation fault의 불호령이 떨어집니다(.....).</p><h1 style="text-align: left;">tbb::concurrent_hash_map<br /></h1><p>한편, 멀티스레드 환경에 편리하게 사용하기 위한 hashmap이면 역시 인텔의 자랑인 <a href="https://github.com/oneapi-src/oneTBB">oneTBB (Threading Building Block)</a>의 tbb:concurrent_hash_map을 빼놓을 수가 없습니다. 멀티스레딩 환경에서 접근제어를 신경쓰지 않고 사용하기에는 역시 TBB의 컨테이너 클래스들이 가장 무난하다 하지 않을 수 없겠습니다. 확실히 인텔이 TBB에 신경을 각별히 쓴게 보이는 것이, std::unordered_map과 성능이 거의 비슷합니다. 각 기능별 소요시간이 std::unordered_map과 거의 똑같더군요.</p><h1 style="text-align: left;">싱글스레드 최속 vs. 멀티스레드 최적<br /></h1><p>한쪽은 싱글스레드 환경 한정으로 속도면에서는 최강자이고, 다른 한 쪽은 멀티스레딩 환경에서 최적화된 성능을 보여줍니다. 그럼 문득 드는 생각 하나...... robin_hood::unordered_map에다가 std::shared_mutex같은걸 씌우면, 어쩌면 읽기가 쓰기보다 더 많은 환경에서는 oneTBB보다 더 빠를 수도 있지 않을까? </p><p>아니 뭐..... 뻘짓인건 알지만, 그래도 길고 짧은건 역시 대봐야 아는 거잖아요? 해서 제가 현재 개발중인 프로그램에 한번 적용해 봤습니다. 해당 프로그램은 읽기와 쓰기 비율이 약 3:1 정도로 추산되고, 접근제어에는 std::shared_mutex를 사용했습니다.</p><div style="text-align: left;">그리고 테스트 결과는...... <b>승자가 없습니다. </b>robin_hood::unordered_map과 tbb:concurrent_hash_map 모두 소요시간이 비슷하더군요.</div><div style="text-align: left;"><h1 style="text-align: left;">당신의 선택은?</h1><div style="text-align: left;"><p style="text-align: left;">프로그램의 구조에 따라 다르겠지만, 전 tbb::concurrent_hash_map이 더 낫겠다는 판단을 내렸습니다. 리소스에 대한 접근 요청이 많아질수록 tbb::concurrent_hash_map이 더 우세할 것 같기도 하고, 고유 문법을 통해 리소스 접근시 data lock을 강제하기 때문에 구현상의 실수가 더 줄어들겠다는 것이 저의 판단입니다.</p><p style="text-align: left;">최소한 당분간 멀티스레드 환경에서는 tbb:concurrent_hash_map을 쓰게 될 것 같네요.</p><h1 style="text-align: left;">부록: QReadWriteLock vs. std::shared_mutex (C++17)<br /></h1><div style="text-align: left;">길고 짧은건 역시 대봐야 합니다. 이왕 하는김에, 저 두 가지 사이에서도 뭔가 속도 차이가 있을까 싶은 생각이 들어서 저 두 가지도 실험해봤습니다. 그리고 여기선 <span style="color: #38761d;"><b>std::shared_mutex</b></span>가 승리했습니다.</div><div style="text-align: left;">Qt Company와 커뮤니티가 QReadWriteLock을 최적화하기 위해 갖은 노력을 다 하긴 했습니다만(예: <a href="https://codereview.qt-project.org/c/qt/qtbase/+/140322/">https://codereview.qt-project.org/c/qt/qtbase/+/140322/</a> and <a href="https://woboq.com/blog/qreadwritelock-gets-faster-in-qt57.html">https://woboq.com/blog/qreadwritelock-gets-faster-in-qt57.html</a>), 몇년 뒤에 더 좋은-그리고 표준화된- 대안이 나온 것 같네요.</div><div style="text-align: left;"> </div>뭐 어찌됐건, 전 Qt의 모든 노력과 그 결과물을 존중합니다. 그리고 저 또한 그 위에 얹혀(?) 살고 있기도 하고요(웃음).<br /></div></div>T.Robinhttp://www.blogger.com/profile/12834494724018712162noreply@blogger.com0tag:blogger.com,1999:blog-3594288631091363665.post-54687085047164402522021-07-27T11:54:00.003+09:002021-07-27T11:54:23.329+09:00If clang backend in Qt Creator stops working in Ubuntu 20.04 LTS<p>Ubutntu 20.04 LTS has Qt Creator 4.11.0 in APT, and if you install this and open a source file you'll encounter continuous error message of <i>The clangbackend process has finished unexpectedly and was restarted</i>. If you open shell you'll see some additional messages like <i>Error in ClangCodeModelServer::documentsClosed: Document '/path/to/sourcefile.cpp' does not exists!</i></p><p>For more than one year, to avoid the message, I turned off clang code model plugin from the Creator, but today I found a simple solution - downgrade clangd from 10.0(stock default install) to 8.0. What a relief!<br /></p><p>Now I can enjoy the powerful help from libclang. :)<br /></p>T.Robinhttp://www.blogger.com/profile/12834494724018712162noreply@blogger.com0tag:blogger.com,1999:blog-3594288631091363665.post-40347562349742524352021-07-27T11:49:00.002+09:002021-07-27T11:56:00.078+09:00Ubuntu 20.04 LTS에서 Qt Creator의 clang backend가 동작하지 않는 경우<p>Ubuntu 20.04 LTS에서 APT를 사용해서Qt Creator(4.11.0)를 설치하고 소스파일을 열면 clang backend가 <i>The clangbackend process has finished unexpectedly and was restarted</i>. 라는 오류를 띄우면서 인해 계속 재시작을 하는 기현상을 경험할 수 있습니다. 만일 shell에서 Qt Creator를 실행하면 <i>Error in ClangCodeModelServer::documentsClosed: Document '/path/to/sourcefile.cpp' does not exists! </i>같은 메시지도 함께 나오더군요.<br /><i></i></p><p>그동안은 저 에러를 회피하려고 clang code model을 꺼놓고 썼는데, 오늘 간만에 맘잡고 이 문제를 붙잡아본 결과 clangd를 10.0에서 8.0으로 내리면 문제가 해결되더군요.</p><p>이걸 몰라서 1년을 넘게 clang code model 없이 삽질께나 했습니다(......).</p>T.Robinhttp://www.blogger.com/profile/12834494724018712162noreply@blogger.com0tag:blogger.com,1999:blog-3594288631091363665.post-24460894715846683042021-05-02T01:13:00.001+09:002021-05-02T01:13:06.634+09:00When Python Language Server is too slow on Qt Creator(Windows)<p>Nowadays I'm teaching Python to my son. After teaching stuff I found out that now it's the very timing to teach how to use GUI. And what? For me tkinter is too "weak" and I decided to use Qt, which is one of my favorite "weapon."</p><p>So I tried Python on Qt Creator. And guess what? Suddenly LPS became the "black sheep." Ctrl-Space doens't work, and according to messages from LPS it takes a few minutes to follow me up for my 3-second typing...... Frankly speaking I was fully disappointed. Does Qt Creator just ignore Python users, though Qt Company says that it officially supports Python?</p><p>However, even it was kind of...... strange. If all the users experience the same symptoms like me, then the forum would have already been highly flamed, but I couldn't find anything.</p><p>Well, and all of sudden, I encountered a solution.</p><p></p><ol style="text-align: left;"><li>Go to <i>Options </i>and select <i>Language Client</i></li><li>Select <i>Python Language Server</i></li><li>Change <i>Startup behavior </i>from <i>Requires an Open file</i>(default) to <i>Start Server per Project</i></li><li>PROFIT!!!</li></ol><p></p><p>I have no idea why, but now I'm satisfied with overall performance and result. With default configuration from Qt Creator practically I couldn't do anything so that I seriously considered Visual Stuio Code + Qt Designer, but now with Qt Creator on full throttle, I don't have to go workaround</p><p>Hope that this small tip helps those who want to develop in Python with Qt Creator.</p>T.Robinhttp://www.blogger.com/profile/12834494724018712162noreply@blogger.com0tag:blogger.com,1999:blog-3594288631091363665.post-66997960672295861812021-05-02T01:00:00.003+09:002021-05-02T01:11:04.262+09:00Qt Creator(Windows)에서 Python Language Server가 너무 느릴때의 조치방법<p>그러니까...... 아들녀석에게 Python을 가르치다 보니 GUI를 가르칠 타이밍이 되었는데, tkinter는 (최소한 제 입장에서는) 뭔가 너무 많이 빈약해서 제 장기(?) 중 하나인 Qt를 가르치는게 더 낫겠다는 생각이 들더군요. 그래서 Qt Creator를 사용해서 Qt for Python을 가르치려고 했는데, LPS가 너무 느린겁니다. Ctrl-Space는 동작하지도 않고, LPS에서 뿌리는 메시지를 보면 제가 3초만에 친 타이핑을 쫓아가는데 몇 분 이상이 걸리고...... 하여간 대단히 실망스러웠습니다. Python을 공식적으로 지원한다고 해 놓고 정작 Qt Creator에서는 Python 사용자들을 거의 방치하는 건가 하는 생각까지 들더군요.</p><p>하지만 한편으로는 그것도 꽤 이상했습니다. 만일 모든 사용자들이 나와 똑같은 상황이라면 분명히 포럼이 쌩난리였을텐데 왜 이렇게 조용하지?</p><p>그런데 의외의 지점에서 해결책이 나오더군요. 제가 찾은 해결책은 이렇습니다.</p><p></p><ol style="text-align: left;"><li>Options에서 Language Client를 선택합니다.</li><li>Python Language Server를 선택합니다.</li><li>Startup behavior를 "Requires an Open file"에서 "Start Server per Project"로 변경합니다</li><li>끝!</li></ol><p></p><p>왜 그런지는 잘 모르겠지만 하여간 이렇게 하니 만족스러운 속도가 나오더군요. 사실 Qt Creator의 기본 설정만으로 작업하려고 했을 때에는 전혀 답이 나오지 않아서 Visual Studio Code + Qt Designer 조합으로 가려고 했는데, Qt Creator가 제대로 성능이 나오기 시작한 이상 굳이 이렇게 갈 필요가 없어졌습니다.</p><p>혹시나 Qt Creator로 Python 개발하시려는 분들께 도움이 되었길 바랍니다.</p>T.Robinhttp://www.blogger.com/profile/12834494724018712162noreply@blogger.com0tag:blogger.com,1999:blog-3594288631091363665.post-54703835012066779672021-03-20T16:06:00.006+09:002021-03-22T08:43:40.265+09:00Would no open source LTS for Qt shrink its user base?<p>It was last year(2020). Qt announced that it won't provide LTS to its open source editions.<br />https://www.qt.io/blog/qt-offering-changes-2020</p><p>And this year(2021), at March 4th, Qt 5.15.3, the first LTS only for commercial users is released.<br />https://www.qt.io/blog/commercial-lts-qt-5.15.3-released</p><p>Of course the replies for this post are under flame war from open source edition users. They say that Qt user base will shrink, the company has started the first nailing to its coffin, ...... and such. Well, the blog evokes flame war from time to time so I'm just watching it with my popcorn.......</p><p>But let's sit down and think. Would the policy of "LTS for commercial clients only" shrink the general user base for Qt? In my thought, it's absolute NO. Let me ask you one question to make it clear.</p><p>"OK. Say, let's not use Qt. Then what will we use?"</p><p>As far as I know, there's no such library which can fully replace Qt in 1 on 1 way. Some features would be able to be replaced, but Qt-only features are not replicable at all(hey, if you think you can replace Qt Quick with Electron, how would you deal with that RAM consumption?), and if you're joining some other ecosystems, chances are that the difficulty for maintenance will rise exponentially. And say, among those "alternatives," which can provide at least a good match in terms of commercial-level documentation and consistency in API structure? wxWidgets? Hey, I saw so many news to migrate from wxWidgets to Qt but not in the opposite way.</p><p>Think of it. When a new language is created, one of the external libraries bound to that language ecosystem at the first time is Qt, which shows what Qt is among developers.</p><div style="text-align: left;">Of course I fully understand the "wrath" from the open source community. I see bugs, report them, and even provide patches for them, and my contributions are applied only to commercial buyers? Yes you have your reasons. Yet...... We also have to admit that we're using commercial grade library without charge in return. And you know what? Before 2011, Qt didn't have LTS at all. (on a side note, if you distinguish open source projects to those with LTS and without one, the former will be only a handful of them).</div><div style="text-align: left;">Well, I don't say that it's great to decide not to provide LTS to open source community. I'd say that it's a typical example of monopoly. The problem is that practically there's no competition. And I don't think that the commercial market is big enough for more than two companies can coexist. Let's admit (the harsh) reality. However hard the open source community works, without good financial support(either by companies or from foundations), you need to pour the money for more than years. Do you think that there would be a company to invest more than years to build a good alternative to Qt? I don't thinks so.</div><div style="text-align: left;"><br /></div><div style="text-align: left;">Someone said that he can't upgrade the version of Qt because LTS is not provided. Well, I'd say that how would you use a library which doesn't provide LTS at all, regarding your concerns about stability?</div>T.Robinhttp://www.blogger.com/profile/12834494724018712162noreply@blogger.com0tag:blogger.com,1999:blog-3594288631091363665.post-74028373717805393732021-03-20T15:47:00.002+09:002021-03-20T16:11:42.893+09:00Qt의 오픈소스 버전 LTS 제공 중지는 Qt의 사용자 기반을 줄일 것인가?작년(2020년)이었죠. Qt가 더이상 오픈소스 버전에 LTS를 제공하지 않기로 선언했습니다.<div>https://www.qt.io/blog/qt-offering-changes-2020</div><div><br /></div><div>그리고 올해(2021년) 3월 4일, 상용으로만 제공되는 첫번째 LTS인 Qt 5.15.3이 발표되었습니다.</div><div>https://www.qt.io/blog/commercial-lts-qt-5.15.3-released</div><div><br /></div><div>들어가 보시면 아시겠지만, 댓글창은 오픈소스 사용자들의 분노(?)의 도가니탕입니다. Qt 사용 인구가 줄어들 것이라던가, 관짝에 첫 못질을 시작했다던가 하는 등등의 이야기들로 말 그대로 불타오르고 있습니다. 뭐 저 곳 블로그는 조용할만하면 어떻게든 꼭 한번씩 불타오르는 터라 그냥 그러려니 하고 있습니다만......</div><div><br /></div><div>그런데 냉정하게 생각해봅시다. LTS를 상용 라이선스 고객에게만 제공한다는 저 정책 때문에 Qt의 사용자 수가 줄어들까요? 솔직히 말해서 전 아니라고 봅니다. 당장 이 질문 하나만 던져보면 확실해지죠.</div><div><br /></div><div>"그래, Qt를 안 쓴다고 치자. 그럼 뭐 쓸래?"</div><div><br /></div><div style="text-align: left;">최소한 제가 알기로는, 여기에 1:1로 매칭되는 대안을 제공할 수 있는 라이브러리같은 것은 없습니다. 일부 기능을 대체할 수는 있겠지만, Qt 고유의 기능들은 대체가 불가능할 뿐더러(혹시 Qt Quick을 Electron으로 완전히 대체할 수 있다고 주장하시는 분들, Electron이 처묵처묵하는 저 엄청난 양의 RAM은 어쩌실 생각이신가요?), 다른 생태계를 끌어들이는 순간 그 프로그램은 유지보수의 난이도가 기하급수적으로 상승하게 될 겁니다. 게다가 그 '대체재'라는 것들 중 Qt가 제공하는 상용 수준의 문서와 일관성있게 잘 다듬어진 API를 쫓아올만한 것이 뭐가 있을까요? wxWidgets? 전 wxWidgets에서 Qt로 마이그레이션한 사례는 봤어도 그 반대사례는 보지 못했습니다.</div><div style="text-align: left;"><br /></div><div style="text-align: left;">당장 새로운 언어가 나오면 GUI를 위해 가장 먼저 바인딩되는 C++ 라이브러리 중 하나가 Qt라는 것만 봐도 Qt가 개발자들 사이에서 가지는 위상이 어떤지가 보이죠.</div><div style="text-align: left;"><br /></div><div style="text-align: left;">물론 오픈소스 커뮤니티의 분노(?)가 이해가 가지 않는건 아닙니다. 난 버그 발견하고, 보고하고, 심지어는 패치까지 무료로 주는데 그걸 돈주고 파는 사람에게만 모아주겠다는건 어떻게 보면 놀부 심보처럼 보일 수 있을겁니다. 하지만 뭐...... 그 대신에 우린 높은 수준의 완성도를 가진 라이브러리를 무료로 사용하고 있잖아요. 게다가 저 LTS라는 것, 2011년 이전에는 아예 존재하지도 않기도 했고...... (여담이지만, 오픈소스 프로젝트 중 LTS를 제공하는 프로젝트와 그렇지 않은 프로젝트를 구분하면 전자는 거의 손가락으로 꼽을 수준 정도밖에 안 될 겁니다)</div><div style="text-align: left;"><br /></div><div style="text-align: left;">뭐, LTS를 오픈소스에 제공하지 않는다는게 잘 하는 짓(?)이라는 이야기는 아닙니다. 해석하기에 따라서는 독점 시장의 폐해라고도 할 수 있지요. 단지 지금으로서는 적절한 대체재가 없다는게 문제입니다. 실제로 이쪽 시장이 생각보다 좁아서 Qt급의 회사가 두 개 이상 존재할만한 크기일 것 같지도 않고요. 인정할 것은 해야죠. 오픈소스가 아무리 열심히 한다고 해도, 제대로 된 자본의 투입이 없다면 그만큼 제대로 된 무언가를 만들기가 쉽지 않습니다. 회사에서 재정 후원을 받든, 아니면 별도로 재단을 설립하든 간에 어쨌든 꾸준히 돈을 쏟아부어야 됩니다. 이미 절대강자가 있는 이 시장에 굳이 최소 몇 년 이상 돈을 꾸준히 쏟아부을 회사들이 있을지...... 솔직히 전 없을 것 같습니다.</div><div style="text-align: left;"><br /></div><div style="text-align: left;">누가 그렇게 썼더군요. 자기 오픈소스 프로젝트에 Qt LTS가 제공되지 않아서 불안해서 못쓰겠다고...... 전 거기다 대고 한마디 하고 싶습니다. 거 LTS 제공 안하는 라이브러리들은 불안해서 어떻게 쓰신데요?</div>T.Robinhttp://www.blogger.com/profile/12834494724018712162noreply@blogger.com0tag:blogger.com,1999:blog-3594288631091363665.post-35904465728016047832020-11-18T11:35:00.000+09:002020-11-18T11:35:01.185+09:00Strangely beautiful SQL UPDATE (PostgreSQL 12)<p>When you need to fill data to null fields of multiple records, you had better avoid UPDATE. Rather:</p><ol style="text-align: left;"><li>Prepare for a table to insert all the data to fill</li><li>Use SELECT ~ OUTER JOIN to join with the original table</li><li>And prepare for the "result" table to push the JOINed results via INSERT INTO.<br /></li></ol><p>I found out that I could finish the job in 5:35 when traditional UPDATE didn't finish in 10 minutes.</p><p>Tested under PostgreSQL 12.</p><br />T.Robinhttp://www.blogger.com/profile/12834494724018712162noreply@blogger.com0tag:blogger.com,1999:blog-3594288631091363665.post-23037575139559296142020-11-18T11:31:00.001+09:002020-11-18T11:31:21.234+09:00이상하고 아름다운 SQL UPDATE (PostgreSQL 12)<p>대량의 레코드를 대상으로 빈 필드에 데이터를 추가로 넣는 작업을 할 경우, UPDATE로 각 레코드를 찾아서 갱신하는 것보다</p><ol style="text-align: left;"><li> 수정할 데이터를 삽입할 테이블을 하나 더 생성해서 추가 데이터를 여기에 넣은 뒤에 </li><li> SELECT ~ OUTER JOIN으로 원래 테이블과 합치고 </li><li>결과를 INSERT INTO로 별도 테이블로 밀어넣는게 훨씬 빠릅니다.<br /></li></ol><p>UPDATE로 10분 넘게 걸려도 안 끝나던게 이 방법을 쓰니까 5분 35초만에 끝나네요. -o-;</p>PostgreSQL 12로 테스트해봤습니다<br />T.Robinhttp://www.blogger.com/profile/12834494724018712162noreply@blogger.com0tag:blogger.com,1999:blog-3594288631091363665.post-52357485495364898412020-09-19T19:55:00.002+09:002020-09-19T19:55:05.551+09:00Short thoughts about PostgreSQL(+a few lines for SQLite)<p>For a few days ago, I started to use PostgreSQL. Current development is not using database as it becomes I/O bottleneck, so I merely write down everything from RAM to disk at once(and additionally it is compressed in LZ4 to shrink write time). The problem was, that RAM usage skyrocketed as target data becomes TBs. Just guess what you feel when you encounter out of memory after 10 minute operation. What's worse, since I was working on portable server, the total amount of RAM was limited to certain degrees, not like rack-type servers.<br /></p><p>So, I changed the mind model - use DB like as something like swap file! Currently the process is processing all the data on <a href="https://github.com/martinus/robin-hood-hashing">robin hood hash</a>, but if we can control them under a database table and remove that table after use, we can save RAM dramatically(well, at least out of memory won't be shown if we limit working memory for DB).<br /></p><p>At first, I tried SQLite. It consumes so low on RAM so that even Android and iOS can use, and before that I developed a class optimized for bulk insert with the engine. However, soon I found that it consumes practically infinite time when the query is even slightly complicated. When I tried an inner join query with two tables with 7 million records each, I gave up after 30 minutes. And it couldn't process in parallel since the engine is working only on single thread.<br /></p><p>Later, I found out that <a href="https://www.sqlite.org/optoverview.html#order_of_tables_in_a_join" target="_blank">SQLite uses only nested loop on table joins.</a> If we're working on previous case(two tables with 7 million records each), we need to loop for <b>49 trillion </b>times. And it should work on single thread. Oh God.<br /></p><p>As I was cornered and forced to use "ordinary" database, my choice was MySQL, MariaDB, and PostgreSQL. MySQL was dropped at early stage as no tweak and tuning seems to have worked as I intended, so my choice became more limited. And I chose PostgreSQL for its a few exceptional features:<br /></p><ol style="text-align: left;"><li>It's not snail-slow as people say. Instead, if you tweak just few, it "flashes." No, I can't agree with the words "it's slower than MySQL or MariaDB." When you're working hard to tweak MySQL/MariaDB to maximize its performance with your complicated tweak but if you don't do same amount of efforts and try PostgreSQL just out-of-box, well, it's not fair. Isn't it? <br /></li><li>Parallel query processing! If you send a query the scheduler "smartly" judges and multi processing is done as needed. For my inner join of two tables with 7 million records, it consumed 3 cores and gave result in 1 minute 30 seconds. <br /></li><li>It's simply reliable in any bad condition. After stress test, PosgreSQL survived and gave result when MariaDB service was restarted<br /></li></ol><p>Currently I'm using it as if it's some kind of hashmap. Well, maybe that's because I previously worked with them. Thinking that the basis of database indexing is hashmap, it may be one of the best practice for any SQL based database. Additionally, my coding became simplified since database does most of the things automatically......</p><p>Now I agree with the slogan, <a href="https://www.postgresql.org/">The World's Most Advanced Open Source Relational Database</a>. Keep up the good work, PostgreSQL Global Development Group!</p>T.Robinhttp://www.blogger.com/profile/12834494724018712162noreply@blogger.com0