이전 글:

2021.04.26 - [잡기술/Ubuntu] - 티먹스 tmux 사용해서 터미널 여러 개 띄우기 (1)

2021.04.28 - [잡기술/Ubuntu] - 티먹스 tmux 사용해서 터미널 여러 개 띄우기 (2) - 스크롤, .tmux.conf

 

tmux의 가장 멋진 기능 중 하나는 원격으로 session을 연결해서 작업할 수 있다는 것이다.

 

예를 들어 연구실 컴퓨터에 tmux session을 만들어서 작업하다가,

 

집에 가서 해당 session에 연결하면 연구실에서 보던 똑같은 터미널 화면을 즉각 내 컴퓨터에도 띄울 수 있다.

 

심지어 동기화 속도도 거의 실시간이라 (별로 실용성은 없다.)

 

내 컴퓨터를 통해 해당 세션에서 작업을 수행하면 모든 것이 바로 연구실 컴퓨터에 반영된다.

 

어떻게 보면 터미널판 팀뷰어라고도 볼 수 있는데,

 

터미널 사용을 조금 공부하고 tmux를 도입한 뒤로 팀뷰어 사용이 확 줄었다.

 

(요즘은 아예 사용하지 않는다.)

 

적당히 남은 작업을 하다가 터미널을 끄고, 나머지를 연구실에 가서 하든, 그 반대로 하든 선택은 자유.

 

(물론 요즘은 집에서 일을 거의 안 한다 ㅎㅎ)

 

연구실 컴퓨터에서 가동되고 있는 session 이름을 알면,

 

이전에 설명한 것처럼 어느 터미널에서나 해당 session과 연결이 가능하다.

$ tmux attach -t SESSION_NAME

당연히 SESSION_NAME은 session 이름. 기본은 0이다.

 

 

그런데 기존 session이 화면에 떠있는 상태에서 새로운 session을 연결할 때,

 

터미널의 크기가 맞지 않으면 렌더링이 깔끔하게 되지 않는다.

 

큰 문제가 아닐 수도 있지만, tqdm 같은 정밀하게 터미널을 조작하는 코드가 있는 경우 사정없이 깨져버린다.

 

또, Windows에서 WSL을 통해 크기가 맞지 않는 session에 접속하면 아래 화면처럼 점박이가 나오고,

 

종종 터미널의 일부가 가리는 상황이 발생한다.

 

 

아래 명령어로 기존에 연결되어 있는 session들을 모두 detach 하고, 현재 화면에서만 해당 session을 렌더링 한다.

$ tmux attach -t SESSION_NAME -d

누가 봐도 -d는 detach라는 뜻이다.

 

집에서 해당 명령을 실행하면, 연구실 컴퓨터에 떠있는 tmux session이 꺼진다.

 

하지만 session 자체가 사라지지는 않기 때문에, 집에서 원하는 만큼 작업을 하고, 터미널을 종료한 뒤에

 

연구실에서 다시 attach를 하면 언제든지 작업 재개가 가능하다.

 

 

멋진 점이 있으면 귀찮은 부분도 있는 법.

 

내가 느꼈던 가장 큰 귀찮은 부분은 컴퓨터를 재부팅할 때마다 window, pane을 다 새로 만들어줘야 한다는 점이다.

 

보통 5~6개의 window를 띄워놓고,

 

각 window마다 많게는 4개씩 pane을 쓰며

 

각 pane은 서로 다른 서버들에 연결되어 있으므로

 

컴퓨터를 재부팅하라는 알림이 나오면 스트레스를 받게 된다.

 

이러한 번거로움을 해소하기 위해 몇 가지 함수들을 만들었다.

 

2021.04.24 - [잡기술/Ubuntu] - 터미널에서 함수 사용하기

 

터미널에서 함수 사용하기

웬만하면 파이썬 스크립트 내에서 반복적인 작업들을 해결할 수 있지만 가끔 어떤 main.py 파일을 한두개의 argument만 조금 바꿔서 반복적으로 실행해야 하는 경우가 있다. 예를 들자면 요즘 arbitrar

sanghyun.tistory.com

#!/bin/bash
divide_two()
{
    tmux split-window -h
}

divide_four()
{
    tmux split-window -h
    tmux split-window -v -t 0 
    tmux split-window -v -t 2
}

split-window는 -h와 함께 쓰이면 현재 window를 좌우, -v와 함께 쓰이면 상하로 분할한다.

 

-t N 을 추가하면 N번 pane에 대해서 해당 명령을 실행한다.

  • pane 인덱스 확인하기.
    prefix - q
  • 현재 선택된 pane 분할. -h는 좌우, -v는 상하. -t N 으로 target pane을 지정 가능.
    tmux split-window {-h or -v}

따라서 divide_two의 경우 현재 window를 좌우로 분할한다.

 

divide_four 같은 경우 현재 window를 우선 좌우로 분할하고, 0번 pane을 상하로 분할한다.

 

그런데 pane 인덱스의 경우 고정이 아니고, pane이 생성됨에 따라서 유동적으로 변한다.

Line 10이 실행된 직후 prefix -  q. 오른쪽 pane은 2번째로 만들어졌지만 유동적으로 2번 pane이 되었다.

따라서 우측 pane은 2번이 되며 2번 pane을 상하로 분할해서 총 4개의 pane을 만들게 된다.

 

divide_four가 실행된 직후의 화면은 아래와 같다.

화면이 4분할 된 모습.

 

다음은 login_all 함수인데, 이 함수가 필요한 배경을 우선 좀 짚어야 한다.

 

tmux에서 새로운 pane이나 window를 만들면, 해당 session이 실행 중인 컴퓨터의 ~/ directory가 기본으로 열린다.

 

그렇기 때문에 만약 어떤 pane을 특정 서버의 ssh 연결을 위해 쓰고 있는데,

 

같은 window에 새로운 pane을 만든다고 해도 그 pane은 ssh에 연결된 서버가 아닌 현재 컴퓨터의 ~/ 을 보여준다.

 

뭔가 주저리주저리 복잡해졌지만, 아무튼 pane이 n개 있으면 ssh 연결도 n번 해야 한다는 것.

 

그러면 tmux는 window를 늘어놓는 용도로만 쓰고, 각 window에는 하나의 pane으로 서버에 연결한 뒤

 

해당 서버에서 tmux를 다시 띄우면 화면 분할을 조금 더 편하게 할 수 있지 않을까?

tmux 안의 tmux? 이러고싶지는 않다.

아쉽게도 prefix는 가장 바깥, 즉 현재 컴퓨터에서 실행 중인 tmux session에만 전달된다.

 

서버와 현재 컴퓨터의 prefix를 서로 다른 것으로 정의하면 혹시 모르겠지만,

 

그렇게까지 복잡한 커맨드를 외우면서 작업 환경을 세팅하고 싶지는 않다.

 

 

대신에 그냥 한방에 로그인을 많이 할 수 있는 함수를 만들었다.

login_all()
{
    local pane=0
    while [ $pane -le $(($2-1)) ]
    do
        tmux select-pane -t $pane
        tmux send-keys "ssh $1"
        tmux send-keys ENTER
        pane=$(($pane+1))
    done
}

htop_nvidia()
{
    tmux select-pane -t 0
    tmux resize-pane -x 80 -y 20
    tmux send-keys "htop"
    tmux send-keys ENTER
    tmux select-pane -t 1
    tmux send-keys "watch -n 0.2 nvidia-smi"
    tmux send-keys ENTER
}

