Software Development

Are You in the Mythical 5%?

Bruce Eckel의 The Mythical 5% (Via Steve Vinoski)라는 글을 읽었다.

최상의 프로그래머가 최악의 프로그래머에 비해 28배의 생산성을 보여준다는 사실은 people factor를 강조하는 사람들에 의해 꾸준히 인용되는 사실이다. 5%라는 숫자가 어디서 나왔는지는 모르겠지만, 여기서 mythical 5%는 그러한 최상의 프로그래머들을 일컫는다.

Bruce Eckel은 이 숫자에 대해 재미있는 가설을 세운다.

Let’s say that this follows the 80-20 rule. Roughly 80% of programmers don’t read books, don’t go to conferences, don’t continue learning, don’t do anything but what they covered in college. Maybe they’ve gotten a job in a big company where they can do the same thing over and over. The other 20% struggle with their profession: they read, try to learn things, listen to podcasts, go to user group meetings and sometimes a conference. 80% of this 20% are not very successful yet; they’re still beginning, still trying. The other 20% of this 20% — that’s about 5% of the whole who are 20x more productive.

즉, 전체 프로그래머들 중 20%만이 뭔가를 배우려고 애쓰고, 그 중에서도 20%만이 성공을 거두어 mythical 5%가 된다는 것이다. Bruce Eckel에 따르면, 이 5%의 사람들은 엄청나게 읽어대며, 새로운 어떤 개념이 쓸만하다면 언제든지 시도해보며, 가장 좋은 도구와 기술, 생각들을 채용한다. 결정적으로 그들은 항상 최선을 다한다.

숫자들은 무시하도록 하자. 분명 프로그래머들 중 소수의 사람들만이 뭔가를 배우려고 애쓰고, 역시 소수의 사람들만이 뛰어나다는 것은 인정할 수 밖에 없는 사실이다. 그런 면에서 Bruce Eckel의 직관은 옳은 것 같다.

적어도 나는 뭔가를 배우려고 애쓰는 20%에 속한다고 말할 수 있다. 아마 이 글을 읽는 사람들도 그러한 20%에 속한 사람들일 것이다. 나는 모든 프로그래머가 그 20%가 되어야한다고는 생각하지 않는다. 각자 어떤 삶을 원하는가는 다른 법이기 때문이다. 어쩌면 묵묵히 자신이 할 일을 하는 80%가 있기에 다른 20%가 존재할 수 있을지도 모른다. (물론 80% 모두가 자신이 할 일을 하지는 않겠지만.)

내가 mythical 5%가 되기에 성공한 20%인지는 아직 모르겠다. 아마도 그 주변부에서 맴돌고 있는 듯하다. 여러 practices들을 접하고 어떤 것들이 best practices인지는 어느 정도 알지만 아직 그것들을 체계화하고 모두 습관화하지는 못한 단계다. Bruce Eckel은 배우려고 하지 않는 80%를 위한 조언을 했을 뿐, mythical 5%가 되기 위해 노력하고 있는 80%를 위한 조언은 하지 않았다. 그 답은 내가 스스로 찾아가야할 것이다. 내가 잊지 않아야하는 태도는 ‘최선을 다한다‘는 것일테다.

이 글의 나머지는 Code Readability의 중요성, Code Review의 유용함, 짧은 역사를 가진 소프트웨어 산업의 한계, 프로세스와 인간의 중요성, management의 어려움과 중요성, 소프트웨어 산업에 있어서의 비결정적인 요소, 경제와 비즈니스의 중요성에 대한 짤막하지만 현재 소프트웨어 산업의 지혜들을 담고 있는 심원한 문장들로 채워져있다. 소프트웨어 산업에 뛰어드는 신입자들로부터 5~10년 정도의 경력자들까지도 이 글을 한번씩 읽어보기를 권장한다.

Are You in the Mythical 5%? 더 읽기"

Java Pitfalls: Excuting an external program using Runtime.exec() method or ProcessBuilder class

Java 프로그램에서 외부 프로그램을 실행하고 싶을 때, Runtime.exec() 또는 Java 1.5에서 추가된 ProcessBuilder를 사용해 Process 객체를 얻을 수 있다. 처음으로 이런 프로그램을 짤 때, 실수한 것이 없어보이는데도 실행한 프로그램이 종료되지 않는 경우가 있다. 더군다가 어떤 경우엔 정상적으로 종료되고 어떤 경우엔 종료가 되지않는 경우도 보인다.

원인은 바로 Process 클래스의 API Reference에 있는 다음과 같은 설명에서 찾을 수 있다.

The created subprocess does not have its own terminal or console. All its standard io (i.e. stdin, stdout, stderr) operations will be redirected to the parent process through three streams (getOutputStream(), getInputStream(), getErrorStream()). The parent process uses these streams to feed input to and get output from the subprocess. Because some native platforms only provide limited buffer size for standard input and output streams, failure to promptly write the input stream or read the output stream of the subprocess may cause the subprocess to block, and even deadlock.

즉, Java에서는 Child 프로세스로의 표준 입출력을 Parent 프로세스가 다루어주어야 한다. 당연하게도, Child의 출력을 언제까지고 버퍼링할 수 없기 때문에, 다른 프로그램을 실행할 때는, 특히 표준 입출력이 존재하는 프로그램을 실행할 경우에는, 항상 이 스트림들의 입력 또는 출력을 제대로 다루어주어야 한다.

이러한 동작은 일반적으로 프로그래머들이 익숙한 시스템콜들, 이를테면 UNIX 계열의 fork()/exec()나 Windows의 CreateProcess()처럼, parent 프로세스의 터미널/콘솔을 공유하는 동작과는 다르기 때문에, 프로그래머들이 쉽게 간과하고 실수하기 쉽다. 더군다나 API Reference가 이러한 점을 명확히 설명하고 있지도 않다.

해결책은 당연하게도 표준 입력이 필요할 때는 Process.getOutputStream()을 이용하여 필요한 입력을 해주고 스트림을 닫아주어야하고, 표준 출력이 필요할 때는 Process.getInputStream() 그리고 Process.getErrorStream()을 이용하여 스트림을 비워주어야한다. 드물겠지만 만약 실행할 프로그램이 interactive한 프로그램일 경우에는 입출력에 좀 더 신경을 써야한다.

다음은 표준 출력을 비워주기 위한 코드가 들어간 간단한 예다.

Runtime rt = Runtime.getRuntime();
try {
Process proc = rt.exec("cmd /c dir");
// Process proc = new ProcessBuilder().command("cmd", "/c", "dir").start();
InputStream is = proc.getInputStream();
BufferedReader reader = new BufferedReader(new InputStreamReader(is));
String line;
while ((line = reader.readLine()) != null) {
System.out.println(line);
}
int exitVal = proc.waitFor();
System.out.println("Process exited with " + exitVal);
} catch (Exception e) {
System.err.println("Failed to execute: " + e.getMessage());
}

표준 출력 뿐만 아니라 표준 에러까지도 처리를 할 수 있어야 좀 더 일반적인 코드일 것이다. 표준 에러의 버퍼가 차버리면 표준 출력을 비우려고 해도 블럭될 수 있다. 이러한 문제는 Java World의 When Runtime.exec() won’t 라는 글에서 StreamGobbler라는 클래스를 도입해서 약간 더 우아하게 해결하고 있다. 하지만, 간단한 작업을 하기 위해 쓰레드를 사용하고 있기 때문에 좀 오버킬이라는 생각이 든다.

표준 출력과 표준 에러를 간단하게 무시하고 다른 프로그램을 실행할 수 있도록 하는 옵션이 있다면 자주 사용할 수 있으며 편리할 듯 하다. 또는, 디폴트 동작을 일반적인 시스템콜의 동작과 비슷하게 만들어주는 것도 괜찮을 듯하다. 시간이 나면 이런 방식의 Wrapper를 찾아보거나 만들어보아야겠다.

Java Pitfalls: Excuting an external program using Runtime.exec() method or ProcessBuilder class 더 읽기"

Silverlight 1.0: Getting Started

갑자기 자다 깨는 바람에, DDJ의 지난 10월 기사인 Silverlight 1.0: Getting Started을 읽었다.