login_all두 개의 인수를 받는데, 하나는 ~/.ssh/config에 지정한 서버 이름 ($1)이고

2021.04.23 - [잡기술/Ubuntu] - SSH 쉽게 사용하기 (1) - .ssh/config

 

SSH 쉽게 사용하기 (1) - .ssh/config

현재 연구실 작업 환경이 [로컬 + 연구실 서버]를 중심으로 되어 있어, ssh 접속을 굉장히 자주 사용하게 된다. 하지만 이를 위해 접속 IP나 도메인을 매 번 입력해 주는 것은 번거로우며, 기억하기

sanghyun.tistory.com

나머지 하나는 로그인할 pane의 개수 ($2)다.

 

예) login_all my_server 4

 

기본적으로 $2번만큼 반복하는 while 문법이며,

  • N번 pane 선택.
    select-pane -t N
  • 현재 선택된 pane에 STRING 입력.
    send-keys "STRING"
  • 현재 선택된 pane에 COMMAND를 입력.
    send-keys COMMAND

을 잘 조합해서 만들었다.

 

풀어서 쓰면 각 pane마다 ssh 접속 명령어를 입력하고 ENTER를 치는 것을 반복하는 형식이라고 보면 된다.

 

 

내친김에 왼쪽 pane 2개에 htop과 nvidia-smi도 자동으로 띄워놓는 코드를 htop_nvidia처럼 작성할 수 있는데,

 

취향대로 크기 조절도 가능하다.

 

  • 현재 선택된 pane의 크기를 WIDTH x HEIGHT로 만듦.
    resize-pane -x WIDTH -y HEIGHT

굳이 htop이나 nvidia-smi가 아니더라도 원하는 프로그램은 뭐든 돌려놓을 수 있다.

 

 

마지막으로, 이렇게 정의한 함수들을 잘 조합해서 자신만의 tmux 레이아웃을 만들어놓고

 

tmux.sh 같은 파일에 저장해놓으면 이제 컴퓨터를 재부팅할 때마다 걱정할 필요가 없다!

export SESSION='0'

tmux new-session -d -s $SESSION

tmux rename-window 'homepage'
tmux select-window -t $SESSION:0
divide_two
login_all 'homepage' 1

tmux new-window -t $SESSION:1 -n 'local'
tmux select-window -t $SESSION:1
divide_four
htop_nvidia

tmux new-window -t $SESSION:2 -n 'local-sub'
tmux select-window -t $SESSION:2
divide_two

tmux new-window -t $SESSION:3 -n 'jupiter'
tmux select-window -t $SESSION:3
divide_four
login_all 'jupiter' 4
htop_nvidia

tmux select-window -t $SESSION:1
tmux attach -t $SESSION

몇 가지 확인만 하자면,

  • SESSION이라는 이름의 session을 detach 상태 (-d)로 만든다.
    new-session -d -s SESSION

해당 session을 background에 놓고, 다양한 명령어로 원하는 레이아웃을 구현한 뒤에 다시 attach 해주는 식으로

 

스크립트를 깔끔하게 만들 수 있다.

 

나 같은 경우에는 굳이 1번부터 window를 시작하지 않고, 0번에 조금 덜 쓰는 홈페이지 서버를 연결해두었다.

 

그 후에는

  • NAME이라는 이름의 window를 만든다.
    rename-window NAME
  • SESSION의 N번 window를 선택한다. select-pane과는 조금 다르게 session 이름을 지정해줘야 정상적으로 작동한다.
    select-window -t SESSION:N
  • SESSION에 NAME이라는 이름의 N번 window를 만든다. window 생성 시에 인덱스도 지정이 가능하네.
    new-window -t SESSION:N -n NAME

명령어들로 window를 잔뜩 만든 뒤에, 원하는 형태로 분할해놓고 필요하다면 htop과 nvidia-smi까지 실행하도록 했다.

 

마지막으로 1번 window를 select-window 명령으로 선택해주고 해당 session을 attach 하도록 하면

 

전체 스크립트를 실행시켰을 때 간편하게 내가 만들어놓은 레이아웃으로 복귀할 수 있다.

 

물론 해당 session이 돌아가고 있을 때, 다시 한번 이 스크립트를 실행시키면 난리가 날 수 있으니 주의.

 

조금 더 찾아보면 같은 이름의 session이 있을 때는 경고 메시지가 나오게 할 수 있지 않을까?

 

 

일단 지금은 패스...

 

암튼 tmux 잘 사용하기 끝.

이전 글:

2021.04.26 - [잡기술/Ubuntu] - 티먹스 tmux 사용해서 터미널 여러 개 띄우기 (1)

 

티먹스 tmux 사용해서 터미널 여러 개 띄우기 (1)

우분투에서 작업하다 보면 한 번에 터미널을 여러 개 켜놓아야 할 때가 있다. 예를 들어 실험을 여러 서버에서 돌린다거나, 한쪽에 코드를 열어놓고 다른 한쪽에서 해당 코드를 참조한다거나.

sanghyun.tistory.com

 

이전 포스트에서 tmux의 간단한 사용법을 정리해봤는데,

 

실제로 써보면 이것 저것 불편한 점이 있다.

 

 

처음 맏닥뜨리는 가장 큰 장벽은 스크롤이다.

 

이전 로그를 보고싶어서 화면을 스크롤하면, 터미널 입력창에 과거에 입력했던 명령어들만 쭉 스크롤된다.

 