이 기사는 Silverlight의 소위 Hello, World를 수행하기 위한 과정을 설명하고 있는데, 쓸데없이 장황한 면이 있다. Microsoft가 제공하는 Silverlight 1.0 Quickstarts를 읽는 것이 나아보인다. 요점은 Silverlight 플러그인을 HTML 내에 삽입하기 위해서는 OBJECT나 EMBED/NOEMBED element를 사용하지말고, Silverlight SDK에서 제공되는 javascript(Silverlight.js)를 사용해야한다는 것이다.

한편, 요샌 RIA 기술에 별로 신경을 쓰지못해서 깨닫지 못하고 있었는데, Microsoft의 SilverlightXAML + Javascript(+.NET languages) 기술, Adobe의 FlexMXML + Actionscript 기술이 SDK와 다른 SDK들로부터의 지원, 개발 도구, 미디어 기술 등과 함께 포장되어나온 것이다. 오래 전에 강문식 군이 XAML을 가지고 놀던 기억이 난다.

Silverlight든 Flex든 XAML/MXML+Javascript/Actionscript의 장점을 고스란히 지니게 된다. 별다른 개발 도구 없이도 서버사이드에서 텍스트 파일을 살짝 수정하는 것만으로도 바로 브라우저에 UI 변경사항이 반영된다는 것이다. 기존의 RIA 기술인 ActiveX나 Flash와 비교해보면 RIA 개발 효율이 상당히 높아질 수 있을 것같다. C로 CGI를 만들던 시절과 Perl/PHP로 Web App을 개발하는 현재를 비교해보면 말이다. Web App을 만들 일이 생긴다면 한번 시도해보고 싶다.

섣불리 얘기하기는 꺼려지지만, XAML/MXML+Javascript/Actionscript들을 서버사이드에 두고 HTTP 프로토콜로 접근한다면, 이들의 용도는 한정될 수 밖에 없는 것 같다. 이를테면 중요한 비즈니스 제약들을 여기에(만) 넣을 수는 없다는 것이다. – 예전에 Flex Architecture를 흘끗 본 기억으로는 서버사이드에서 실행되는 컴포넌트 기술도 포함하고 있었던 것 같고, Silverlight를 아우르는 ASP.NET도 당연히 그러한 기술을 포함하고 있겠지만, 여기서는 Silverlight와 Flex의 핵심 기술들만 얘기하자. – 하지만, 궁극적으로는 클라이언트 상에서 동작하는 RIA 기술 자체가 그런 문제를 가지고 있는 것이지 이 기술들이 가지고 있다고 볼 수는 없는 것 같다. 실질적으로도 (적어도 현재는) RIA 기술들은 주로 Presentation 위주로 사용되며, 중요한 트랜잭션 (이를테면 신용카드 결제) 들은 다른 방법으로 (예를 들어, 서버사이드에서 실행되는 로직) 해결하는 것이 일반적인 상황인 것 같다. 다시 말하면, RIA 기술은 Presentation 위주로 사용하고, 중요한 비즈니스 로직은 서버사이드에서 실행하는 식으로 이러한 문제는 해결되고 있는 것 같다. 다른 해결책도 물론 있다. 정말 필요하다면 암호화와 신뢰를 보장하기 위한 보안 모델을 사용할 수도 있다. 이 방법 역시 ActiveX에서 사용하고 있는 것이다 (ActiveX의 인증서!). 하지만, 사람들은(개발자든 사용자든) 보안으로 인한 비용들(귀찮음이든 개발 비용이든)을 별로 좋아하지 않는다는 것이 이 방법의 문제점이다.

잡설이 길었다. RIA 기술들의 실제적인 필요성은 점점 부각되고 있다. Microsoft와 Adobe가 열정적으로 RIA 기술을 지원하고 있으며, 특정 부문에서 이들을 대체할만한 기술 (e.g. HTML 5)이 단기간 내 – 3년 내에 – 나오기는 하더라도 – 성숙하기는 힘들지 않을까 예상된다. 이런 환경에서 RIA와 관련이 없는 개발자라고 하더라도 Silverlight 또는 Flex는 한번쯤 주목해볼만한 기술일 것이다. 과연, 어느 쪽이 주도권을 잡을 것인가? 어쩌면 HTML 4일지도. 후훗.

부록 1: Silverlight는 Microsoft의 구현 만 있는 것이 아니라 Mono 팀의 구현인 Moonlight도 있다.

부록 2: XML의 응용인 XAML보다 IronRuby를 이용한 Silverlight DSL이 훨씬 읽기편하고 예뻐보인다. 인간이 읽고 써야하는 선언적 코드(e.g. Configuration, Markup, Schema, …)에서 XML보다는 Agile 계열의 언어를 활용한 DSL을 사용하는 프랙티스들이 요즘 많이 나오고 있다. 아직은 실험적인 단계라고 해야겠지만, 내겐 긍정적으로 보인다.

Silverlight 1.0: Getting Started 더 읽기"

Database Access Layer

일반적으로 여러 DBMS 벤더 별로 플랫폼 또는 언어에 따라 접근할 수 있는 서로 다른 API들을 제공한다. 이들을 한꺼풀 덮어씌워 공통적인 방법으로 접근하도록 해주는 API를 Database Access Layer 또는 Database Abstraction Layer라고 부른다.

JDBC, ODBC, ADO.NET, Perl의 DBI, PHP의 PDO, Pear::MDB2 (Pear::DB, Pear::MDB) 등은 모두 Database Access Layer에 해당하는 것들이다. 각자 특정 플랫폼 또는 프로그래밍 언어에서 표준적인 위치를 가지고 있다. 물론 이외에도 이들과 경쟁하는 API들이 있으며, 이 외의 플랫폼 또는 언어들도 이러한 API를 가지고 있다.

Jeremy Zawodny는 LtU에서도 이슈가 된 모양Database Abstraction Layers Must Die!라는 글에서,

  • 성능을 떨어뜨리고 복잡도를 증가시킨다.
  • 데이터베이스를 쉽게 바꿀 수 있다는 이점은 별로 중요하지 않으며, 실제로는 쉽게 바꿀 수 없다.
  • 데이터베이스 기능을 완전하게 활용할 수 없고, 튜닝도 제대로 할 수 없다.

는 점에서 Database Abstraction Layer를 사용하기 보다는, 데이터베이스에 접근하는 부분을 ‘라이브러리’를 사용해서 모듈화하고, 만일에 하나라도 데이터베이스를 변경할 일이 생긴다면, 이를 수정하면 된다고 설명하고 있다. 그의 주장은 일견 옳다.

이 외에도

하지만, 여러 사람이 지적한대로, 그가 얘기하는 ‘라이브러리’가 바로 Database Abstraction Layer다. 그리고,

  • 성능은 떨어질 수 있지만, scalability도 떨어지는 것은 아니다.
  • 여러 데이터베이스를 지원해야하는 소프트웨어도 존재한다.
  • 하나의 소프트웨어에서 데이터베이스를 바꾸지 않더라도, 프로그래머는 여러 소프트웨어에서 서로 다른 데이터베이스를 접근해야한다.
  • Database Abstraction Layer 라기보다는 Database Access Layer다. 즉, 데이터베이스의 공통적인 기능에는 공통적인 방법으로 접근할 수 있도록 하지만, 특정한 데이터베이스가 지원하는 기능을 접근하도록 만들 수도 있다.

는 면에서 Database Access Layer는 유용하다.

JDBC를 예로 들어보면,

  • 많은 Java 프로그래머들은 특정 데이터베이스 API를 익힐 필요없이 JDBC만을 알아도 데이터베이스에 접근하는 기본적인 프로그램을 짤 수 있다.
  • 특정한 데이터베이스가 지원하는 대부분의 기능은 SQL이라는 불투명한 데이터를 전달하는 API를 통하거나, 설정을 통해 동작방식을 제어하는 방식으로 접근할 수 있다.

‘왜 PHP 개발자들은 Pear::MDB2 또는 PDO를 아직 사용하지 않는가’라고 물어보면 분명 위와 같은 이유로 반대하는 사람들이 많을 것이라고 생각한다. 이 글이 그에 대한 대답이다. 그리고 한가지 더, JDBC, ODBC, ADO.NET, Perl DBI 등의 성공은 결정적으로 Database Access Layer의 유용함을 반영하고 있다. JDBC를 한번이라도 사용해본 사람들이 PHP의 mysql이나 mysqli 모듈로 돌아갈 것이라고 절대 상상할 수 없다.

Database Access Layer 더 읽기"

Dryad

Microsoft의 MapReduce라고 부를 수 있는 DryadGoogle Tech Talks 비디오를 보았다.