아래와 같이 하면 된다.

  • 화면 스크롤 모드 시작. 해당 모드에서는 PgUp/Down, 마우스 휠로 화면을 스크롤할 수 있다. 스크롤 모드를 종료할 때는 <[ctrl] + c>를 입력하면 된다.
    prefix - [
  • 화면 스크롤 모드 시작하며 위로 한 페이지 스크롤. 실제로는 이걸 더 많이 쓴다.
    prefix - PgUp

tmux에서 아무 생각 없이 터미널 내용을 복사하려고 긁으면, pane 단위가 아니라 window 단위로 선택이 되는 걸 알 수 있다.

 

아무도 이런 복사 기능을 원하지 않는데, tmux에서 제대로 복사를 하는 방법은 조금 복잡하다.

  1. prefix - [
  2. 복사를 시작하고 싶은 위치로 커서 이동
  3. <[ctrl] + [space]> 로 선택 모드 활성화
  4. 화살표로 복사하고 싶은 영역 선택
  5. <[alt] + w> 로 복사
  6. prefix + ] 로 터미널이나 vim 등의 에디터에 붙여 넣기

정리해놔도 더럽게 복잡하다.

 

특히 6에서 터미널 에디터 대신 클립보드에 복사하려면 뭔가를 설치해야 한다고 하는데

 

정말로 내 취향이 아니므로 패스.

 

다행히 그럴 일은 거의 없었다.

 

 

아예 마우스를 쓰고 싶다면, ~/.tmux.conf 파일을 열어서 아래와 같이 입력한다.

 

해당 파일은 기본 생성이 아니기 때문에, 없으면 만들면 된다.

set -g mouse on
set -g history-limit 10000

Line 1이 있으면, pane을 클릭하는 것으로 이동할 수 있고, 마우스 휠로 스크롤 모드를 시작할 수 있다.

 

심지어 pane 경계를 드래그하면 pane 사이즈 조절도 된다.

 

Line 2는 스크롤 가능한 history를 늘려준다.

 

default로는 약간 부족할 때가 있기 때문에, 설정해놓으면 좋은 듯.

  • 현재 실행 중인 session에 업데이트된 .tmux.conf를 적용하려면 아래와 같은 순서로 입력한다.
    prefix - : - "source-file ~/.tmux.conf" - [enter]
  • 현재 session을 실행 중이지 않다면, 터미널에 아래와 같이 입력한다.
$ tmux source-file ~/.tmux.conf

개인적으로 마우스 기능은 쓰지 않는데,

 

이러면 굳이 키보드만으로 작업을 하기 위해 vi나 tmux 등에 익숙해진 의미가 없지 않냐는 좀 구닥다리 같은 이유다.

 

언젠가 정말 필요하면 사용하겠지...

 

 

사실 여기 소개되어있는 여러 가지 명령어들은 전부 다 ~/.tmux.conf에 입력하는 대신,

 

터미널에서 $ tmux [명령어]의 조합, 혹은 prefix - : - [명령어] - [enter]로도 가능하다.

 

다시 말해 ~/.tmux.conf는 현재 설정이 저장되어 있는 파일이라기보다는,

 

단순히 tmux 설정을 바꾸기 위해 실행하는 스크립트인 셈.

 

이 점을 인식하고 다시 생각해보면, 이미 활성화한 마우스 기능을 끄려면 단순히 해당 Line을 지우는 것이 아닌

set -g mouse off

를 명시해놓아야 한다.

 

 

tmux의 window는 기본적으로 0번부터 시작한다.

 

지극히 합리적인 디자인이긴 하지만, 0번과 1번 window를 왔다 갔다 할 때

 

prefix - 0과 prefix - 1은 생각보다 손의 동선이 불편하다.

 

아래 Line을 ~/.tmux.conf에 추가하면 window 번호가 1번부터 시작한다.

set -g base-index 1

 

~/.tmux.conf 파일은 생각보다 다양하게 커스텀 가능하다.

 

기존 설정된 단축키를 바꾸는 것이 메인디쉬인 듯한데, 나는 거의 항상 순정 커맨드를 선호하므로 딱히 바꾸지는 않았다.

 

그래도 정리해놓으면 필요할 때가 있겠지.

 

필요에 따라 아래 라인들을 ~/.tmux.conf에 적절히 추가하면 된다.

# prefix를 <[ctrl] + b>에서 <[ctrl] + a>로 변경하는 예시.
# C-b는 C와 b를 같이 누른다는 뜻인데, C는 [ctrl]키를 의미한다 (대소문자 주의!).
unbind C-b
set -g prefix C-a
bind-key C-a send-prefix

# 좌우 분할 명령을 %에서 |로 바꾸는 예시.
unbind %
bind | split-window -v

# -n을 지정하면 prefix 없이 동작하는 명령어가 된다.
# M은 meta (혹은 [alt])키를 의미한다.
# prefix - 화살표 대신 <[alt] + 화살표>로 pane을 선택하는 예시.
bind -n M-Left select-pane -L
bind -n M-Right select-pane -R
bind -n M-Up select-pane -U
bind -n M-Down select-pane -D

 

여기까지 하면, 대충 필요한 설정들을 적재적소에 맞춰서 사용할 수 있을 것 같다.

 

그런데 tmux를 잘 쓰려면 아직도 생각해야 할 게 조금 더 남아있다.

 

(이 정도면 대체 왜 쓰는 건지? ㅎㅎ)

 

아마 열심히 세팅하고 잘 사용하다가 컴퓨터를 재부팅할 때가 오면 약간 긴장이 되기 시작할 텐데,

 

그건 다음 포스트에 정리해야지.

다음 글:

2021.04.29 - [잡기술/Ubuntu] - 티먹스 tmux 사용해서 터미널 여러 개 띄우기 (3) - session 관리

 

티먹스 tmux 사용해서 터미널 여러 개 띄우기 (3) - session 관리

이전 글: 2021.04.26 - [잡기술/Ubuntu] - 티먹스 tmux 사용해서 터미널 여러 개 띄우기 (1) 2021.04.28 - [잡기술/Ubuntu] - 티먹스 tmux 사용해서 터미널 여러 개 띄우기 (2) - 스크롤, .tmux.conf tmux의 가장..

sanghyun.tistory.com

 

종종 기발한 아이디어를 떠올리다 보면,

 

"이걸 구현하기 위한 레이어가 파이토치에 있었나?" 라는 의구심이 든다.

 

다행히 세상이 좋아져서 대부분의 멋진 아이디어들은 쓰기 좋고 깔끔한 파이토치 구현체가 있지만,

 

정 안 되는 경우 내가 직접 만들어야 하는 경우가 생긴다.

 

파이토치의 custom layer는 만들기 어려운 편은 아니지만,

 

효율 등의 문제로 CUDA 프로그래밍이 필요하다면 문제가 많이 복잡해진다.

 

지난 CVPR을 준비하며 CUDA 프로그래밍을 동반한 custom layer를 만들어보았는데,

 

언젠가 또 비슷한 걸 만들지 모르니 정리해두는 게 좋겠다.

 

 

Toy example로 vector x와 y를 더하는 vecadd의 CUDA custom layer를 만들어보자.

 

(즉, z = x + y)

 

vecadd를 동작하게 만들 수 있다면, 훨씬 더 복잡한 연산도 문제없이 구현이 가능하다.

 

물론 연산 자체를 C++로 어떻게 짜야하는지 모르겠다면 그건 어쩔 수 없지만...

 

 

아무튼 전체 프로젝트는 아래 다이어그램처럼 구성된다.

파이토치 CUDA custom layer 프로젝트 다이어그램 (수정).

뭐가 이렇게 많나 생각이 들지만, 구조를 이해하면 나름 합리적이다.

 

(그렇다고 내가 각각의 디테일을 다 이해한 것은 아니다.)

 

 

가장 먼저 하면 좋은 것은 CUDA 커널을 작성하는 것이다. (사실 가장 어려운 일이기도 하다.)

 

직접 해 보면 나머지 파일들은 대부분 boilerplate 코드를 적절히 수정하는 것이지만

 

CUDA 커널은 layer의 핵심 로직이 담겨있는 것과 동시에 상당히 낯선 프로그래밍 스킬을 요구한다.

 

조금 복잡한 연산을 구현하고자 하는 경우에는,

 

파이썬 등으로 해당 로직을 충분히 검증한 뒤 CUDA 커널을 작성하는 게 시간을 아끼는 길이다.

 

안타깝게도 나는 CUDA 프로그래밍을 지난 CVPR을 준비하며 처음 해 보아,

 

이것저것 다양한 잡기술들을 적어놓을 재주가 없다.

 

그렇기에 일단 최대한 작동하는 무언가를 만들어놓고,

 

거기서 하면 안 되는 것, 주의할 것 등을 적어놓는 게 나중에 참조하는 데에 있어 훨씬 유용할 것 같다.

 

아 참, 일단 CUDA 프로그래밍을 하려면 시스템에 CUDA가 설치되어 있어야 한다.

 

다른 사람들이 열심히 정리해 놓았으니 그 부분은 각자 알아서.

 

 

일단 아무 이름으로 프로젝트 폴더를 만들고, 그 아래에 cuda라는 이름의 폴더를 추가로 만들자.

 

CUDA 커널 관련한 모든 코드는 그 폴더에 넣는다.

 

CUDA 프로그래밍을 처음 접하면 조금 헷갈리는 것이,

 

CUDA 커널 자체와 CUDA 커널을 호출하는 함수 2개를 짜야한다.

 

두 함수의 구현은 .cu 파일에 넣고, CUDA 커널을 호출하는 함수의 정의만 .cuh 파일에 넣는다.

 

따라서 vecadd_kernel.cuh 파일은 아래와 같이 작성할 수 있다.

// vecadd_kernel.cuh
#ifndef VECADD
#define VECADD

void VecAddCuda(
    const float* x,
    const float* y,
    float* z,
    const int n
);

#endif //VECADD

기본적으로 파이토치 텐서도 메모리 레이아웃상에서는 C의 배열과 동일하기에 float* 형태로 인수를 전달받는 게 편하다.

 

PyTorch의 C++ wrapper를 사용하는 방법도 있지만, 밑준비가 배로 늘어나므로 개인적으로는 이 방법이 훨씬 쉬운 것 같다.

 

그리고 z = x + y를 구현하고 싶기 때문에, z만 const를 붙이지 않는다.

 

주의해야 할 것은 C의 배열 크기 정보에 직접 접근할 수 있는 방법이 없기 때문에, n을 같이 넘겨줘야 한다.

 

조금 더 정교한 프로그래밍을 하고 싶으면 x, y, z의 배열 크기가 다를 때, 자료형이 다를 때 적절한 예외 처리가 필요하지만

 

그건 조금 더 high-level에서 다루고, 일단 CUDA 코드 상에서는 모든 게 잘 갖춰져 있다고 가정하고 진행하는 게 속 편하다.

 

 

다음은 실제 함수를 구현하는 vecadd_kernel.cu 파일이다.

// vecadd_kernel.cu
#ifndef NUM_THREADS
#define NUM_THREADS 1024
#endif

#include <cuda.h>
#include <cuda_runtime.h>

#include "vecadd_kernel.cuh"

__global__ void VecAddCudaKernel(
    const float* __restrict__ x,
    const float* __restrict__ y,
    float* __restrict__ z,
    const int n
)
{
    int idx = blockIdx.x * blockDim.x + threadIdx.x;
    if (idx < n) {
        z[idx] = x[idx] + y[idx];
    }
    return;
}

void VecAddCuda(
    const float* x,
    const float* y,
    float* z,
    const int n
)
{
    int n_blocks = int((n + NUM_THREADS - 1) / NUM_THREADS);
    VecAddCudaKernel<<<n_blocks, NUM_THREADS>>>(x, y, z, n);
    return;
}

Line 2~9는 선언 부분이다.

 

특히 CUDA 프로그램 빌드를 위해서는 Line 6, 7의 cuda.h와 cuda_runtime.h를 include 해야 한다.

 

해당 파일들이 어디에 있는지 몰라도, 시스템에 CUDA가 설치되어 있으면 상관없다.

 

NUM_THREADS는 디바이스 특성인데 현재 딥러닝에 정상적으로 사용할 수 있는 디바이스들이면 

 

 

Line 11~23은 CUDA 커널이다.

 

GPU에서 돌아가는 코드이기 때문에, 기존 C++에서 볼 수 없었던 예약어들이 여럿 보인다.

 

잠시 뒤로 하고 Line 25~35를 보면 아까 헤더 파일에서 선언한 VecAddCuda의 실질적인 구현이 있다.

 

내용물을 보면 별거 없는 게, 단순히 CUDA 커널 VecAddCudaKernel을 호출하는 인터페이스 역할만을 한다.

 

여기서 조금 더 깔끔하고 이해하기 쉬운 프로그래밍을 하려면, grid - block - thread의 hierarchy를 알아야 한다.

 

warp라는 개념도 있는 것 같은데, 아주 빡센 최적화를 할 때 고려하는 요소 같으니 패스.

 

어차피 자꾸 봐도 헷갈리니까 여기에 정리해 놓자.

grid - block - thread hierarchy.

쉽게 말해 grid는 하나의 CUDA 커널마다 하나씩 할당되는 block들의 집합,

 

block은 같은 CUDA 코드를 실행시키는 thread의 집합이다.

 

더 자세한 설명은 stackoverflow나 구글이 알려줄 것.

 

그림에서는 block과 thread가 2D 배열처럼 놓여있지만

 

이는 일단 프로그래밍의 편의를 위한 것으로 1D, 2D, 3D 중 원하는 인덱싱 방법을 고를 수 있다.

 

(여기에서는 1D로 놓고 했다. 2D, 3D도 써봤는데 나중에 정리해야지.)

 

다만 thread의 경우 한 block 안에 NUM_THREADS (=1024) 개가 최대이니 잘 배치해보자.

 

 

잠시 코드로 돌아가면, Line 32의 n_blocks는 우리가 실행시킬 block의 개수이다.

 

하나의 block에 NUM_THREADS개의 커널을 실행시키고

 

총 n번의 커널 호출 (element-wise 덧셈)이 필요하므로 이에 맞춰서 필요한 block 수를 계산한다.

 

이를 이용해서 Line 33에서 <<< >>> 표현을 통해 CUDA 커널을 호출하는데,

 

n_blocks * NUM_THREADS 개의 커널이 호출된다.

 

n이 NUM_THREADS의 배수가 아닌 경우 노는 커널이 있을 수 있다는 걸 알아놓자.

 

 

Line 11에서 __global__로 시작하는 부분은 해당 함수가 GPU 상에서 실행된다는 걸 명시한다.

 

또한 인수 부분의 __restrict__는 __restrict__가 붙은 인수들이 메모리상에서 독립적이라는 걸 의미하며,

 

프로그램 최적화에 도움을 준다고 한다.

 

막상 작성할 때는 습관적으로 넣었지만, 정리하며 다시 생각해보니 z = x + y에서

 

x, y, z가 항상 서로 다른 메모리 주소를 가리킨다는 보장이 없으므로 빼는 게 맞는 것 같다.

 

(예: x = x + x)

 

그냥 이런 것도 있구나 하고 넘어가면 될 듯.

 

 

그다음은 현재 커널의 인덱스를 구해줘야 한다.

 

표현이 조금 헷갈리기 쉬운데,

 

blockIdx.x는 이게 x 방향 (1D)으로 몇 번째 block인지 (block마다 다름),

 

blockDim.x는 한 block 안에 x 방향으로 총 몇 개의 thread가 있는지 (하나의 커널에서는 항상 동일),

 

마찬가지로 threadIdx.x는 해당 block에서 x 방향으로 몇 번째의 thread인지를 나타낸다.

 

쉽게 말해 Idx는 해당 방향에서의 절대적 위치, Dim은 하위 요소가 해당 방향으로 몇 개나 들어가 있는지를 의미한다.

 

따라서 현재 커널의 인덱스는 Line 18처럼 구할 수 있다.

 

만약 block 안에 64 * 16같이 2D로 thread를 배치하였으면 아래와 같이 커널 인덱스를 구하면 될 것 같다.

int idx = blockIdx.x * (blockDim.x * blockDim.y) + threadIdx.x * blockDim.x + threadIdx.y;

여기에 조금 더 구체적인 예시가 있으니 참고.

 

Line 19에서는, 해당 커널이 유효한 커널인지를 확인 후에 Line 20에서 실제 연산이 이루어진다.

 

 n_blocks * NUM_THREADS 개의 커널이 호출되므로, 이게 n보다 크다면 위에 말한 대로 남는 커널은 그냥 return.

 

하나의 커널이 x, y, z 텐서의 하나의 element를 담당하며 덧셈을 수행한다.

 

물론 최적화나 기타 구현 상의 이유로 하나의 커널에서 여러 개의 element에 접근해도 전혀 문제가 없다.

 

중요한 것은 n개의 커널이 n개의 결과를 온전히 만드는 것.

 

 

일단 이렇게 하면 가장 중요한 부분인 CUDA 프로그래밍은 어느 정도 완료되었다.

 

다음에는 이렇게 작성한 커널들을 빌드하고 PyTorch에 연결하는 작업이다.

 

다음 글:

2021.04.30 - [코딩/PyTorch] - 파이토치 PyTorch CUDA custom layer 만들기 (2) - 커널 빌드 및 Python 바인딩

 

파이토치 PyTorch CUDA custom layer 만들기 (2) - 커널 빌드 및 Python 바인딩

이전 글: 2021.04.28 - [코딩/PyTorch] - 파이토치 PyTorch CUDA custom layer 만들기 (1) - CUDA 커널 파이토치 PyTorch CUDA custom layer 만들기 (1) - CUDA 커널 종종 기발한 아이디어를 떠올리다 보면, "이걸..

sanghyun.tistory.com

 

논문 작업을 하며 매번 느끼는 거지만, figure는 파워포인트로 만드는 게 최고로 효율이 좋다.

 

재린이는 Inkscape가 좋다고 자꾸 영업을 시도하지만 (그리고 확실히 좋아 보이긴 하지만),

 

파워포인트의 범용성과 익숙함을 생각하면 넘어가기가 쉽지 않다.

 

딱 한가지 아쉬운 게 있었는데, ppt의 수식 렌더링이 LaTeX과 미묘하게 다른 것.

 

이 문제에 대해 얼마 전 승준이 형이 꽤 괜찮은 해결책을 찾아내셨다.

 

  1. 일단 여기에 가서 IguanaTex의 .ppam 파일을 다운로드 받는다.
  2. 만약 LaTeX이 설치되어 있지 않다면, 일단 TeXstudio부터 다운로드해야 한다. IguanaTex이 무슨 대단한 거라기보다는 해당 PC에 설치된 LaTeX 렌더링 파이프라인을 파워포인트에서 접근 가능하게 해주는 보조적인 도구에 가깝기 때문이다.
  3. 파워포인트를 켜서, 왼쪽 위 파일 - 왼쪽 아래 옵션을 차례대로 클릭한다.
  4. 옵션 창에서 추가 기능 - 관리 - PowerPoint 추가 기능 - 이동(G)을 차례대로 클릭한다.

  1. 새로 설치(A)...를 누르고 아까 받은 .ppam 파일의 위치를 지정하면 완료. 이제 파워포인트 리본 메뉴에서 IguanaTeX 항목을 확인할 수 있다 (공식 명칭은 IguanaTex인데 실제로는 IguanaTeX로 나온다.).

IguanaTeX 리본 메뉴가 추가된 모습.

 

본격적으로 사용하기 전, 한 가지 신경 써줄 것이 있다.

 

파워포인트로 figure를 그리는 가장 큰 이유는 벡터 그래픽을 손쉽게 만들 수 있다는 점인데,

 

별도의 설정을 하지 않은 경우 IguanaTex은 비트맵 그래픽으로 수식을 렌더링 한다.

 

벡터 그래픽으로 수식을 렌더링 하기 위해, 이곳에서 TeX2img 2.2.1 (작성일 기준)을 다운로드하자.

 

받은 파일의 압축을 풀고, IguanaTeX 리본 메뉴에서 Main Settings를 클릭한다.

Main Settings 화면.

해당 화면의 아래에서 두 번째 칸, Set full path to TeX2imgc (vector output):

 

방금 다운로드한 압축 파일 안에 있는 TeX2imgc.exe의 경로를 지정한다.

 

파일 이름을 정확히 확인해야 한다.

 

나 같은 경우는 ImageMagick은 원래 깔려있어서 신경을 쓰지 않았지만,

 

해당 프로그램도 추가로 까는 것을 권장한다고 하니 확인해보자.

 

 

이제 IguanaTeX 메뉴의 New LaTeX display를 눌러서 LaTeX-compatible 한 수식을 입력할 수 있다.

 

사실상 외부 LaTeX 플러그인을 연결해주는 개념이므로 패키지나 명령어를 제약 없이 쓸 수 있을 것 같다.

벡터 그래픽으로 넣으려면 LaTeX Engine: Vector로 설정한다.

폰트 사이즈를 정하고, Generate를 누르면 수식이 삽입된다.

 

개인적으로는 폰트 사이즈를 정하는 게 약간 불편했는데, 임의의 파워포인트 텍스트를 선택하고

 

New LaTeX display를 누르면 선택한 영역과 같은 폰트 사이즈를 기본으로 설정해준다.

 

벡터 그래픽을 렌더링 하기 위해서는 오른쪽 위의 Bitmap을 Vector로 바꿔줘야 한다.

 

그런데 벡터 그래픽에는 폰트 사이즈가 제대로 반영되지 않으니,

 

사이즈를 정확히 맞추고 싶으면 Bitmap으로 렌더링 후 IguanaTeX 메뉴의 Vectorize selection을 눌러

 

Bitmap을 벡터로 바꿔준다.

 

아래 사진은 IguanaTex로 렌더링 한 결과와 파워포인트 기본 수식 입력을 비교한 것이다.

IguanaTex vs . PowerPoint

지금까지 별 신경을 쓰지 않고 파워포인트를 썼지만,

 

이렇게 직접적으로 비교를 하니 다시는 기본 수식 입력기를 쓸 일이 없을 것 같다.

 

특히 파워포인트의 x는 알파벳이라기보다 그리스 문자 카이χ에 가까운 모양인데,

 

중학교 시절 수학 선생님이 x를 저렇게 쓰지 말라고 누누이 강조하신 게 문득 떠올라

 

저런 수식 입력기를 아무 생각 없이 썼던 자신을 반성하게 만든다.

 

y나 z, 기호의 두께 등 다른 요소들도 차이가 많이 나는 모습을 확인할 수 있다.

 

 

이제 이렇게 렌더링 된 수식을 figure 주변에 잘 배치해주면 된다.

 

끝.

마땅한 배너가 없어서 나무위키에서 가져옴.

it takes two

장르: 협동, 퍼즐, 3D 플랫포머, 3D 액션 어드벤처

플랫폼: PC (steam, origin), PS, XBOX

정가: 44,000원

권장 사양: Geforce GTX 960

플레이 시간: 10~15시간

추천 대상: 게임 좀 해본 커플/친구

 

개인적인 점수 (절대적 기준은 없음, 10점 만점)

그래도 숫자를 매기는 건데

점수 기준은 어딘가에 따로 정리해야겠다.

 

그래픽: 9

게임성: 9

몰입감: 10

스토리: 8

편의성: 7

 

종합: 9 (강추)

 

 

 

 

 

토요일에 몬헌 라이즈 훈장 작업을 열심히 하며 똑같은 퀘스트를 뺑뺑이 돌다가,

 

문득 이게 뭐지 싶어 잠시 쉬는 와중 형준이가 카톡방에 재밌어 보이는 게임을 가져왔다.

 

그렇게 무거워 보이지도 않으면서, 익살스러운 조작과 액션성이 재밌어 보여서

 

조금 고민하다가 바로 해보기로 했다.

 

 

사실 이 게임의 최대 단점은 무조건 2인이 필요하다는 건데,

 

일반적인 협동 게임이 혼자 해도 진행이 가능하지만, 2인이 하면 더욱 재미있는 구조라면

 

이 게임은 2인이 아니면 시작도 할 수 없다 (일단 마우스가 2개 필요한가?).

 

그러니까 형준이가 이 게임을 들고 온 이유도 설명할 필요가 없을 정도로 굉장히 명확하다.

 

 

내가 디스코드에서 이거 해볼까...라는 말을 끝내기 무섭게 형준이가 구매를 완료하였고

 

이 게임은 둘 중 한 명만 소유하고 있어도 별도의 클라이언트를 사용한 2인 플레이가 가능하기에

 

졸지에 공짜로 게임을 하는 기회가 되었다.

 

 

이 게임을 해볼까 고민했던 이유가 굳이 하나 있다면

 

주제 자체가 부부 사이의 갈등을 봉합하고 행복한 가정을 만든다는,

 

남자 2명이서 하기에 조금 어색할 수 있는 내용이었기 때문이다.

 

따라서 남캐와 여캐를 각각 1명씩 담당하여 플레이하게 되는데,

 

일단은 형준이가 실질적 소유주이기 때문에 먼저 고르라고 했다.

 

사실 아무래도 상관없었던 것 같은 게,

 

결국에는 형준이가 뜸을 들이는 도중 여캐를 내가 먼저 고르게 되었다.

 

 

게임은 지극히 디즈니 같은 엉뚱한 창의력으로 시작된다.

 

코디와 메이는 볼 때마다 싸움을 반복하며 이혼을 앞둔 부부이다.

 

이에 부모님이 화목해지기를 바라는 딸 로즈가 어딘가에서 주워온 마법서로 주문을 건다.

저 책, 실제로 보면 굉장히 짜증난다. 출처: 공식 사이트

주문에 걸려 찰흙, 나무 인형으로 변한 부부의 앞에 나타난 건

 

자신을 사랑 전문가라고 주장하는 잔망스러운 책 Dr. 하킴.

 

둘이 협동하여 사이좋은 관계를 회복하지 않으면 본래의 몸으로 돌아갈 수 없다는 그런 이야기다.

 

 

아래의 초반 플레이 영상을 보면 알 수 있듯이

 

이런 익살스러운 주제를 따라 그래픽 또한 애니메이션 풍의 귀여운 스타일이다.

공식 게임플레이 트레일러.

플레이하면서 느낀 것은, 색감이 아주 깔끔할 뿐만 아니라 광원, 질감 표현 등이 굉장히 괜찮다는 것이다.

 

나는 스위치 게임을 즐겨 하기에 게임 그래픽에 대한 역치가 그리 높지 않은데,

 

그런 눈으로 봐도 그래픽 및 아트워크에 상당히 많은 공을 쏟은 게임인 것 같았다.

명부와 암부 표현, 그림자, 연출 등 공을 많이 들인 티가 난다. 직접 촬영.

 

 

사실 나는 게임에서 그래픽보다는 플레이 경험을 훨씬 중요시하는데,

 

그래픽 이상으로 만족스러웠던 건 게임이 주는 즐거움이었다.

 

굳이 장르를 써놓기는 했지만 이 게임의 플레이 방식은 하나로 고정되어 있지 않고,

 

스테이지마다, 그 안의 세부 장소마다 변화한다.

갑자기 분위기 디아블로? 직접 촬영.

어떤 곳에서는 2D 횡스크롤 같은 느낌, 어떤 곳에서는 탑뷰, 종종 등장하는 미니게임 등

 

이런 플랫포머에서 쉽게 느낄 수 있는 반복에 대한 지루함을 상당히 효과적으로 억제하는 수단이 된 것 같다.

 

각 파트가 그렇게 크지 않아 나무위키에 쓰여있듯 "수십 가지"의 게임 장르가 들어있다고 말할 정도는 안되지만,

 

우당탕 즐거운 느낌을 주기에 좋은 선택이었던 것 같다.

 

 

딱 하나의 장르로 이 게임을 정의해보라면

 

"협동 퍼즐" 이 적당할 것 같다.

 

안 그래도 요즘 방탈출을 열심히 다니는데 (최근에는 좀 뜸하지만)

 

디지털 방탈출을 하는 것 같은 느낌도 들고 좋았다.

 

특히 퍼즐을 풀다가 막히는 상황에 연출되는 각본 없는 코미디는 이런 류 게임을 더욱 즐겁게 만들어준다.

여기에 더빙이 들어가 있다는 것은, "너네 어차피 여기서 한 번 막힐거잖아"라는 제작진의 의도? 이러한 해프닝이 적지 않다. 직접 촬영.

 

장르적 요소뿐만이 아니라 협동에 관련한 레벨 디자인도 상당히 괜찮다.

 

남캐와 여캐가 나뉘어 있지만 어느 한쪽이 주도적인 역할을 하지 않고,

 

둘이 동등한 기여를 해야지 스테이지를 클리어할 수 있다.

 

하나의 화면을 두 개로 나눠서 남의 플레이도 같이 볼 수 있는 형태로 만든 것도

 

(개인적으로 이 시스템은 유효 시야가 좁아져서 약간 아쉬움.)

 

한 사람의 사각을 다른 한 사람이 쉽게 커버할 수 있게 하려는 의도일 것이다.

 

 

난이도 조정도 절묘하다.

 

사실 쌩으로 플레이할 때, 결코 쉬운 게임은 아니고 오히려 어려운 축에 속한다.

 

어지간한 몹에게는 2대 정도만 맞으면 바로 죽고, 스테이지 곳곳에 낙사를 유발하는 지형이 수두룩하다.

 

그럼에도 일반적인 상황에서는 목숨이 무한정이고,

 

보스전 등 긴박감이 필요한 부분에서는 둘 다 동시에 죽지 않으면 게임 오버가 되지 않는다.

 

(쉽다는 뜻은 아니다. 나랑 형준이는 그래도 나름 짬이 있는데, 적지 않은 게임 오버를 당했다.)

 

게임 오버가 되더라도 자동 저장 체크포인트가 굉장히 촘촘해서,

 

거의 항상 직전의 상황에서 다시 시작한다.

 

요즘 게임에서 어려운 것 중 하나가 난이도 조절인데,

 

적절한 긴장감을 주면서도 특정 구간에서 턱 막히지 않게 해 둔 설계가 아주 좋다.

 

게임을 전혀 해보지 않은 사람이 하기에는 확실히 난이도가 있기 때문에 영업에는 주의가 필요할 듯.

 

 

게임 중 좋았던 경험 중 또 하나는 몰입감이다.

 

우선 챕터의 개념이 모호해서, 게임 플레이가 중간에 끊긴다는 느낌이 전혀 들지 않는다.

 

물론 공간적 배경이 확 바뀌거나, 중간에 컷신이 들어간다거나 해서

 

"우리 이제 대충 다음 챕터로 갈 거야"라는 뉘앙스는 풍기지만

 

그 전환이 굉장히 자연스럽고 밀도가 높아서 뭔가 중간에 놓기 힘든 애매한 상황을 유발한다.

 

실제로 게임을 플레이 시작한 게 토요일-일요일 넘어가는 새벽이고,

 

정신 못 차리고 4시간을 한 뒤에 일요일에 3시간-휴식-4시간 정도 해서 클리어하였을 정도다.

 

사실 이런 느낌은 실제로 플레이해보지 않으면 느끼기 힘든데,

 

스피드감이 중요하고 늘어질수록 지루한 플랫포머 계열에 아주 잘 맞는 몰입감이었다.

 

 

스토리는 사실 조금 뻔하고 어디에나 있을만한, 그런 내용이다.

 

창의력이 많이 가미되긴 했지만 주제 자체는 결코 참신하지 않다고 생각한다.

 

다만 이를 전달하는 방식,

 

중간중간에 들어간 컷신의 퀄리티나 연출 등이 나름 스토리를 극적으로 만들어준다.

 

그리고 다행히 처음에 우려했던 "이혼 직전인 부부의 이야기" 라는 부분은

 

남자 두 명의 게임 몰입도에 부정적인 영향을 거의 주지 않았다.

 

별개로 개인적으로 비슷한 주제에 조금 몰입할만한 일이 있어서 그런지,

 

플레이 도중 스스로를 약간 숙연한 기분으로 반성하는 시간을 갖기도 했다.

 

참고로 12세 이용가지만 은근히 폭력적이니 이 부분도 약간 주의를.

 

만약 10세 정도의 아이를 데리고 한다면 (요즘 애들 게임 잘한다.),

 

정서상 아주 약간 안 좋을 수 있는 부분이 있다.

슈퍼 마리오 시리즈의 오마쥬? 직접 촬영.

 

굳이 약간 아쉬웠던 부분을 꼽자면 꼭 2인이 필요한 플레이 방식.

 

물론 나는 함께 할 사람이 있어서 괜찮았지만,

 

2인 이서 로컬로 하는 경우가 아니면 싱크/통신 문제가 있을 수도 있고,

 

(실제로 전체 플레이 동안 2번 튕겼다. 별로 많이 튕긴 것은 아닌 듯 하지만.)

 

애초에 2인을 모으기 힘든 사람도 세상에는 많다.

 

중간중간에 3인칭 슈팅으로 변환되는 부분도 마우스 감도가 오락가락해서 조금 불편했던 기억이 있다.

 

(어디에서는 사용자 편의를 위한 자동 록온 기능이라고 하는데, 이 부분은 약간 튜닝이 필요할 듯)

 

추가로 위에 언급했지만 게임 중 거의 대부분의 시간을 반쪽짜리 화면을 보며 플레이하는데,

 

평소에 보던 16:9 스크린을 온전히 활용할 수 없다는 게 약간 아쉽기도 했다.

 

후반부로 갈수록 게임이 익숙해지면 신선한 기믹이 적어지고,

 

약간 뻔한 퍼즐들이 많이 나오는 것,

 

다회차나 파고들기 요소가 전혀 없는 것 등은 어쩔 수 없는 장르의 한계인 것 같다.

 

(뜬금없지만 이런 부분에서 마리오 오디세이는 엄청나게 잘 만든 게임이다.)

 

그럼에도 불구하고, 게임이 늘어지고 지루해지기 이전 기분 좋은 템포로 엔딩 크레딧을 볼 수 있지만.

 

 

아무튼 굉장히 잘 만든 게임을 기분 좋게 즐겼다.

 

메타크리틱 88점을 받은 이유가 충분히 납득이 간다.

 

예전에는 묵직하고 깊이 있는 게임이 좋았지만, 요즘 들어 경쾌한 템포로 "끝"을 볼 수 있는 게임이 좋다.

 

덕분에 즐거운 주말을 보냈으니 이번 주도 화이팅!

우분투에서 작업하다 보면 한 번에 터미널을 여러 개 켜놓아야 할 때가 있다.

 

예를 들어 실험을 여러 서버에서 돌린다거나,

 

한쪽에 코드를 열어놓고 다른 한쪽에서 해당 코드를 참조한다거나.

 

 

그런데 단순히 터미널을 여러 개 틀어놓는 것은 생각보다 불편하다.

 

가장 큰 이유는 창간의 이동을 키보드로 하기 썩 편하지 않다는 것인데,

 

alt + tab 신공을 사용해서 어찌어찌 해결한다 해도

 

레이아웃이 이쁘지 않다든지, 더 많은 창을 띄우려면 머리가 많이 복잡해진다든지 하는 문제가 있다.

 

이를 해결하기 위한 많은 방법이 있지만, 내 기준으로는 티먹스 tmux가 가장 편하고 용도에 맞는 것 같다.

 

다른 블로그에도 관련된 많은 포스트가 있지만,

 

직접 사용해보며 가장 많이 썼던 핵심 명령어들만 정리해보려고 한다.

 

 

일반적인 우분투 프로그램과 같이, 아래 명령어로 설치한다.

$ sudo apt-get install tmux

실행하는 방법은 몹시 간단하다.

$ tmux

tmux 실행 화면.

순정 bash에는 없던 상태 표시줄이 생겼다.

 

tmux의 대부분의 기능은 prefix라는 특정 커맨드와 후속 명령어를 연속해서 입력하는 것으로 실행할 수 있다.

 

예를 들어, 기본 prefix는 <ctrl + b>이고 현재 창을 좌우로 분할하는 명령은 % 이다.

 

따라서 <[ctrl] + b>와 %를 순서대로 입력하면 아래와 같은 결과를 얻을 수 있다.

창이 좌우로 분할된 모습.

가끔 WSL (Windows Subsystem for Linux)을 통해 tmux를 실행하는데,

 

WSL과 tmux의 호환성은 썩 좋은 편이 아니다.

 

가운데의 세로 선이 조금 잘려 나온다거나, 나중에 설명하겠지만 특정 상황에서 글자가 조금 깨지기도 한다.

 

 

아무튼, tmux에서는 저렇게 구분된 하나의 터미널을 pane이라고 하고,

 

pane과 관련된 명령어들은 아래와 같다.

 

여기서 -로 구분된 커맨드들은 순서대로, <a + b>는 a와 b를 동시에 입력한다는 의미이다.

  • 현재 pane을 좌우로 분할.
    prefix - %
  • 현재 pane을 상하로 분할.
    prefix - "
  • pane 사이에 커서 (focus) 이동.
    prefix - 방향키
  • 현재 pane 닫기. pane에 커서가 있을 때 prefix 없이 <[ctrl] + d>로도 가능하다.
    pane을 닫는 경우, 해당 pane에서 실행 중인 프로그램은 날아간다.
    prefix - x
  • 해당 방향으로 현재 pane을 밀어냄 (크기 변경).
    alt 대신 ctrl로 더욱 섬세한 조정이 가능하다.
    prefix - <[alt] + 방향키>

몇 번 해보면 금세 적응이 된다.

 

tmux에서는 저런 pane들이 하나 이상 놓여있는 창을 window라고 한다.

 

아래 상태 표시줄에는 선택된 윈도우*가 몇 번인지도 나와있다.

 

예를 들어 위 스크린샷은 pane이 2개 있는 0번 윈도우가 선택되어 있는 경우라고 볼 수 있다.

 

window와 관련된 명령어들은 아래와 같다.

  • 새로운 window를 추가.
    prefix - c
  • n번 window로 이동 (n은 숫자).
    prefix - n
  • n번 window로 이동 (n이 10 이상). 총 4단계를 거쳐야 한다. 웬만하면 10개 이하의 window를 쓰자.
    prefix - ' - n - [enter]
  • 현재 window 닫기. pane이 하나만 있는 window의 경우, prefix 없이 <[ctrl] + d>로도 가능하다.
    prefix - &
  • 현재 윈도우 이름 수정. window가 3개만 되어도 각각의 역할이 헷갈리기 때문에, 은근히 중요한 기능이다.
    prefix - , - [이름 입력] - [enter]

3개의 윈도우를 만든 모습. 현재 선택된 윈도우*는 2번.

마지막으로, tmux에서는 1개 이상의 window를 모아놓은 객체를 session이라고 부른다.

 

session의 이름은 default가 0이고, tmux 상태 표시줄의 제일 왼쪽 [ ] 안에 적혀있다.

 

예를 들어 첨부한 캡처 화면의 경우는 1이 된다.

 

session을 여러 개 쓰는 경우 여러 개의 창을 띄워놓고 각각의 창에 다른 session을 만들면 되기 때문에

 

session 관련 명령어는 잘 쓰지 않는 편이다.

 

개인적으로 느낀 필수적인 session 관련 명령어들은 아래와 같다.

  • session detach 시키기. 해당 session에서 돌던 모든 프로그램들은 계속해서 돈다.
    prefix - d
  • detach 시킨 session 다시 접속하기. 해당 명령어는 터미널에서 입력한다.
    SESSION_NAME은 위에서 언급했던 session 이름.
$ tmux attach -t SESSION_NAME
  • session 종료하기. 생각보다 절차가 귀찮다. 불의의 사고로 session을 날려버리는 걸 막기 위해서?
    당연한 말이지만 session을 종료하면 해당 session의 모든 프로그램이 날아간다.
    마지막 window의 마지막 화면에서 prefix 없이 <[ctrl] + d>
    혹은 detach 후 터미널에서 아래와 같이 입력. SESSION_NAME은 위에서 언급했던 session 이름.
$ tmux kill-session -t SESSION_NAME

 

이 정도면, tmux 전체 기능의 75% 정도는 문제없이 사용할 수 있는 것 같다.

 

글이 너무 길어지면 번잡하니까 사용하며 조금 생각이 필요한 기능들 15%는 다음 글에 써야지.

 

다음 글:

2021.04.28 - [잡기술/Ubuntu] - 티먹스 tmux 사용해서 터미널 여러 개 띄우기 (2) - 스크롤, .tmux.conf

 

티먹스 tmux 사용해서 터미널 여러 개 띄우기 (2) - 스크롤, .tmux.conf

이전 글: 2021.04.26 - [잡기술/Ubuntu] - 티먹스 tmux 사용해서 터미널 여러 개 띄우기 (1) 티먹스 tmux 사용해서 터미널 여러 개 띄우기 (1) 우분투에서 작업하다 보면 한 번에 터미널을 여러 개 켜놓아야

sanghyun.tistory.com

나머지 10%는 외우기보다 사용하며 필요할 때 직접 찾아보는 게 나은 것 같다.

웬만하면 파이썬 스크립트 내에서 반복적인 작업들을 해결할 수 있지만

 

가끔 어떤 main.py 파일을 한두개의 argument만 조금 바꿔서 반복적으로 실행해야 하는 경우가 있다.

 

예를 들자면 요즘 arbitrary-scale SR 실험을 이것 저것 해보고 있는데,

 

x1.1~x4.0까지의 결과를 만들고 각 scale에서의 PSNR을 측정하는 작업을 일일히 하기에는 너무 번거로운 일이다.

 

 

bash의 함수 정의와 export 구문을 사용하면 아래와 같은 조악한 pseudo-code가 나온다.

#!/bin/bash

do_something () {
    python task_1.py --var1 ${VAR1} --shared1 SHARED1
    python task_2.py --var2 ${VAR2} --shared2 SHARED2
}

export VAR1="abc"
export VAR2="def"
do_something

export VAR1="ghi"
export VAR2="jkl"
do_something

SHARED1과 SHARED2는 고정된 argument이고, 같은 스크립트(들)을 --var1과 --var2를 바꿔가며 돌릴 수 있다.

 

다만 몇 가지 문제가 있는데,

  • export 구문으로 정의한 값이 system에 미리 할당이 되어 있던 경우 참사가 발생할 수 있음.
  • 하나의 do_something을 실행하는데에 3줄이나 필요해서 깔끔하지 않음.

정도가 내가 신경쓰였던 것이다.

 

 

아래와 같이 바꾸면 이런 문제를 해결할 수 있다.

#!/bin/bash

do_something () {
    python task_1.py --var1 $1 --shared1 SHARED1
    python task_2.py --var2 $2 --shared2 SHARED2
}

do_something "abc" "def"
do_something "ghi" "jkl"

신기하게도 bash function은 정의할 때 argument를 몇 개 받는지 명시해놓지 않는다.

 

$1과 $2는 각각 함수 호출시에 넣어준 argument들을 말한다.

 

 

여기서 조금 주의해야 할 것은 bash 스크립트 호출 당시에 사용한 argument 또한 $1, $2, ... 등으로 사용하는데

 

함수의 scope 내에서는 이 값들이 함수의 argument로 덮어씌워진다.

 

아래 예시를 보면 쉽게 알 수 있다.

#!/bin/bash

do_something () {
    echo $1
    echo $2
}

echo $1
do_something "123" "456"

위와 같은 example.sh 파일을 만들고 실행하면 다음과 같은 결과가 나온다.

$ bash example.sh "hi"
hi
123
456

script 실행시에 제공한 argument를 함수 내에서 사용하려면

 

export 구문을 조심스럽게 사용하거나, 아니면 함수의 argument로 $1, $2, ... 등을 넣어주면 될 것 같다.

터미널에서 간단한 코딩 작업을 할 때 은근히 필요한 게

 

다른 파일에서 특정 단어나 표현을 찾는 것이다.

 

 

예를 들어 함수 foo가 a.py에 정의되어 있는데,

 

원래는 2개의 인수를 받아 foo(x, y)처럼 동작하던 것을 3개의 인수를 받아 foo(x, y, z)처럼 사용하도록 수정했다고 하자.

 

다음과 같은 명령어로 현재 디렉터리의 파이썬 스크립트로부터 foo라는 텍스트의 위치를 찾을 수 있다.

$ grep -n foo ./*.py
# recursive하게 하위 폴더도 찾아보고 싶으면,
$ grep -nr foo --include="*.py"

 

foo 대신에 PixelShuffle 함수를 찾도록 하면 아래와 같이 나오더라.

rdn_f.py:76: nn.PixelShuffle(r),
rdn_f.py:82: nn.PixelShuffle(2),
rdn_f.py:84: nn.PixelShuffle(2),
msrn.py:43: nn.PixelShuffle(args.scale),

rdn_f.py의 76, 82, 84번째 줄에 해당 함수가 사용되었다는 뜻이다.

$ vi rdn_f.py +76

으로 바로 76번째 줄을 수정하러 갈 수 있다.

 

 

몇 가지 옵션이 있는데,

  • -n이 없으면 line number가 나오지 않는다. 웬만하면 line number가 필요하지 않을까?
  • -r과 --include가 있는 스크립트는 하위 폴더에도 똑같은 연산을 recursive 하게 수행한다.
    -r 대신에 -R을 사용하면 symbolic link도 따라간다고 하는데, 썩 좋은 생각은 아닌 것 같다.

 

막상 해당 단어를 찾기는 했는데, 한 줄만 딸랑 나와서 전후 맥락을 파악하기 힘든 경우에는

 

아래와 같은 방법도 있다.

# 앞으로 (Before) 3줄, 뒤로 (After) 2줄이 추가로 나온다.
# 빈 줄은 센스있게 제거해주더라.
$ grep -n foo ./*.py -A 2 -B 3

+ Recent posts