MapReduce와의 가장 큰 차이점은 acyclic graph 모델을 사용한다는 것이다. 강연에서는 계속 optimization에 관해 얘기하는데, 내가 가장 궁금한 점은 이러한 프로그래밍 모델이 사용자에게 어떻게 보일 것인가 하는 것이다. 분명 acyclic graph 모델이 유용하게 쓰일 수 있는 경우는 있을테지만, 너무 복잡한 프로그래밍 모델은 현실적으로 많은 프로그래머가 접근하지 못하게 만든다. 게다가 acyclic graph 모델이라는 아이디어 자체는 그리 새로운 것은 아니다. MapReduce 페이퍼에서도 인용하고 있는 Condor가 훨씬 generic한 분산 환경이다.

요즘 팀에서 주로 하는 일이 대용량 데이터 처리다보니 이런 문제들에 대해 많이 고민하게 되는데, 현재는 단순한 파이프라인 (정확히는 Pipeline & Filter) 모델을 분산된 멀티프로세서 환경에 최적화하면서, 동시에 사용자가 쉽게 프로그래밍할 수 있는가를 고민하고 있다. 물론, 후자가 더욱 어려운 일이다.

Dryad 더 읽기"

Exception Handling

Rob Walling의 Exception Handling에 관한 글에서 Exception Handling의 두가지 기본적인 규칙이 마음에 들어 인용해본다.

  • Rule #1: If you can’t add helpful information to an error message, don’t catch the exception.
  • Rule #2: If you catch an exception, log it.

실제로 사람들은 너무나 바빠서 또는 게을러서 다음과 같은 짓을 한다.

} catch (YourOwnException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}

이클립스의 기본 Exception Handling 코드 템플릿이다. 사람들은 그저 이클립스에 생성해주는 코드를 너무 신뢰하고 있든가, TODO 라인도 지워주기 어려울 만큼 바쁘거나 귀찮은거다.

만약 프로젝트를 시작하고 있다면 이클립스의 Exception Handling 코드 템플릿을 다음과 같이 정해주면 좋을 것이다.

} catch (YourOwnException e) {
logger.error("failed to do SOMETHING", e);
throw new RuntimeException(e);
}

Exception Handling 더 읽기"

On Swapping and Swappiness

swapping의 목적은, 자주 사용되는 디스크 블럭의 디스크 캐쉬와 자주 사용되는 애플리케이션 메모리를, 자주 사용되지 않는 애플리케이션 메모리로부터 확보해, 메모리의 사용률을 높히고, 결과적으로 전체적인 시스템의 성능을 높히는데 있다.

디스크 캐쉬가 필요로 하는 메모리의 크기는 사용하는 디스크 블럭의 크기와 같다. 디스크를 전혀 읽지 않는다면, 페이지 캐쉬는 필요없고, 디스크의 대부분을 사용한다면, 최대한 많은 메모리를 디스크 캐쉬로 이용하고 싶을 것이다.

결국, (애플리케이션이 할당한 메모리의 크기) + (사용하는 디스크 블럭의 크기)를 한정된 메모리 크기 내에서 가장 효율적으로 사용하기 위한 방법이 swapping이다. 이를 뒤집어 말하면, 모든 데이터들을 담을만큼 메모리가 크다면, swapping을 할 필요는 없다는 얘기가 된다.

커널은 페이지 별로 사용 빈도를 알고 있기 때문에, 애플리케이션이 할당한 메모리의 페이지들과 디스크 캐쉬의 페이지들 가운데 어느 것이 덜 사용되는 지를 판단할 수 있으며, 기본적으로 애플리케이션이 할당한 메모리 페이지의 빈도가 낮다면 swapping을 할 것이고, 디스크 캐쉬 페이지의 사용 빈도가 낮다면 이를 해제하고 애플리케이션 메모리로 활용할 것이다.

쓸데없이 메모리만 할당해놓고 전체 디스크를 긁어대는 애플리케이션이나, 메모리 상의 대용량 데이터를 이용한 계산을 하면서 장시간동안 로그를 기록하는 애플리케이션을 상상해보면, 그리 어렵지 않게 잘 동작할 것이라고 추측할 수 있다.

하지만, 문제는 실제로 사용하는 메모리도 많으면서 사용하는 디스크 블럭도 많은 경우 – 대용량 데이터 처리를 수행하는 디스크 바운드 애플리케이션이다. 이 때, 애플리케이션이 할당한 메모리를 swap하게 되면, 다시 필요할 때, 디스크에서 읽어오기 위해 애플리케이션이 느려진다. (Thrashing) 대신 디스크 캐쉬를 줄이면, 역시 디스크의 반응속도도 느려진다. 게다가 swapping은 디스크 I/O도 느리게 만든다. 당연하게도 이러한 애플리케이션이 느려지지 않기를 원한다면, 충분한 메모리를 확보하는 수 밖에는 없다.

Swappiness에 관한 글에서 언급했듯이 리눅스 2.6 커널은 80% 이상의 메모리가 사용 중일 때, swapping을 하기 시작한다. 이러한 동작은 (특히 데스크탑에서의) responsiveness를 보장하기 위해서 이루어지는 것으로 보인다. 부수적으로 페이지 캐쉬의 크기를 보장하는 효과도 있으리라고 보인다. 1GB의 메모리를 가진 머신에서, 20% 즉, 200MB의 페이지 캐쉬를 확보하기 위한 노력이라고 보면 당연해 보이지만, 32GB의 메모리를 가진 머신에서, 위와 같은 애플리케이션을 동작시킨다면, 6.4GB의 페이지 캐쉬를 확보하기 위해 swapping을 하는 것은 비정상적일 수도 있다. 6.4GB의 페이지 캐쉬가 적절한 정도인가는 사용되는 디스크 블럭의 크기와 사용 빈도에 따라 달라질 수 있는 판단이고, swapping이 애플리케이션의 성능에 도움이 되리라는 보장은 없는 것이다. 80%가 디폴트 동작인 것은 중요만 의미가 있는 것은 아닐 듯하고, 메모리의 크기가 커질 수록 더욱 그 의미가 이상해진다.

swapping이 애플리케이션에 해를 끼치는 경우는 극단적으로 애플리케이션이 자체적으로 디스크 캐싱을 하는 경우다. MySQL이 대표적인 경우인데, 자체 캐쉬를 줄이고, 리눅스의 디스크 캐쉬를 활용할 수도 있지만, 자체 캐쉬를 사용할 경우 좀 더 효율적인 메모리 관리를 기대할 수 있다. 그렇다면, MySQL이 자체 캐쉬를 사용할 경우에, 페이지 캐쉬의 크기를 20% 정도로 유지하는 것이 의미가 있을까? 32GB 머신에서 6.4GB를 페이지 캐쉬로 확보해둔 상태에서 swap을 해야할까? (물론 MySQL이 페이지 캐쉬를 사용하지 않도록 설정해도 마찬가지다.)

이러한 경우에 Swappiness를 튜닝할 필요성이 발생하는 것 같다. 실제로 많은 MySQL 튜닝 가이드라인은 Swappiness를 0으로 만들 것을 권장하고 있다. 개인적인 생각으로는 Swappiness를 30-50 정도만 하더라도 애플리케이션이 메모리를 모두 사용하더라도 swap_tendency가 100을 넘지 않아 swap을 시작하지 않으므로 괜찮으리라 생각되지만, distress 등의 변수를 생각하면 실제 상황에서의 실험이 필요할 듯 하다. VM에 관련된 커널 동작은 커널 버전, 심지어는 2.6 내에서도 크게 달라지는 것으로 보인다. /proc/sys/vm/*의 파라미터들도 2.6 내에서 생겼다가 사라지기도 하고, 각각의 동작을 정확하게 정의하기도 어려우며, 그 파라미터들이 어울려서 어떤 동작이 나올 것인가를 예측하기란 더욱 어렵다.

 

결론: 대용량 데이터 처리를 수행하는 디스크 바운드 애플리케이션을 대용량 메모리 상에서 수행할 경우 Swappiness 튜닝에 신경 쓰도록 하자.

On Swapping and Swappiness 더 읽기"

Swappiness in Linux 2.6

Linux 2.6의 swap 경향은 mm/vmscan.c에 의하면 다음과 같이 정의된다.

swap_tendency = mapped_ratio / 2 + distress + sc->swappiness;

이 때, mapped_ratio는 다음과 정의된다.

/*
* The point of this algorithm is to decide when to start
* reclaiming mapped memory instead of just pagecache.  Work out
* how much memory
* is mapped.
*/
mapped_ratio = ((global_page_state(NR_FILE_MAPPED) +
global_page_state(NR_ANON_PAGES)) * 100) /
vm_total_pages;

즉, mapped_ratio는 전체 메모리 크기에 대한 애플리케이션이 사용하고 있는 메모리 크기의 비율이다.

distress는 다음과 같이 정의된다.

/*
* `distress' is a measure of how much trouble we're having
* reclaiming pages.  0 -> no problems.  100 -> great trouble.
*/
distress = 100 >> min(zone->prev_priority, priority);

include/linux/mmzone.h에 따르면, priority는 priority of VM scanning이며, (아마도 free page를 확보하기 위해) 한번에 zone별 page 리스트를 스캐닝 하는 페이지의 수를 얻는데 (queue_length >> priority) 사용되며, 디폴트 값은 12로 한번에 2^12 = 4096개의 페이지를 스캔하는 것을 의미한다. (아마 free page를 확보하지 못해,) priority가 올라갈 수록 (값이 낮아질수록) 많은 페이지를 스캔하게 된다. 2.6 VM을 제대로 공부하지 않아서 이 부분은 확신할 수는 없다.

어쨌든, priority가 디폴트 값인 12일 때, distress값은 100/2^12 = 0이다.

swappiness는 sysctl이나 /proc/sys/vm/swappiness를 통해 사용자가 설정할 수 있는 파라미터이며, 0-100 사이의 값을 갖는다. swappiness의 디폴트 값은 60이다.

/*
* From 0 .. 100.  Higher means more swappy.
*/
int vm_swappiness = 60;

이런 값들로부터 계산된 swap_tendency가 100이 넘으면, mapped memory를 inactive list로 옮기기 시작한다. 다시 말하면, swap을 시작하게 된다.

예를 들어, 2GB의 메모리를 가지고 있는데, 1.8GB의 메모리를 애플리케이션이 사용하고 있고, distress가 0인 상태라면, swap_tendency는 1.8GB * 100 / 2GB / 2 + 0 + 60 = 105가 되어, swap 되기 시작한다.

결국, swap_tendency를 산출하는 식에 따르면, swappiness가 디폴트 값 60인 상태에서는 80% 이상의 메모리를 사용하기 시작하면, swap하기 시작한다. 다음과 같은 방법으로 swappiness 값을 조정함으로써, 이러한 swap behavior를 조정할 수 있다.

sysctl -w vm.swappiness=30

또는,

echo 30 >/proc/sys/vm/swappiness

References

Swappiness in Linux 2.6 더 읽기"

WebCrawler

Pinkerton, B. 1994. Finding What People Want: Experiences with the WebCrawler

이 논문은 1994년 10월, Chicago에서 열린 두번째 WWW 컨퍼런스에서 발표된 논문이다. WebCrawler 역시 웹의 resource discovery 문제 해결을 위해 웹 크롤러를 사용하는 검색엔진에 해당한다. 불과 5개월 간격이지만 첫번째 WWW 컨퍼런스에 등장한 검색엔진에 비해서 꽤 완성도 면에 있어서 나아진 면을 보인다.

WebCrawler의 디자인에 대한 설명은 웹에 대해서 잘 모르는 사람들도 이해할 수 있을 정도로 자세하고 쉽게 되어있어서, 웹 크롤러에 대한 설명이 필요하다면 참고할 만하다. WebCrawler의 architecture는 크게 Search Engine, Agents, Database, Query Server로 이루어진다. Search Engine은 크롤과 인덱싱 작업을 담당하며, 여러 프로세스 (15개)로 동시에 동작하는 Agents는 실제로 웹 페이지를 다운로드 받는 역할을 한다. Database는 문서의 메타데이터나 링크, full-text 인덱스를 저장한다. 특이한 점은 방문한 server들에 대한 정보를 가지고 있어서, 방문하지 않은 서버나 least recently visited 서버를 먼저 방문함으로써, 가능한 한 여러 서버들을 공평하게 방문하려고 시도한다는 것이다. Query Server는 사용자의 검색엔진 질의를 담당한다.

WebCrawler의 성능은 486 PC에서 1시간에 1000개의 문서를 인덱싱하는 정도라고 한다. 현재의 기계로는 1초에 그 정도 이상의 문서를 처리할 수 있음을 생각하면, 1000~10000배 이상의 성능 차이가 난다는 것을 알 수 있다. 게다가, 이러한 성능 차이는 웹 크롤러 – 검색엔진의 디자인에 많은 영향을 줄 것임에 틀림없다.

인덱싱은 IndexingKit이라는 라이브러리를 사용하는 것으로 보이고, lexical anlyzer, stop list, inverted index, vector-space model에 관한 언급들이 보인다.

이 논문에서 저자는 6개월 간의 WebCrawler 운영을 통한 교훈을 정리해놓았는데, 다음과 같다.

  • Full-text indexing is necessary
    • 당시에도 20% 가량의 웹 페이지들은 title을 가지고 있지 않았으며, title의 유효성을 인정하기 어렵기 때문에, full-text 인덱싱이 필수적이라는 견해를 보인다. 그리고 실제로 사람들이 원하는 것은 title이 아닌 내용에 대한 좀 더 나은 쿼리 기능을 원하고 있다는 면에서 full-text 인덱싱의 중요성을 설명하고 있다.
  • Precison is the limiting factor in finding documents
    • WebCrawler는 Recall을 적절히 만족시키고 있지만(adequate), Precison의 면에서 만족스럽지 못하다고 얘기하고 있다. 문서의 weighting이 도움이 되지만 false positive (irrelevant documents)를 제거하고 있지는 못했다고 한다. (이 문제는 랭킹의 문제로 보인다.) 또다른 문제는 사용자가 well-focused query를 던지지 못하는 문제를 언급하고 있으며, 사용자 쿼리의 평균 단어 수가 1.5이었다고 한다. 검색엔진이 보편화된 현재도 사용자들은 여전히 단순한 쿼리를 던지고 있다.
  • Breadth-first searching at the server level builds a useful index
    • 어떤 정보를 가지고 있는 서버만 발견할 수 있다면, 사용자들은 그 서버 내에서 좀 더 쉽게 navigate할 수 있기 때문에, Breadth-first searching이 사용자들을 만족시켜준다고 한다. navigational search라는 검색엔진의 용도라고 볼 수 있을 듯 하다. 또한, 크롤 전략 – 순서가 검색 결과에 영향을 미칠 수 있다는 인식이 보인다. 한편, BFS 크롤 전략의 다른 이익으로 politeness를 언급하고 있기도 하다.
  • Robot-like behavior is necessary now
    • 크롤러가 웹 트래픽을 낭비한다는 비판에 대해서 크롤러를 통해 오히려 웹 트래픽을 절감할 수 있다고 주장하고 있다. ALIWEB과 같이 사이트 별 인덱스를 사용하는 것이 아니라, 실제 문서를 인덱싱에 사용함으로써, 좀 더 나은 인덱싱 방법을 사용할 수 있음을 지적하고 있다.
  • Widespread client-based searching is not yet practical
    • 클라이언트에서 크롤러를 동작시켜 문서를 찾아내는 방법은 하나의 서버에서 인덱스를 만드는 방법에 비해 효율적이지 않음을 지적하고 있다. Future Work에서 클라이언트 기반 검색을 하더라도 이 결과를 사용자들이 공유하는 인덱스에 추가하는 방법을 제안하고 있다.

Future Work에서 좀 더 강력한 full-text 엔진의 기능으로, proximity searching과 query expansion & refinement를 제안하고 있다.

결론에서 크롤러에 기반한 검색엔진을 분산된 웹의 특성에 비유하고 있다. 크롤러에 기반한 검색엔진은, 웹 사이트들로부터 (아마도 인덱스 생성과 같은) 특별한 참여를 필요로 하지도 않고, 웹 프로토콜을 그대로 사용함으로써, 동작 방식과 정책의 분리가 가능해졌고, 여러 검색엔진이 여러가지 정책을 채용함으로써, 사용자는 자신의 필요에 적합한 검색엔진을 선택할 것이며, 기술은 좀 더 빠르게 성숙될 것이라고 저자는 예견하고 있다.

WebCrawler 더 읽기"