출처 : http://biz.chosun.com/site/data/html_dir/2010/12/10/2010121000690.html

Google Interview Questions: Product Marketing Manager

- Why do you want to join Google?

- What do you know about Google's product and technology?

- If you are Product Manager for Google's Adwords, how do you plan to market this?

- What would you say during an AdWords or AdSense product seminar?

- Who are Google's competitors, and how does Google compete with them?

- Have you ever used Google's products? Gmail?

- What's a creative way of marketing Google's brand name and product?

- If you are the product marketing manager for Google's Gmail product, how do you plan to market it so as to achieve 100 million customers in 6 months?

- How much money you think Google makes daily from Gmail ads?

- Name a piece of technology you’ve read about recently. Now tell me your own creative execution for an ad for that product.

- Say an advertiser makes $0.10 every time someone clicks on their ad. Only 20% of people who visit the site click on their ad. How many people need to visit the site for the advertiser to make $20?

- Estimate the number of students who are college seniors, attend four-year schools, and graduate with a job in the United States every year.

 

Google Interview Questions: Product Manager

- How would you boost the GMail subscription base?

- What is the most efficient way to sort a million integers?

- How would you re-position Google's offerings to counteract competitive threats from Microsoft?

- How many golf balls can fit in a school bus?

- You are shrunk to the height of a nickel and your mass is proportionally reduced so as to maintain your original density. You are then thrown into an empty glass blender. The blades will start moving in 60 seconds. What do you do?

- How much should you charge to wash all the windows in Seattle?

- How would you find out if a machine’s stack grows up or down in memory?

- Explain a database in three sentences to your eight-year-old nephew.

- How many times a day does a clock’s hands overlap?

- You have to get from point A to point B. You don’t know if you can get there. What would you do?

- Imagine you have a closet full of shirts. It’s very hard to find a shirt. So what can you do to organize your shirts for easy retrieval?

- Every man in a village of 100 married couples has cheated on his wife. Every wife in the village instantly knows when a man other than her husband has cheated, but does not know when her own husband has. The village has a law that does not allow for adultery. Any wife who can prove that her husband is unfaithful must kill him that very day. The women of the village would never disobey this law. One day, the queen of the village visits and announces that at least one husband has been unfaithful. What happens?

- In a country in which people only want boys, every family continues to have children until they have a boy. If they have a girl, they have another child. If they have a boy, they stop. What is the proportion of boys to girls in the country?

- If the probability of observing a car in 30 minutes on a highway is 0.95, what is the probability of observing a car in 10 minutes (assuming constant default probability)?

- If you look at a clock and the time is 3:15, what is the angle between the hour and the minute hands? (The answer to this is not zero!)

- Four people need to cross a rickety rope bridge to get back to their camp at night. Unfortunately, they only have one flashlight and it only has enough light left for seventeen minutes. The bridge is too dangerous to cross without a flashlight, and it's only strong enough to support two people at any given time. Each of the campers walks at a different speed. One can cross the bridge in 1 minute, another in 2 minutes, the third in 5 minutes, and the slow poke takes 10 minutes to cross. How do the campers make it across in 17 minutes?

- You are at a party with a friend and 10 people are present including you and the friend. your friend makes you a wager that for every person you find that has the same birthday as you, you get $1; for every person he finds that does not have the same birthday as you, he gets $2. would you accept the wager?

- How many piano tuners are there in the entire world?

- You have eight balls all of the same size. 7 of them weigh the same, and one of them weighs slightly more. How can you find the ball that is heavier by using a balance and only two weighings?

- You have five pirates, ranked from 5 to 1 in descending order. The top pirate has the right to propose how 100 gold coins should be divided among them. But the others get to vote on his plan, and if fewer than half agree with him, he gets killed. How should he allocate the gold in order to maximize his share but live to enjoy it? (Hint: One pirate ends up with 98 percent of the gold.)

- You are given 2 eggs. You have access to a 100-story building. Eggs can be very hard or very fragile means it may break if dropped from the first floor or may not even break if dropped from 100th floor. Both eggs are identical. You need to figure out the highest floor of a 100-story building an egg can be dropped without breaking. The question is how many drops you need to make. You are allowed to break 2 eggs in the process.

- Describe a technical problem you had and how you solved it.

- How would you design a simple search engine?

- Design an evacuation plan for San Francisco.

- There's a latency problem in South Africa. Diagnose it.

- What are three long term challenges facing Google?

- Name three non-Google websites that you visit often and like. What do you like about the user interface and design? Choose one of the three sites and comment on what new feature or project you would work on. How would you design it?

- If there is only one elevator in the building, how would you change the design? How about if there are only two elevators in the building?

- How many vacuum’s are made per year in USA? 

 



Google Interview Questions: Software Engineer


- Why are manhole covers round?

- What is the difference between a mutex and a semaphore? Which one would you use to protect access to an increment operation?

- A man pushed his car to a hotel and lost his fortune. What happened?

- Explain the significance of "dead beef".

- Write a C program which measures the the speed of a context switch on a UNIX/Linux system.

- Given a function which produces a random integer in the range 1 to 5, write a function which produces a random integer in the range 1 to 7.

- Describe the algorithm for a depth-first graph traversal.

- Design a class library for writing card games.

- You need to check that your friend, Bob, has your correct phone number, but you cannot ask him directly. You must write a the question on a card which and give it to Eve who will take the card to Bob and return the answer to you. What must you write on the card, besides the question, to ensure Bob can encode the message so that Eve cannot read your phone number?

- How are cookies passed in the HTTP protocol?

- Design the SQL database tables for a car rental database.

- Write a regular expression which matches a email address.

- Write a function f(a, b) which takes two character string arguments and returns a string containing only the characters found in both strings in the order of a. Write a version which is order N-squared and one which is order N.

- You are given a the source to a application which is crashing when run. After running it 10 times in a debugger, you find it never crashes in the same place. The application is single threaded, and uses only the C standard library. What programming errors could be causing this crash? How would you test each one?

- Explain how congestion control works in the TCP protocol.

- In Java, what is the difference between final, finally, and finalize?

- What is multithreaded programming? What is a deadlock?

- Write a function (with helper functions if needed) called to Excel that takes an excel column value (A,B,C,D...AA,AB,AC,...AAA..) and returns a corresponding integer value(A=1,B=2,...AA=26..).

- You have a stream of infinite queries (ie: real time Google search queries that people are entering). Describe how you would go about finding a good estimate of 1000 samples from this never ending set of data and then write code for it.

- Tree search algorithms. Write BFS and DFS code, explain run time and space requirements. Modify the code to handle trees with weighted edges and loops with BFS and DFS, make the code print out path to goal state.

- You are given a list of numbers. When you reach the end of the list you will come back to the beginning of the list (a circular list). Write the most efficient algorithm to find the minimum # in this list. Find any given # in the list. The numbers in the list are always increasing but you don’t know where the circular list begins, ie: 38, 40, 55, 89, 6, 13, 20, 23, 36.

- Describe the data structure that is used to manage memory. (stack)

- What's the difference between local and global variables?

- If you have 1 million integers, how would you sort them efficiently? (modify a specific sorting algorithm to solve this)

- In Java, what is the difference between static, final, and const. (if you don't know Java they will ask something similar for C or C++).

- Talk about your class projects or work projects (pick something easy)... then describe how you could make them more efficient (in terms of algorithms).

- Suppose you have an NxN matrix of positive and negative integers. Write some code that finds the sub-matrix with the maximum sum of its elements.

- Write some code to reverse a string.

- Implement division (without using the divide operator, obviously).

- Write some code to find all permutations of the letters in a particular string.
 
- What method would you use to look up a word in a dictionary?

- Imagine you have a closet full of shirts. It’s very hard to find a shirt. So what can you do to organize your shirts for easy retrieval?

- You have eight balls all of the same size. 7 of them weigh the same, and one of them weighs slightly more. How can you fine the ball that is heavier by using a balance and only two weighings?

- What is the C-language command for opening a connection with a foreign host over the internet?

- Design and describe a system/application that will most efficiently produce a report of the top 1 million Google search requests. These are the particulars: 1) You are given 12 servers to work with. They are all dual-processor machines with 4Gb of RAM, 4x400GB hard drives and networked together. (Basically, nothing more than high-end PC’s) 2) The log data has already been cleaned for you. It consists of 100 Billion log lines, broken down into 12 320 GB files of 40-byte search terms per line. 3) You can use only custom written applications or available free open-source software.

- There is an array A[N] of N numbers. You have to compose an array Output[N] such that Output[i] will be equal to multiplication of all the elements of A[N] except A[i]. For example Output [0] will be multiplication of A[1] to A[N-1] and Output[1] will be multiplication of A[0] and from A[2] to A[N-1]. Solve it without division operator and in O(n).

- There is a linked list of numbers of length N. N is very large and you don’t know N. You have to write a function that will return k random numbers from the list. Numbers should be completely random. Hint: 1. Use random function rand() (returns a number between 0 and 1) and irand() (return either 0 or 1) 2. It should be done in O(n).

- Find or determine non existence of a number in a sorted list of N numbers where the numbers range over M, M>> N and N large enough to span multiple disks. Algorithm to beat O(log n) bonus points for constant time algorithm.

- You are given a game of Tic Tac Toe. You have to write a function in which you pass the whole game and name of a player. The function will return whether the player has won the game or not. First you to decide which data structure you will use for the game. You need to tell the algorithm first and then need to write the code. Note: Some position may be blank in the game  So your data structure should consider this condition also.

- You are given an array [a1 To an] and we have to construct another array [b1 To bn] where bi = a1*a2*...*an/ai. you are allowed to use only constant space and the time complexity is O(n). No divisions are allowed.

- How do you put a Binary Search Tree in an array in a efficient manner. Hint :: If the node is stored at the ith position and its children are at 2i and 2i+1(I mean level order wise)Its not the most efficient way.

- How do you find out the fifth maximum element in an Binary Search Tree in efficient manner. Note: You should not use use any extra space. i.e sorting Binary Search Tree and storing the results in an array and listing out the fifth element.

- Given a Data Structure having first n integers and next n chars. A = i1 i2 i3 ... iN c1 c2 c3 ... cN. Write an in-place algorithm to rearrange the elements of the array ass A = i1 c1 i2 c2 ... in cn

- Given two sequences of items, find the items whose absolute number increases or decreases the most when comparing one sequence with the other by reading the sequence only once.

- Given That One of the strings is very very long , and the other one could be of various sizes. Windowing will result in O(N+M) solution but could it be better? May be NlogM or even better?

- How many lines can be drawn in a 2D plane such that they are equidistant from 3 non-collinear points?

- Let's say you have to construct Google maps from scratch and guide a person standing on Gateway of India (Mumbai) to India Gate(Delhi). How do you do the same?

- Given that you have one string of length N and M small strings of length L. How do you efficiently find the occurrence of each small string in the larger one?

- Given a binary tree, programmatically you need to prove it is a binary search tree.

- You are given a small sorted list of numbers, and a very very long sorted list of numbers - so long that it had to be put on a disk in different blocks.

How would you find those short list numbers in the bigger one?

- Suppose you have given N companies, and we want to eventually merge them into one big company. How many ways are theres to merge?

- Given a file of 4 billion 32-bit integers, how to find one that appears at least twice?

- Write a program for displaying the ten most frequent words in a file such that your program should be efficient in all complexity measures.

- Design a stack. We want to push, pop, and also, retrieve the minimum element in constant time.

- Given a set of coin denominators, find the minimum number of coins to give a certain amount of change.

- Given an array, i) find the longest continuous increasing subsequence. ii) find the longest increasing subsequence.

- Suppose we have N companies, and we want to eventually merge them into one big company. How many ways are there to merge?

- Write a function to find the middle node of a single link list.

- Given two binary trees, write a compare function to check if they are equal or not. Being equal means that they have the same value and same structure.

- Implement put/get methods of a fixed size cache with LRU replacement algorithm.

- You are given with three sorted arrays ( in ascending order), you are required to find a triplet ( one element from each array) such that distance is minimum.

- Distance is defined like this : If a[i], b[j] and c[k] are three elements then distance=max(abs(a[i]-b[j]),abs(a[i]-c[k]),abs(b[j]-c[k]))" Please give a solution in O(n) time complexity

- How does C++ deal with constructors and deconstructors of a class and its child class?

- Write a function that flips the bits inside a byte (either in C++ or Java). Write an algorithm that take a list of n words, and an integer m, and retrieves the mth most frequent word in that list.

- What's 2 to the power of 64?

- Given that you have one string of length N and M small strings of length L. How do you efficiently find the occurrence of each small string in the larger one?

- How do you find out the fifth maximum element in an Binary Search Tree in efficient manner.

- Suppose we have N companies, and we want to eventually merge them into one big company. How many ways are there to merge?

- There is linked list of millions of node and you do not know the length of it. Write a function which will return a random number from the list.

- You need to check that your friend, Bob, has your correct phone number, but you cannot ask him directly. You must write a the question on a card which and give it to Eve who will take the card to Bob and return the answer to you. What must you write on the card, besides the question, to ensure Bob can encode the message so that Eve cannot read your phone number?

- How long it would take to sort 1 trillion numbers? Come up with a good estimate.

- Order the functions in order of their asymptotic performance: 1) 2^n 2) n^100 3) n! 4) n^n

- There are some data represented by(x,y,z). Now we want to find the Kth least data. We say (x1, y1, z1) > (x2, y2, z2) when value(x1, y1, z1) > value(x2, y2, z2) where value(x,y,z) = (2^x)*(3^y)*(5^z). Now we can not get it by calculating value(x,y,z) or through other indirect calculations as lg(value(x,y,z)). How to solve it?

- How many degrees are there in the angle between the hour and minute hands of a clock when the time is a quarter past three?

- Given an array whose elements are sorted, return the index of a the first occurrence of a specific integer. Do this in sub-linear time. I.e. do not just go through each element searching for that element.

- Given two linked lists, return the intersection of the two lists: i.e. return a list containing only the elements that occur in both of the input lists.

- What's the difference between a hashtable and a hashmap?

- If a person dials a sequence of numbers on the telephone, what possible words/strings can be formed from the letters associated with those numbers?

- How would you reverse the image on an n by n matrix where each pixel is represented by a bit?

- Create a fast cached storage mechanism that, given a limitation on the amount of cache memory, will ensure that only the least recently used items are discarded when the cache memory is reached when inserting a new item. It supports 2 functions: String get(T t) and void put(String k, T t).

- Create a cost model that allows Google to make purchasing decisions on to compare the cost of purchasing more RAM memory for their servers vs. buying more disk space.

- Design an algorithm to play a game of Frogger and then code the solution. The object of the game is to direct a frog to avoid cars while crossing a busy road. You may represent a road lane via an array. Generalize the solution for an N-lane road.

- What sort would you use if you had a large data set on disk and a small amount of ram to work with?

- What sort would you use if you required tight max time bounds and wanted highly regular performance.

- How would you store 1 million phone numbers?

- Design a 2D dungeon crawling game. It must allow for various items in the maze - walls, objects, and computer-controlled characters. (The focus was on the

class structures, and how to optimize the experience for the user as s/he travels through the dungeon.)

- What is the size of the C structure below on a 32-bit system? On a 64-bit?

 

Google Interview: Software Engineer in Test
 
- Efficiently implement 3 stacks in a single array.

- Given an array of integers which is circularly sorted, how do you find a given integer.

- Write a program to find depth of binary search tree without using recursion.

- Find the maximum rectangle (in terms of area) under a histogram in linear time.

- Most phones now have full keyboards. Before there there three letters mapped to a number button. Describe how you would go about implementing spelling and word suggestions as people type.

- Describe recursive mergesort and its runtime. Write an iterative version in C++/Java/Python.

- How would you determine if someone has won a game of tic-tac-toe on a board of any size?

- Given an array of numbers, replace each number with the product of all the numbers in the array except the number itself *without* using division.

- Create a cache with fast look up that only stores the N most recently accessed items.

- How to design a search engine? If each document contains a set of keywords, and is associated with a numeric attribute, how to build indices?

- Given two files that has list of words (one per line), write a program to show the intersection.

- What kind of data structure would you use to index annagrams of words? e.g. if there exists the word "top" in the database, the query for "pot" should list that.

 

Google Interview: Quantitative Compensation Analyst

- What is the yearly standard deviation of a stock given the monthly standard deviation?

- How many resumes does Google receive each year for software engineering?

- Anywhere in the world, where would you open up a new Google office and how would you figure out compensation for all the employees at this new office? What is the probability of breaking a stick into 3 pieces and forming a triangle? 

 

Google Interview: Engineering Manager

- You're the captain of a pirate ship, and your crew gets to vote on how the gold is divided up. If fewer than half of the pirates agree with you, you die.

- How do you recommend apportioning the gold in such a way that you get a good share of the booty, but still survive?

 

Google Interview: AdWords Associate

- How would you work with an advertiser who was not seeing the benefits of the AdWords relationship due to poor conversions?

- How would you deal with an angry or frustrated advertisers on the phone?


================================================================================================================

출처 : http://biz.chosun.com/site/data/html_dir/2010/12/10/2010121001145.html

기상천외한 문제들… 무릎을 탁치는 대답 못하면 '위험'
구글 면접 4대 키워드
1. 얼마나 창의적인가
2. 논리적 사고를 하는가
3. 상황 대처능력 평가
4. 업무지식·비전이 있나국내서도 유사한 시험 유행

세계 최대 인터넷 기업인 구글(Google)의 입사 면접시험 문제는 기발할 뿐 아니라 대답하기 어렵기로 유명하다. 발상의 전환과 논리적 사고력을 동시에 요구하면서 실전적인 문제 해결 능력까지 테스트하기 때문이다. 미국의 고용 컨설팅 업체인 시애틀 인터뷰 코치(Seattle Interview Coach)는 최근 구글 면접시험 응시자들을 통해 파악한 면접시험 문제 140개를 발굴해 공개했다.

이 문제들은 예측하기 힘든 변화의 상황에서 창의적 해결책을 제시할 줄 아는 인재를 찾는 데 주안점을 두고 있다. 입사 동기나 자신의 장단점, 포부 등을 묻는 평범한 질문보다는 기발하고 때론 황당하기까지 한 질문이 많다. 논리적이면서도 때로는 '상자 밖'에서 생각할 수 있는 진정한 '구글러(Googler)'를 찾아내려는 것이다. 구글은 또한 제대로 된 질문을 할 줄 아는 인재도 중요시한다. 응시자가 때로는 면접관에게 역으로 질문을 던져야 한다는 얘기다.

실제 면접시험에서 이런 질문을 받는다면 얼마나 당황스러울까. 그러나 최근엔 국내에서도 일부 컨설팅 회사와 대기업들이 구글과 유사한 방식으로 면접시험을 치르고 있다. 요즘 세상이 원하는 인재상이 어떤지를 알기 위해 구글이 출제한 시험문제들을 살펴보기로 하자. 구글의 면접시험 문제는 크게 4가지 유형이다.

■발상의 전환

①옷장에 셔츠가 가득 차 있는데, 원하는 셔츠를 찾기 힘들다. 쉽게 찾기 위한 당신만의 정리법은?

②건물에 엘리베이터가 한 대뿐이라면 이를 어떻게 디자인하겠는가? 2대라면?

③A지점에서 B지점으로 가야 한다. 그런데 도착할 수 있을지도 확실치 않다. 어떻게 하겠는가?

이들 질문의 의도는 정답을 원하는 게 아니다. 지원자가 기존의 틀에서 벗어나 얼마나 자유롭게 사고하고, 혁신적 아이디어를 낼 수 있는지를 알아보려는 것이다. '내 의견이 곧 답'이라는 자세로 생각을 자유롭게 표현하면 된다.

■논리적 사고력

①3시15분에 시침과 분침이 만들어내는 각도는?

②미국 대학의 4학년 학생 중 취업 후에 졸업하는 학생은 몇 명인가?

③인터넷 사이트 방문자가 배너 광고를 한번 클릭하면 광고회사는 10센트를 번다. 방문자의 20%만 클릭을 한다. 20달러를 벌려면 몇 명이 방문해야 하나?

수리적 계산 및 논리적 추론 능력을 보는 질문이다. 정보가 부족한 상황에서 지원자가 어떻게 대응하는지도 살펴본다. ①번의 답은 7.5도. ②번 질문은 자신이 졸업한 대학을 사례로 학생 숫자를 추정한 뒤 전체 대학 수에 곱하는 것이 한 방법이다. ③번의 경우 10센트를 벌려면 5명, 그 200배인 20달러를 벌려면 1000명이 방문해야 한다.

■상황에 따른 문제 해결

①100명의 부부가 사는 마을에서 모든 남편은 바람을 피운다. 부인들은 남의 남편이 바람피우는 것은 알지만, 내 남편이 바람피우는 건 모른다. 부인이 남편 바람피운 것을 증명하면 남편을 죽여야 한다. 이 규칙은 누구도 어기지 못한다. 어느 날 이 마을의 여왕이 이 마을 남편 중 최소한 1명은 바람을 피운다는 사실을 공표했다. 어떤 일이 벌어질까?

②A·B·C·D 4명이 밤에 다리를 건너는데, 각각 1·2·5·10분씩 걸린다. 동시에 두 사람만 건널 수 있고, 손전등이 반드시 필요하다. 손전등 한 개만 갖고 4명이 17분 안에 다 건너갈 방법은?


상황에 대한 판단력과 논리적인 해결 능력을 보기 위한 질문으로, 비정형적 상황이 발생했을 때 어떤 논리로 대응해 나가는지를 테스트한다. 문제 해결 과정이 평가의 초점이다. ①번 답은 '이 마을의 남편들이 결국 모두 죽는다'이다. ②번 답은 A, B가 같이 건넌 뒤(2분), A가 손전등을 갖고 돌아와서(1분), C, D가 함께 건너고(10분), B가 돌아온 뒤(2분), A, B가 함께 건넌다(2분) 총 17분이다.

■업무지식과 비전

①구글의 경쟁사는 어디이며, 어떻게 경쟁해야 하나?

②6개월 안에 G메일 고객 1억명을 확보할 방안을 세워보라.

③구글이 당면한 세 가지 장기 과제는?


지원자가 구글과 IT산업, 소프트웨어 등에 대해 얼마나 아는지, 업무 능력과 비전을 갖고 있는지를 알아보는 것이다. 회사에 대한 기본적 지식과 함께 최신 이슈를 파악하고 자신만의 업무비전과 계획도 가지고 있어야 한다.


================================================================================================================

출처 : http://biz.chosun.com/site/data/html_dir/2010/12/10/2010121001578.html

미국의 비즈니스 잡지 중 하나인 ‘비즈니스 인사이더(Business Insider)’는 구글의 면접 질문 중 답이 있는 질문들에 대해 모범답안을 제시했다. 다음은 그 중 일부이다. 질문의 의도가 무엇이고 면접자의 어떤 능력을 보려고 한 것인지에 대한 설명(☆)과 함께 모범답안(→)을 예시했다.

Q: A나라 사람들은 모두 아들을 극단적으로 선호해서 아들을 가질 때까지 계속해서 아이를 낳습니다. 아들을 가지면 아이 낳기를 중단하고, 딸을 낳으면 아들을 가질 때까지 계속 아이를 낳습니다. 이 나라에서 아들과 딸의 비율은 어떻게 될까요?

☆상당한 논란을 낳을 수 있는 질문입니다. 그러나 논리적 절차에 따라 비율을 계산하면 의외로 간단합니다.

→답은 50대 50으로 같습니다. 가령 이 나라에 1000쌍의 부부가 있고 각각 1명씩 1000명의 아이(아들 500명, 딸 500명)를 낳았다고 합시다. 아들을 가진 부부는 자녀 갖기를 중단할 것이고, 딸 가진 부부는 또 아이를 낳을 겁니다. 자연적 확률에 따라 아들 250명, 딸 250명이 됩니다. 이때 아들의 총수는 750명이고, 딸도 750명입니다. 딸 가진 부부(250쌍)는 또 아이를 가지는데, 아들 125명, 딸 125명을 낳습니다. 이때 아들은 총 875명, 딸도 875명입니다. 이렇게 해서 딸 가진 부부가 계속 아이를 가져도 아들·딸의 비율은 50대 50으로 변화가 없습니다.

Q: 맨홀 뚜껑은 왜 둥글까요?

☆기하학적 지식을 요구하는 문제입니다. 사각형이나 삼각형에 비해 원이 가지는 기하학적 특징이 무엇인지를 맨홀과 연결지어서 생각해야 합니다.

→둥근 맨홀 뚜껑은 절대 맨홀 안으로 떨어지지 않기 때문입니다. 좀 더 자세히 설명하겠습니다. 맨홀 뚜껑 아래에는 이를 받쳐주는 좁은 밑받침(턱)이 있습니다. 그래서 뚜껑이 사각이든 삼각이든 원형이든 평소에는 맨홀 아래로 떨어지지 않습니다. 그러나 맨홀 속으로 들어가기 위해 뚜껑을 열었을 때는? 뚜껑을 수직으로 세운 뒤 방향을 틀어보세요. 사각형이나 삼각형 뚜껑은 짧은 변이 대각선이나 긴 변보다 길이가 짧기 때문에 맨홀 아래로 떨어집니다. 그러나 원은 모든 방향으로 길이가 같기 때문에 어떤 경우에도 아래로 떨어지지 않습니다.

한 가지 이유가 더 있습니다. 원은 평면에 비해 압력에 견디는 힘이 강합니다. 맨홀이 둥근 모양인 것은 양옆에서 가해지는 압박에 잘 견디기 때문입니다. 그래서 맨홀 뚜껑도 원형으로 설계돼 있습니다.

Q: 시계의 시침과 분침은 하루에 몇 번이나 만날까요?

☆차분하게 논리적으로 생각하는 능력을 파악하려는 것입니다. 덜렁대면 착각할 수 있습니다.

→일단 자정에 한번 만납니다. 그리고 오전 1시5분, 2시11분, 3시16분, 4시22분, 5시27분, 6시33분, 7시38분, 8시44분, 9시49분, 10시55분 무렵에 만납니다. 오전 11시 대에는 분침과 시침이 안 만납니다. 오전에 총 11번. 오후에도 동일합니다. 그래서 총 22번 만납니다.

Q: 당신은 해적선 선장입니다. 황금을 어떻게 배분할지에 대한 당신의 안을 놓고 100명의 선원이 투표를 합니다. 과반의 지지를 못 얻으면 당신은 죽어요. 죽지 않으면서 최대한 많은 황금을 차지할 수 있는 안은 무엇인가요?

☆순발력과 함께 투표 행위의 본질적 요소가 무엇인지에 대한 이해가 요구됩니다.

→선원 51명과 황금을 똑같이 나눠 가지는 겁니다. 왜? 과반의 지지를 얻으려면 선원 51%를 내 편으로 만들어야 합니다. 그러려면 황금을 줘야 합니다. 그러나 분배 과정에서 불만이 있으면 안 되죠. 1표가 똑같은 가치인 만큼 황금도 똑같이 나눠야 합니다. 51명 초과의 지지는 배분 몫만 줄이므로 불필요합니다.

Q: 같은 크기의 공이 8개 있는데, 그 중 7개는 무게가 같고 한 개는 더 무거워요. 저울을 두 번만 사용해서 무거운 공을 찾아내세요.

☆논리적인 문제 해결 능력을 측정하는 문제입니다.

→8개의 공 중에서 6개를 임의로 고른 뒤 저울 양쪽에 3개씩 올려놓습니다. 저울이 균형을 이루면 6개의 공은 모두 무게가 같은 겁니다. 남은 2개의 공을 저울로 재서 무거운 공을 찾아내면 됩니다. 반대로 저울이 한쪽으로 기울면, 무거운 쪽 공 3개 중에서 2개를 임의로 골라 저울에 다시 잽니다. 한쪽이 무거우면 그 공이 답이고요, 저울이 균형을 이루면 남은 1개의 공이 무거운 공입니다.

Q: 샌프란시스코 시민들을 한꺼번에 대피시킬 계획을 말해보세요.

☆면접자가 문제에 어떻게 대처하는지 그 태도를 보려는 질문입니다. 대답하려고 끙끙거리기보다는 질문자에게 거꾸로 질문 공세를 펴보세요.

→대피 계획을 세우라고 하셨는데, 도대체 어떤 종류의 재앙에 대한 대피계획을 세워야 하는지 말씀해 주십시오.

Q: 특수 제조한 계란이 2개 있는데, 100층 높이 빌딩의 몇 층에서 떨어뜨려야 깨지는지 알아내려 합니다. 단 2개의 계란만 사용해서 몇 층에서 깨지는지 확실하게 알아내려면 계란을 최소 몇 번 떨어뜨려 봐야 할까요?

☆논리적 계산력과 함께 직관적 해결능력이 필요한, 상당히 난해한 문제입니다. 최소 비용(계란)과 시간(횟수)으로 실험을 하라는 겁니다.

→답은 14번입니다. 1층부터 차례로 계란을 떨어뜨려 보면 몇 층에서 깨지는지 확실히 알 수 있지만, 100층까지 최대 100번을 던져봐야 합니다. 50층에서 실험한 뒤 깨지면 1층부터, 안 깨지면 51층부터 실험하면 실험 횟수를 절반으로 줄일 수 있죠. 같은 식으로 최초에 시작하는 층을 계산해 보면 14층이 가장 효율적입니다. 14층에서 깨지면 1층부터 실험합니다(최대 14회 실험). 안 깨지면 13층 위인 27층에서 다시 실험합니다. 깨지면 15층부터 26층까지 던져보고(이 경우도 최대 14회 실험), 안 깨지면 12층 위인 39층에서 실험을 반복합니다. 계속 같은 실험을 50·60·69·77·80·86·91· 95·98층에서 차례로 실시합니다. 98층에서 안 깨지면 99·100층에서 최종 투척 실험을 합니다. 이 모든 경우 최대 실험 횟수는 14번입니다. 이것이 계란이 몇 층에서 깨지는지를 아는 가장 효율적인 방법입니다.

Q: 8세짜리 조카에게 데이터베이스(database)가 무엇인지 3문장 이내로 설명해 보세요.

☆복잡한 개념을 쉬운 말로 설명하면서, 누구와도 소통할 수 있는 능력을 알아보려는 것입니다.

→데이터베이스는 여러 가지 사물에 대한 많은 정보를 기억할 수 있는 기계란다. 사람들은 이 기계에 많은 정보를 저장한 뒤 필요할 때 꺼내 쓰지. 자, 이제 알았으니 나가서 뛰어놀렴.

'ETC > 잡동사니 이야기' 카테고리의 다른 글

MSN 움직이는 토끼 이모티콘  (0) 2013.02.27
화투점 풀이  (0) 2013.02.27
회사 직급 영문 표기  (0) 2013.02.27
Self-discovery exercise (자기발견 프로그램)  (0) 2010.12.29
어깨 피로 푸는 요가 자세  (0) 2010.11.01
안정적인 DNS서비스 DNSEver DNS server, DNS service
Posted by 키르히아이스
,

__int64 n64Test = 9223372036854775807;
unsigned __int64 un64Test = 18446744073709551615;

wsprintf("%I64d 는 __int64 값입니다", n64Test);
wsprintf("%I64u 는 unsigned __int64 값입니다", un64Test);
안정적인 DNS서비스 DNSEver DNS server, DNS service
Posted by 키르히아이스
,
출처 : http://support.microsoft.com/kb/125056/ko


기술 자료: 125056 - 마지막 검토: 2004년 1월 19일 월요일 - 수정: 2.0

INFO: 부동 소수점 연산의 정밀도와 정확도

이 문서는 이전에 다음 ID로 출판되었음: KR125056

이 페이지에서

모두 확대 | 모두 축소

요약
부동 소수점수의 연산은 정밀도, 반올림 및 정확도에 따라 예상 밖의 결과를 얻는 경우가 많습니다. 다음은 부동 소수점 연산에서 지켜야 할 4가지...

부동 소수점수의 연산은 정밀도, 반올림 및 정확도에 따라 예상 밖의 결과를 얻는 경우가 많습니다. 다음은 부동 소수점 연산에서 지켜야 할 4가지 일반 규칙입니다.
  1. 단정밀도 수와 배정밀도 수의 연산결과는 대부분 단정밀도 수 연산 결과와 같은 정확하지 않은 결과를 리턴합니다. 배정밀도 수가 필요하면 상수를 포함한 해당 연산의 모든 숫자를 배정밀도 수로 지정해야 합니다.
  2. 컴퓨터에서는 아무리 단순한 부동 소수점 숫자 값이라도 정확하게 표현되지 않습니다. 대부분의 부동 소수점 값은 이진수 값으로 정확하게 표현할 수 없습니다. 예를 들어, 0.1을 이진수로 표현하면 무한히 반복되는 0.0001100110011...이므로, PC를 포함한 이진법을 사용하는 모든 컴퓨터에서는 완전히 정확하게 표현할 수 없습니다.
  3. 연산 결과가 마지막 소수 자릿수까지 정확하지 않을 수 있습니다. 사람이 계산하는 "정확한" 값과 이진법을 사용하는 한정된 정밀도로 부동 소수점 연산을 수행하는 컴퓨터에서의 결과 값 사이에는 항상 약간의 차이가 있습니다.
  4. 서로 같은지 확인하기 위해 두 개의 부동 소수점 값을 비교하지는 마십시오. 규칙 3에 따라 "반드시" 같아야 하는 두 숫자 사이에도 항상 약간의 차이가 납니다. 그 대신 항상 숫자들이 거의 근사한 값인지 확인하십시오. 즉, 두 수의 차이가 아주 작은지 또는 무의미한지 확인하십시오.

추가 정보
일반적으로 앞에서 설명한 규칙은 C, C++, 어셈블러를 포함하여 모든 언어에 적용됩니다. 아래의 예제에서는 FORTRAN PowerStatio...

일반적으로 앞에서 설명한 규칙은 C, C++, 어셈블러를 포함하여 모든 언어에 적용됩니다. 아래의 예제에서는 FORTRAN PowerStation 사용과 관련된 몇 가지 규칙을 보여줍니다. C로 작성된 마지막 예제을 제외하고 모든 예제가 옵션 없이 FORTRAN PowerStation 32를 사용하여 컴파일되었습니다.

숫자 상수에 대한 자세한 내용은 Microsoft FORTRAN과 함께 제공되는 FORTRAN 설명서를 참조하고, 부동 소수점 값의 내부 표현에 대한 자세한 내용은 36068  (http://support.microsoft.com/kb/36068/EN-US/ ) 문서를 참조하십시오.

예제 1

첫 번째 예제는 다음 두 가지 사항을 보여줍니다.

  • FORTRAN 상수는 기본적으로 단정밀도 수이지만, C 상수는 기본적으로 배정밀도입니다.
  • 단정밀도 수를 포함하는 연산은 모든 연산되는 수가 단정밀도 수인 연산보다 정확하지는 않습니다.
단정밀도 상수 1.1로 초기화된 후 y는 단정밀도 변수만큼 정확하지 않습니다.
   x = 1.100000000000000  y = 1.100000023841858
단정밀도 값을 배정밀도 값으로 곱한 결과는 두 개의 단정밀도 값을 곱한 것만큼이나 정확하지 않습니다. 두 가지 연산 모두 두 개의 배정밀도 값을 곱했을 때보다 오류가 많습니다.
   true = 1.320000000000000 (두 개의 배정밀도 값을 곱한 경우)
   y    = 1.320000052452087 (배정밀도 값과 단정밀도 값을 곱한 경우)
   z =1.320000081062318 (두 개의 단정밀도 값을 곱한 경우)

예제 코드

C Compile options: none

       real*8 x,y,z
       x = 1.1D0
       y = 1.1
       print *, 'x =',x, 'y =', y
       y = 1.2 * x
       z = 1.2 * 1.1
       print *, x, y, z
       end

예제 2

예제 2에서는 2차 방정식이 사용됩니다. 이 예제에서는 배정밀도 연산도 완벽하지 않으며 작은 오류가 결과에 큰 변화를 줄 수 있는 경우 그 전에 연산 결과를 테스트해야 함을 보여줍니다. 예제 2에 나와 있는 제곱근 함수의 입력은 아주 약간 틀리지만 역시 올바르지 않은 값입니다. 배정밀도 연산에 아주 작은 오류도 없다면 결과는 다음과 같아야 합니다.
   Root =   -1.1500000000
그러나, 이진법을 사용하는 컴퓨터에서는 다음과 같은 오류가 생성됩니다.
run-time error M6201: MATH
- sqrt: DOMAIN error

예제 코드

C Compile options: none

       real*8 a,b,c,x,y
       a=1.0D0
       b=2.3D0
       c=1.322D0
       x = b**2
       y = 4*a*c
       print *,x,y,x-y
       print "(' Root =',F16.10)",(-b+dsqrt(x-y))/(2*a)
       end

예제 3

예제 3에서는 최적화를 켜지 않은 경우에도 최적화가 발생하여 값이 일시적으로 예상한 것보다 높은 정밀도를 유지할 수 있다는 점과 두 개의 부동 소수점 값이 같은지 비교하는 것은 현명하지 못함을 보여줍니다.

이 예에서는 두 개의 값이 모두 같거나 같지 않습니다. 첫째 IF에서는 값 Z가 아직 보조 프로세서의 스택에 있어 Y와 같은 정밀도를 갖습니다. 따라서 X가 Y와 같지 않아 첫째 메시지가 인쇄됩니다. 둘째 IF가 처리될 때는 Z가 메모리에서 로드되어야 하므로 X와 같은 정밀도와 값을 갖게 되어 둘째 메시지도 인쇄됩니다.

예제 코드

C Compile options: none

       real*8 y
       y=27.1024D0
       x=27.1024
       z=y
       if (x.ne.z) then
         print *,'X does not equal Z'
       end if
       if (x.eq.z) then
         print *,'X equals Z'
       end if
       end

예제 4

예제 코드 4의 첫째 부분에서는 1.0에 인접한 두 숫자 사이에서 가능한 가장 작은 차이를 계산합니다. 1.0의 이진 표현에 단일 비트를 추가하는 방법을 통해 계산합니다.
   x   = 1.00000000000000000  (1.0보다 1비트 큼)
   y   = 1.00000000000000000  (1.0)
   x-y =  .00000000000000022  (가능한 가장 작은 차이)
일부 FORTRAN 버전에서는 숫자를 표시할 때 반올림하므로 본질적인 숫자의 부정확함을 쉽게 알아차릴 수 없습니다. 이 때문에 x와 y를 표시하면 같은 값으로 표시됩니다.

예제 코드 4의 둘째 부분에서는 10.0에 인접한 두 숫자 사이에서 가능한 가장 작은 차이를 계산합니다. 10.0의 이진 표현에 단일 비트를 추가하는 방법을 통해 계산합니다. 10에 인접한 숫자들 간의 차이는 1에 인접한 숫자들 간의 차이보다 큽니다. 즉, 숫자의 절대값이 클수록 주어진 비트 숫자에서 정확하게 저장되지 않을 가능성이 늘어납니다.
   x   = 10.00000000000000000  (10.0보다 1비트 큼)
   y   = 10.00000000000000000  (10.0)
   x-y =   .00000000000000178
해당 숫자가 1비트만 차이남을 보여주기 위해 숫자의 이진 표현도 표시했습니다.
   x = 4024000000000001 Hex
   y = 4024000000000000 Hex
예제 코드 4의 마지막 부분에서는 간단한 비순환 10진 값은 대개 순환 소수로 이진 표현할 수 있음을 보여줍니다. 이 경우 x=1.05는 가수(mantissa)에서 반복 인자 CCCCCCCC....(16진수)를 필요로 합니다. FORTRAN에서는 가능한 가장 높은 정확도를 유지하기 위해 마지막 자릿수 "C"를 "D"로 반올림합니다.
   x = 3FF0CCCCCCCCCCCD (1.05D0의 16진 표현)
반올림해도 결과가 완벽하게 정확하지는 않습니다. 첫째 자릿수를 제거한 후에 볼 수 있는 최소 유효 자릿수 뒤에 몇 가지 오류가 있습니다.
   x-1 = .05000000000000004

예제 코드

C Compile options: none

       IMPLICIT real*8 (A-Z)
       integer*4 i(2)
       real*8 x,y
       equivalence (i(1),x)

       x=1.
       y=x
       i(1)=i(1)+1
       print "(1x,'x  =',F20.17,'  y=',f20.17)", x,y
       print "(1x,'x-y=',F20.17)", x-y
       print *

       x=10.
       y=x
       i(1)=i(1)+1
       print "(1x,'x  =',F20.17,'  y=',f20.17)", x,y
       print "(1x,'x-y=',F20.17)", x-y
       print *
       print "(1x,'x  =',Z16,' Hex  y=',Z16,' Hex')", x,y
       print *

       x=1.05D0
       print "(1x,'x  =',F20.17)", x
       print "(1x,'x  =',Z16,' Hex')", x
       x=x-1
       print "(1x,'x-1=',F20.17)", x
       print *

       end

예제 5

C에서 부동 소수점 상수는 기본적으로 배정밀도 수입니다. float 값을 나타내려면 "f"를 사용하십시오(예: "89.95f").
   /* Compile options needed: none
   */ 

   #include <stdio.h>

   void main()
   {
      float floatvar;
      double doublevar;

   /* Print double constant. */ 
      printf("89.95 = %f\n", 89.95);      // 89.95 = 89.950000

   /* Printf float constant */ 
      printf("89.95 = %f\n", 89.95F);     // 89.95 = 89.949997

   /*** Use double constant. ***/ 
      floatvar = 89.95;
      doublevar = 89.95;

      printf("89.95 = %f\n", floatvar);   // 89.95 = 89.949997
      printf("89.95 = %lf\n", doublevar); // 89.95 = 89.950000

   /*** Use float constant. ***/ 
      floatvar = 89.95f;
      doublevar = 89.95f;

      printf("89.95 = %f\n", floatvar);   // 89.95 = 89.949997
      printf("89.95 = %lf\n", doublevar); // 89.95 = 89.949997
   }

본 문서의 정보는 다음의 제품에 적용됩니다.
  • Microsoft FORTRAN PowerStation 1.0 Standard Edition
  • Microsoft Fortran PowerStation 1.0a for MS-DOS
  • Microsoft FORTRAN PowerStation 32
  • Microsoft Visual C++ 1.0 Professional Edition
  • Microsoft Visual C++ 1.5 Professional Edition
  • Microsoft Visual C++ 1.51
  • Microsoft Visual C++ 2.0 Professional Edition
  • Microsoft Visual C++ 4.0 Standard Edition
  • Microsoft Visual C++ 5.0 Enterprise Edition
  • Microsoft Visual C++ 6.0 Enterprise Edition
  • Microsoft Visual C++ 5.0 Professional Edition
  • Microsoft Visual C++ 6.0 Professional Edition
  • Microsoft Visual C++, 32비트 Learning Edition 6.0
키워드: 
kbinfo kblangfortran kblangc kbcode kbfortranps KB125056
안정적인 DNS서비스 DNSEver DNS server, DNS service
Posted by 키르히아이스
,
출처 : http://www.gpgstudy.com/gpg2/gpg2s2-1draft.html

이 글은 조만간 출판될 Game Programming Gems 2 한국어판(류광 역, 정보문화사)의 일부입니다. 이 글에 대한 모든 권리는 정보문화사가 가지고 있으며, 사전 허락 없이는 웹 사이트 게시를 비롯한 어떠한 형태의 재배포도 금지되어 있습니다.

이 글은 최종 교정 전의 상태이므로 오타나 오역이 있을 수 있습니다. 또한 웹 페이지의 한계 상, 실제로 종이에 인쇄된 형태와는 다를 수 있습니다.

Game Programming Gem 2 한국어판에 대한 정보는 GPG 스터디(http://www.gpgstudy.com/)에서 얻을 수 있습니다.

2.1 부동소수점 비법들: IEEE 부동소수점을 통한 성능 향상

Yossarian King, Electronic Arts Canada
yking(at)ea.com

개요

정수(integer, 整數)는 고정된 정밀도(precision, 유효자리수)와 크기(magnitude)를 가진다. 부동소수점(floating-point, 浮動小數點) 수는 말 그대로 소수점(decimal point)의 위치가 떠다니는(floating, 부동) 수를 말하며, 크기도 가변적이다. 역사적으로, 정수는 빠르고 부동소수점수는 느렸으므로, 대부분의 게임 프로그래머들은 부동소수점수를 피하고 대신 정수를 사용했는데, 사실 일반적인 계산을 정수로만 수행하는 것은 번거로운 일이 아닐 수 없었다. 그래도 얻을 수 있는 이득은 많았다. 그러나 하드웨어 비용이 대단히 낮아지고, PC와 게임 콘솔들이 부동소수점 덧셈, 뺄셈, 곱셈, 나눗셈을 단 몇 사이클 만에 처리하게 됨에 따라, 이제는 게임 프로그래머들이 손쉬운 부동소수점 연산의 장점을 취할 수 있는 시절이 도래하게 되었다.

기본적인 부동소수점 연산이 빨라지긴 했지만, 복잡한 함수들은 여전히 느리다. 부동소수점 라이브러리들이 더 최적화될 수도 있으나, 그런 라이브러리들은 일반적으로 성능이 아닌 정확성을 위해 구현된 것들이다. 게임에서는 정확성보다 성능이 더 중요한 경우가 많다.

이 글은 정확성을 희생함으로써 부동소수점 연산 속도를 빠르게 하는 여러 가지 비법들을 제시한다. 정수 연산에는 오랫동안 테이블 참조 기법이 즐겨 쓰였다. 이 글에서는 임의의 부동소수점 함수의 최적화를 위한 일반화된 선형 및 대수적(logarithmic, 對數的) 참조 테이블 기법들을 소개한다.

이 글에서 이야기하는 주제들은 다음과 같다.

  • IEEE 부동소수점 표준
  • 빠른 float/int 변환, 비교, 제한
  • 사인.코사인 최적화를 위한 선형 참조 테이블 방법
  • 임의의 부동소수점 함수의 최적화를 위한 일반화된 참조 테이블 기법
  • 성능 측정의 중요성

IEEE 부동소수점 형식

부동소수점 수에 대한 IEEE 표준에는 부동소수점의 이진(binary) 표현과 반올림, 정확성, 예외적 결과(0으로 나누기 등)에 대한 규약들이 명시되어 있다. 이 글에서 다루는 기법은 이진 표현에 근거한 것이며, 반올림이나 예외 처리와는 큰 관련이 없다. IEEE의 표준 이진 표현을 사용하는 컴퓨터나 콘솔이라면 부동소수점 처리에 대한 IEEE 표준을 완전히 만족하지 않는다 해도 이 글의 기법들을 적용할 수 있다. 펜티엄 III Streaming SIMD Extensions(SSE)와 PS2 벡터 유닛은 둘다 IEEE 표준의 부분집합을 만족한다. 그 부분집합은 예외 처리를 완전히 지원하지는 않으나, 이진 표현만큼은 표준을 따르므로 이 글의 기법들을 문제없이 적용할 수 있다.

IEEE 표준은 부동소수점 수를 하나의 부호 비트, 하나의 바이어스된 지수(biased exponent, -指數), 하나의 정규화된 가수(mantissa, 假數) 또는 유효수(significand, 有效數)로 표현한다. 단정도(single precision, 單程度) 32 비트 부동소수점 수(C의 float)는 그림 2.1.1과 같은 방식으로 저장된다.

s = 부호
e = 바이어스된 지수
m = 정규화된 가수
부동 소수점 수는 s x 1.m x 2(e-127)

그림 2.1.1 IEEE 32 비트 부동소수점 형식. 부호 1 비트, 지수 8 비트, 가수 23 비트로 되어 있다.

지수는 바이어스된 형식의 양수(positive number, 陽數)로서 저장된다(이와는 달리 정수의 경우 지수는 2의 보수(complement, 補數)로 표현된다). 여기서 바이어스되었다는 것은 실제 지수에 127이 더해졌다는 뜻이다. 가수는 23 비트 소수에 1을 붙이는 방식으로 정규화된 형태로 저장된다. 이런 방식의 정규화는 사용할 수 있는 비트들로부터 최대의 정밀도를 얻기 위한 것이다.

따라서, 하나의 부동소수점 수는 1에서 2 사이의 값으로 정규화된 유효수 하나, 이진 소수점의 위치를 가리키는 바이어스된 지수 하나, 그리고 부호 비트 하나로 구성된다. 하나의 부동 소수점 수는 다음과 같은 공식으로 표현할 수 있다.

n = s x 1.m x 2(e -127)

예를 들어서 십진수 -6.25는 이진수로 -110.01이며, 이를 위의 공식으로 표현하면 -1 x 1.1001 x 22가 된다. 이는 s = 1, e = 2 + 127 = 10000001, m = [1.]1001에 해당한다(그림 2.1.2).

지수에는 약간의 예외적인 규칙들이 존재한다. e = 255일 때, m에는 숫자 아님(not-a-number, NaN), 정의할 수 없는 결과, 양의 무한대 또는 음의 무한대 같은 특별한 조건들이 들어간다. e = 0은 숫자의 역정규화(denormalize)된 숫자, 즉 숫자가 너무 작아서 지수가 8 비트를 넘어갔을 때의 경우를 위해 쓰인다.

그림 2.1.2 -6.25가 32 비트 IEEE 부동소수점 형식으로 메모리에 저장되는 방식

배정도(double precision, 倍程度) 64비트 부동소수점 수도 앞에서 설명한 방식과 비슷한 구조를 지니고 있다. 차이는 지수가 11 비트이고 유효수가 52 비트라는 점이다. 지수의 바이어스 값은 127이 아니라 1023이다. 배정도 수는 저장 공간이 단정도의 두 배이므로 메모리로부터 로드하거나 처리하는 것이 더 느릴 수 있다. 그런 이유로 게임에서는 배정도를 잘 쓰지 않는다. 이 글에서도 단정도 부동소수점수에 대해서만 이야기한다.

부동소수점 트릭

참조 테이블 기법으로 들어가기 전에, 부동소수점 수의 비트 패턴을 조작함으로써 부동소수점 연산의 속도를 높일 수 있는 몇 가지 비법들을 이야기하겠다.

float/int 변환

이후에 나올 참조 테이블 기법은 부동소수점 수를 정수로 변환함으로써 참조 테이블 색인들을 생성한다. 이러한 작업의 속도는 느릴 수 있다. 예를 들어서, 펜티엄 II에서 (int)f라는 코드를 통해 하나의 float을 하나의 int로 변환할 때에는 약 60 사이클 정도가 걸린다. 이처럼 많은 사이클이 소비되는 이유는, ANSI C 표준에서는 float에서 int로의 형변환 도중 소수부는 잘려나가야 되는 것으로 명시하고 있지만, 정작 FPU는 가장 가까운 정수로 반올림하기 때문이다. int로의 변환을 위해서는 FPU 반올림(rounding) 모드를 변경하는 루틴을 호출하고, 변환을 수행하고, 다시 반올림 모드를 원래 대로 바꾸는 과정을 거쳐야 하는데, 이는 대단히 번거로운 작업이다.

int와 float 사이의 형변환에 걸리는 시간은 컴파일러나 프로세서에 따라 다를 수 있다. 모든 최적화 옵션들을 켜놓은 상태에서 이러한 변환을 일반적인 형변환과 비교해보고, 생성된 어셈블리 코드를 살펴보면 실제로 어떤 일이 일어나는지 알 수 있을 것이다.

부동소수점에서 정수로의 형변환을 수행하는 좀 더 빠른 방법은, 부동소수점 수에 1 x 223을 더하고 그 결과에서 상위 지수 비트들을 버리는 것이다. 그럼 코드를 먼저 살펴보고 어떻게 작동하는지 설명하겠다.

편의를 위해, 하나의 32 비트 수를 int 형으로도, 그리고 float 형으로도 접근할 수 있도록 하는 공용체(union)을 정의한다.

typedef union
{
      int      i;
      float    f;
   } _INTORFLOAT;

INTORFLOAT 형은 이 글의 모든 예제 코드에서 쓰인다. 이 공용체를 통해서 수의 비트 패턴에 접근하는 것은 매우 간단해 보이지만, 실제로는 컴파일러가 예상 외로 많은 코드를 생성해야 할 수도 있다. 예를 들어서 펜티엄 II의 경우 부동소수점과 정수 레지스터들은 개별적인 하드웨어 유닛에 존재하며, 데이터를 한 쪽에서 다른 쪽으로 옮기려면 반드시 메모리를 거쳐야 한다. 따라서 INTORFLOAT 공용체의 수에 접근하려면 추가적인 메모리 로드-저장 과정이 일어날 수 있다. 다음은 하나의 float을 int로 변환하는 코드이다.

   INTORFLOAT	n;		// 변환할 부동소수점 수
   INTORFLOAT	bias;	// "마법의" 수

   bias.i = (23 + 127) << 23;	// 바이어스 상수 = 1 x 2^23
   n.f = 123.456f;			// 부동소수점 수

   n.f += bias.f;			// 부동소수점으로서 더하고
   n.i -= bias.i;			// 정수로서 뺀다.
   // n.i는 123이 된다. 이는 n.f의 정수 부분과 일치한다.

마법과도 같은 일이 일어났는데, 왜 그런지 살펴보자. 부동소수점 수에 1 x 223을 더하면 가수가 하위 23비트 쪽으로 밀려나며, 지수는 알려진 값인 (23 + 127)로 설정된다. 거기서 알려진 값 (23 +127)을 정수로서 빼면 불필요한 상위 비트들은 사라지고 하위비트들, 즉 정수부만 남게 된다. 그림 2.1.3은 43.25에 대해 이러한 과정이 일어나는 예를 보여주고 있다.

그림 2.1.3 수 43.25를 부동소수점 형식을 조작해서 정수로 변환한다. 가수에서 밑줄친 비트들은 메모리에 맞아들어가지 않으며, 폐기된다(반올림이 적용됨).

펜티엄 II에서(모든 것들이 캐시 안에 있다고 할 때) 이 기법을 사용하면 변환 시간이 60 사이클에서 5 사이클로 줄어든다. 물론 인라인 어셈블리 코드를 이용하면 float를 int로 변환할 때 FPU가 반올림 모드를 바꾸지 않도록 만드는 것도 가능하긴 하다. 이는 일반적인 형변환보다는 빠르나, 이 글에서 설명하는 바이어스 기법보다는 일반적으로 느리다.

이 기법은 변환할 부동소수점 수가 더해질 바이어스 상수와 겹치지 않는다는 가정 하에서만 작동한다. 구체적으로 말해서, 수가 223보다 작다면 문제 없이 작동하게 된다.

음의 수를 제대로 처리하려면 bias = ((23 + 127) << 23) + (1 << 22)를 사용해야 한다. 추가된 (1 << 22)는 1.5 x 223에 해당하는데, 이 부분에 의해 음수에 대한 정확한 반올림이 일어나게 된다(그림 2.1.4). 여분의 비트들은 뺄셈의 빌려오기 연산이 가수의 최상위 비트(비트 23)에 영향을 미치지 않도록 하기 위한 것이다. 이 경우 9개가 아니라 10개의 상위 비트들이 제거되므로, 이 기법은 양수보다 1비트 작은 범위에만 적용된다. 즉 변환할 수가 222보다 작아야 하는 것이다.

float를 임의의 소수부 비트들을 가진 고정소수점(fixed-point) 형식으로 변환할 때에는 bias = (23 - 소수부_비트수 + 127) << 23을 사용한다. 음수를 처리하기 위해서는 바이어스에 (1 << 22)를 더해야 한다. 그림 2.1.5는 부동소수점 수 192.8125를 소수부가 2비트인 고정소수점 수로 변환하는 예이다.

정수를 부동소수점 수로 변환할 때에는 위에서 이야기한 과정을 역으로 적용하면 된다.

그림 2.1.4 음의 float를 int로 바꾸는 방식은 양의 float의 경우와 조금 다르다. 이 그림은 -43.25를 변환하는 예이다. 밑줄 친 비트들이 폐기될 때 반올림이 적용됨으로써 정확한 음의 정수가 만들어지는 방식을 주목하기 바란다.
그림 2.1.5 float에서 int로 변환될 때 소수부 비트들은 보존된다. 이 그림은 192.8125를 소수부가 2 비트인 고정소수점 수로 변환하는 예이다.
	n.i = 123;         // 변환할 정수

	n.i += bias.i;     // 정수로서 더하고
	n.f -= bias.f;     // 부동소수점 수로서 뺀다.
	// n.f는 원래의 n.i를 부동소수점으로 변환한 결과인 123.0이 된다.

일반적으로 int를 float로 바꾸는 작업은 일반적인 형변환으로도 충분히 빠르게 수행할 수 있으므로, 이 기법을 사용할 필요는 별로 없을 것이다.

부호 판정

부동소수점 수의 부호 비트는 31번째 비트이다. 이는 정수에서도 마찬가지이다. 따라서 정수 유닛을 통해서도 부동소수점 수의 음과 양을 구분할 수 있다. f를 부동소수점 수라고 할 때, 다음의 두 코드 조각들은 (거의)동일한 의미를 지닌다.

if ( f < 0.0f )	// 부동소수점 비교
...

INTORFLOAT	ftmp;
ftmp.f = f;
if (ftmp.i < 0)	// 정수 비교
...

의미는 같으나 속도는 다를 수 있다. 정수 비교가 더 빠를 수 있는데, 이는 정수의 명령 스트림 파이프라이닝이 더 우월하기 때문이다. 구체적인 성능 평가를 해보면 알 수 있을 것이다("거의"라고 표현한 이유는, 음의 0에 대한 처리 방식이 조금 다르기 때문이다).

비교

부동소수점 형식은 부호, 지수, 가수 순서로 비트들을 저장하므로, 정수 유닛을 통해서 부동소수점 수들을 비교하는 것이 가능하다. a의 지수가 b의 지수보다 크면 가수에 상관없이 a는 b보다 크다. 따라서 다음 두 코드 조각들의 의미는 동일하다.

if ( a < b )			// 부동소수점 비교
...

INTORFLOAT atmp, btmp;
atmp.f = f; btmp.f = b;
if (atmp.i < btmp.i)	// 정수 비교
...

부호 판정에서와 마찬가지로, 정수의 파이프라이닝이 더 우월하므로 정수 비교가 더 빠르다. 단 이 기법은 a와 b가 모두 음수인 경우에는 적용되지 않는다. 이는 부동소수점에서 지수와 가수 비트들이 정수처럼 2의 보수 형식으로 저장되는 것이 아니기 때문이다. 두 수 중 적어도 하나가 양인 것이 틀림없는 경우라면 이 기법이 두 수를 비교하는 가장 빠른 방법이다.

제한

게임 프로그래밍에서 하나의 값을 특정한 범위로 제한(clamping)해야 하는 경우는 흔히 생긴다. 특히 3D 프로그래밍이라면 수를 [0, 1] 범위로 제한해야 하는 일이 많다. 부동소수점 수 f를 0으로 제한하는 것(즉 f<0이면 f=0)은 부호 비트를 하나의 마스크로 만드는 식으로 처리할 수 있다. 다음 코드를 보자.

INTORFLOAT ftmp;
ftmp.f = f;
int s = ftmp.i >> 31;   // 부호 비트 마스크를 만든다.
s = ~s;                 // 마스크의 비트들을 뒤집는다.
ftmp.i &= s;            // ftmp = ftmp & mask
f = ftmp.f;

s에는 f의 비트들을 오른쪽으로 31번 이동(shift)시킨 값이 들어간다. 이에 의해 32개의 비트들 모두 부호 비트로 채워진 마스크가 만들어진다. 이것을 NOT 시키면 f가 음수이면 0, 양수이면 1로 채워진 값이 된다. 다시 이것을 f와 AND 시키면 f는 변하지 않거나 0이 된다. 정리하자면, f가 음수이면 0이 되고, 양수이면 그냥 그대로 변하지 않는 것이다.

이 코드는 완전히 정수 유닛으로 실행되며, 비교나 분기(branching, 分岐)가 없다. 코드를 벤치마크해봤을 때, 부동소수점 비교화 제한에서는 18 사이클 정도가 소요되었으나, 정수 제한 방식에는 5 사이클 이하만 소요되었다(이 사이클들은 루프의 추가부담도 포함한 것임을 주의할 것).

양수를 0으로 제한하는 경우(f>0이면 f = 0)는 음수를 0으로 제한하는 경우보다 사용 횟수가 적지만, 마스크 비트들을 뒤집을 필요가 없으므로 기법 자체는 더 쉽다.

INTORFLOAT ftmp;
ftmp.f = f;
int s = ftmp.i >> 31;	// 부호 비트 마스크를 만든다.

ftmp.i &= s; 			// ftmp = ftmp & mask
f = ftmp.f;

1로의 제한(f > 1이면 f = 1)은 1을 빼고, 0으로 제한하고, 다시 1을 더해서 수행한다.

INTORFLOAT ftmp;
ftmp.f = f  1.0f;
int s = ftmp.i >> 31;	// 부호 비트 마스크를 만든다.
ftmp.i &= s; 			// ftmp = ftmp & mask
f = ftmp.f + 1.0f;

일반적으로, 어셈블리에서 조건부 load 명령을 사용하면 분기할 필요가 없어지므로(분기의 필요성이 존재하면 명령 파이프라인 안에서의 분기 예측 로직이 깨진다) 제한 연산의 속도가 올라가게 된다.

절대값

이건 쉽다. 부동소수점 수는 2의 보수를 사용하지 않으므로, 그냥 부호 비트를 0으로 설정하기만 하면 절대값을 얻을 수 있다.

INTORFLOAT ftmp;
ftmp.f = f;
ftmp.i &= 0x7fffffff;
f = ftmp.f;

f가 0인지 비교할 필요가 없으므로 속도가 매우 빠르다.

사인 및 코사인을 위한 선형 참조 테이블

게임에서 삼각함수는 유용하게 쓰인다. 예를 들어서 거리나 각도를 계산한다던가, 원호를 만든다던가, 물결 메시를 움직이는 경우 등에서 삼각함수는 중요한 역할을 담당한다. 표준 math 라이브러리에는 모든 일반적인 삼각함수들이 포함되어 있으나, 매우 느리며, float이 아니라 double을 다루기 때문에 메모리도 많이 잡아먹는다. 게임의 경우 정밀도가 낮은 float을 사용해도 충분한 경우가 많다.

사인과 코사인 값을 효율적으로 얻고자 할 때 주로 쓰는 것이 참조 테이블(lookup table)이다. 일반적인 접근방식은 각도를 정수 색인으로 참조하고(예를 들면 360도 한바퀴를 0에서 1023으로 표현하는 등) 각 색인의 사인 또는 코사인 값을 고정소수점으로 계산하는 것이다. 그러나 이를 위해서는 게임 프로그래머가 사인과 코사인에 대한 라이브러리 구현을 이해해야 하며, 또 표준적인 라디안(radian) 각도 대신 인위적인 정수 색인을 사용해야 한다는 단점이 존재한다. 효율적인 색인을 위한 부동소수점 트릭을 이용하면, 삼각함수의 구현에 대한 세부를 알지 못해도 표준 라디안 각도를 받아들여서 부동소수점 값을 돌려주는 삼각함수 기능을 얻을 수 있다.

사인

목표는 이런 함수이다.

	float fsin( float theta );

이는 참조 테이블을 이용해서 쉽게 구현할 수 있다. 0에서 2pi를 256 단계로 나눠서 저장하는 참조 테이블을 만드는 것은 간다하다. 루프로 i 값을 증가시키면서 다음의 코드를 수행하면 된다.

sintable[i] = (float)sin((double)i * 2.0*3.14159265/256.0)

i를 0에서 255까지 변화시켜 가면서 sintable 배열을 채우면 해상도가 256이며 float의 정밀도를 가지는 사인값을 담는 사인 참조 테이블이 생긴다.

fsin에서 할 일은 매개변수로 주어진 float 값을 0에서 255 사이의 정수 색인으로 바꾸고 그 색인에 해당하는 float 값을 돌려주는 것뿐이다.

	float fsin( float theta )
	{
	    i = (unsigned int)(theta * 256.0f/
 	        (2.0f*3.14159265f));return table[i];
	    }

그러나 이 구현은 두 가지 문제를 가지고 있다. 우선 이 함수는 느린 float->int 변환을 수행하며, 두 번째로 theta가 범위 [0, 2pi) 바깥이면 정수 색인이 배열의 범위를 넘어가게 된다.

두 문제를 해결한 버전은 다음과 같다.

	#define FTOIBIAS	12582912.0f	// 1.5 * 2^23
	#define PI			3.14159265f

	float fsin( float theta )
	{
		int		i;
		INTORFLOAT	ftmp;
		ftmp.f = theta * (256.0f/(2.0f*PI)) + FTOIBIAS;
		i = ftmp.i & 255;
		return table[i];
	}

이 버전은 앞에서 이야기한 부동소수점 바이어스 트릭을 이용해서 빠른 부동소수점-정수 변환을 수행한다. 또한 변환된 정수를 255로 마스킹하므로 색인이 배열의 범위 안에서만 순환된다. 단 f가 2^22를 넘으면 float->int 변환 트릭이 실패하므로, 주기적으로 f를 감소시켜서 [0, 2p) 범위 안에 들어가도록 해야 할 필요는 여전히 존재한다.

이 버전의 fsin은 펜티엄 II에서(모든 코드와 데이터가 기본 캐시에 들어있다고 할 때) 약 10 사이클을 소비하는데, 이는 표준 math 라이브러리의 sin이 거의 140 사이클인 것에 비하면 엄청난 향상이다(sin이 하드웨어 FPU의 사인 명령을 사용한다는 점을 생각하면 더욱 그렇다).

256 항목의 부동소수점 참조 테이블은 1K의 메모리를 차지하는데, 이 정도면 내부 루프 안에서 캐시에 안전하게 머무를 수 있는 크기이다. 항목이 256개이므로 이 테이블에 의한 사인 값의 정확도는 8 비트이다. 최악의 경우에서의 오차는 참조 테이블을 분석함으로써 알아낼 수 있다(부록 CD의 코드를 참고할 것). 참조 테이블의 크기가 클 수록 정확도가 올라가나, 캐시 적중률이 낮아지므로 속도는 떨어진다.

코사인

코사인 함수도 따로 참조 테이블을 만들어서 사인 함수와 동일한 방법으로 구현하면 된다. 그러나 cos(q) = sin(q + pi/2)라는 성질을 이용하면 사인과 코사인이 같은 참조 테이블을 공유하게 만들 수 있다. 참조 테이블 색인을 만들 때 256/4(pi/2 는 25도에 해당하므로)를 더하면 되는 것이다. 다음 코드가 이를 구현하는 예이다.

	float fcos( float theta )
	{
		int		i;
		INTORFLOAT	ftmp;
		ftmp.f = theta * (256.0f/(2.0f*PI)) + FTOIBIAS + 64f;
		i = ftmp.i & 255;
		return table[i];
	}

상황에 따라서는 동시에 사인과 코사인 값을 모두 구하는 것이 유용할 때가 있다. 그런 경우 하나의 함수 안에서 사인 값을 구하고, 그 때 쓰인 색인에 64를 더하는 식으로 코사인 값을 구하게 되면 성능이 더욱 향상될 것이다. 마찬가지로, 여러 사인들과 코사인들을 한 번에 구해야 하는 경우라면 불필요한 반복 계산을 줄이는 방향으로 코드를 수정하면 된다.

제곱근에 대한 대수적 최적화

제곱근(square root)은 거리 계산이나 벡터 정규화, 이차방정식의 해 구하기 등에서 흔히 쓰인다. FPU에도 제곱근을 구하는 명령이 있긴 하지만, 펜티엄 II CPU에서 표준 C 라이브러리의 sqrt 함수는 여전히 80 사이클 정도를 소비한다. 따라서 최적화를 시도할 먹이감으로 충분히 선정될 만 하다.

제곱근 최적화 역시 부동소수점 비트들을 이리 저리 조작하는 방식으로 이루어진다. 제곱근은 지수에 관련된 것이며 가수와는 상관이 없기 때문에, 지수 비트들만 적당히 조작하면 제곱근을 얻을 수 있다. 부동소수점의 제곱근에 관련된 공식들은 다음과 같다.

f = 1.m x 2e

sqrt(f) = sqrt(1.m x 2e) = sqrt(1.m) x 2e/2

이들을 자세히 보면, 가수는 그대로 놔두고 지수만 2로 나누면 된다는 것을 알 수 있다. 그러나 지수는 정수이므로, 지수가 홀수이면 2로 나눴을 때 하위 비트를 잃게 된다. 이 문제는 지수의 하위 비트를 가수로 넘겨줌으로써 해결할 수 있다. 결론적으로 해답은 바로 다음과 같은 공식이 된다.

sqrt(f) = sqrt(1.m x 2e0) x 2[e/2]

여기서 e0은 지수의 하위 비트이다.

사인에서와 마찬가지로 제곱근도 256 항목의 테이블을 만들어서 참조하는 방식으로 구현할 수 있다. 이 때 테이블에는 가수만 담아두고, 지수는 비트 조작을 통해서 얻으면 된다.

float  fsqrt( float f )
   {
   INTORFLOAT		ftmp;
   unsigned int	n, e;

   ftmp.f = f;
   n = ftmp.i;
   e = (n >> 1) & 0x3f800000;	// 지수를 2로 나눈다.
   n = (n >> 16) & 0xff;	// 테이블 색인은 e0+m22-m16

   ftmp.i = sqrttable[n] + e;	// 결과들을 합한다.

   return ftmp.f;
}

테이블 색인은 가수의 상위 비트들과 지수(e0)의 하위 비트를 조합한 것이다. 참조 테이블은 미리 계산된 제곱근의 가수를 담고 있다.

제곱근의 지수는 f의 지수를 오른쪽으로 1비트 이동시키면 얻을 수 있다(결과적으로는 2로 나누는 것임). 지수는 바이어스된 것이므로 1비트 이동시키면 결과적으로는 지수뿐만 아니라 바이어스 또한 2로 나눠지게 된다. 이는 원치 않는 결과이므로, sqrttable의 항목에 추가적인 인수를 더함으로써 지수를 다시 바이어스시켜야 한다.

이 fsqrt 함수는 펜티엄 II CPU에서 약 16 사이클을 소모하는데, 이는 표준 C 라이브러리의 구현보다 5 배 정도 빠른 것이다. 물론 이것은 모든 것들이 캐시 안에 들어있다는 가정 하의 이야기이다.

알고리즘에 대한 좀 더 상세한 설명은 부록 CD의 예제 코드를 참고하기 바란다.

임의의 함수의 최적화

하나의 매개변수를 가지는 임의의 부동소수점 함수를 생각하보자.

y = f(x)

앞에서 살펴 본 기법들을 통해서, 일반적인 함수에 대한 테이블 기반 최적화의 두 가지 기본적인 방법을 배울 수 있다. 사인과 코사인의 경우, x의 값은 알려진 범위에 대해 선형적으로 수량화(quantize)되며, y를 참조하기 위한 테이블 색인으로 쓰인다. 제곱근의 경우, x의 값은 대수적으로 수량화되며 어떠한 하나의 값(그 즉시 y 값이 되는 것이 아니다)을 참조하기 위한 테이블 색인으로 쓰인다. 그 하나의 값은 x의 지수의 함수에 의해 비례되며, 그 결과로 최종적인 y의 값이 나온다.

선형적 접근에서, 하나의 부동소수점 수는 재비례 된 후 하나의 정수로 변환되며, 그 정수는 선형 수량화를 통해서 하나의 참조 테이블 색인이 된다. 이는 정수 참조 테이블과 매우 비슷한 간단한 기법으로, 다른 점이라면 부동 소수점 값을 정수로 효율적으로 변환하기 위해 약간의 트릭을 사용하는 것 뿐이다. 대수적 접근의 경우에는 부동소수점 비트 패턴을 직접 테이블 색인으로 사용함으로써 대수적인 수량화를 수행한다.

이 두 기법을 일반화하면 임의의 함수에 대한 최적화를 구현할 수 있다. 선형적 수량화를 사용할 것이냐 대수적 수량화를 사용할 것이냐는 함수의 성격에 따라 달라진다.

선형적 수량화

앞에서 이야기한 fsin 함수는 선형적 수량화를 통한 최적화의 틀로 쓰일 수 있다. 어떤 함수의 정의역(x의 범위)이 고정되어 있다고 하자(즉 x E [A, B) ). 그러면 그 범위를 균일하게 포괄하는 하나의 참조 테이블을 만들 수 있으며, 그 범위 안의 x를 테이블에 대한 적절한 색인으로 만들 수 있다. 그렇다고 할 때 최적화된 함수 f는 다음과 같은 모습일 것이다.

#define FTOIBIAS		12582912.0f	// 1.5 * 2^23
#define TABLESIZE		256
#define INDEXSCALE		((float)TABLESIZE / (B  A))

float flut( float x )
{
	int		i;
	INTORFLOAT	ftmp;
	ftmp.f = x * INDEXSCALE + (FTOIBIAS  A * INDEXSCALE);
	i = ftmp.i & (TABLESIZE  1);
	return ftable[i];
}

참조 테이블은 다음과 같은 식으로 초기화된다.

ftable[i] = f( (float)i / INDEXSCALE + A );

여기서 f는 완전한 정밀도를 가진 부동소수점 값을 돌려주는 "진짜" 함수이다. flut는 두 번의 부동소수점 연산(곱셈, 뺄셈)과 한 번의 정수 비트단위 마스킹, 그리고 한 번의 테이블 참조만으로 끝난다. 펜티엄 II CPU의 경우 약 10 사이클 정도가 소비된다.

인접한 두 테이블 항목들을 선형으로 보간함으로써(CPU 사이클이 약간 더 소비되겠지만) 정확성을 높이는 것도 가능하다. 일반적인 함수에 대한 이러한 최적화를 지원(정확성 향상을 추가적인 선형 보간도 포함되어 있다)하는 API가 부록 CD에 들어 있다.

대수적 수량화

선형적 수량화는 범위 [A,B)를 균일하게 다룬다. 함수에 따라서는 대수적 취급이 더 적합한 경우가 있다(제곱근 등). 기본적인 개념은, 부동소수점 x를 정수 범위로 변환하는 대신 부동소수점 표현의 비트들을 직접 테이블 색인으로 사용한다는 것이다. 기본적인 접근 방식은, 부호나 지수, 가수의 일부 비트들을 추출하고 조작함으로써 IEEE 1:8:23 형식의 부동소수점을 그 보다 덜 정밀한 또 다른 임의의 부동소수점 형식으로 변환하는 것이다.

제곱근 예에서는 8개의 비트들을 추출함으로써 0:1:7의 대수적으로 수량화된 값을 얻었다. 이 때 1 비트는 지수로, 7 비트는 가수로 사용되었다. 부호는 폐기했는데, 실수 공간에서 음의 제곱근은 정의되지 않기 때문이다. 0:1:7 형식은 8 비트의 가수(IEEE 표현에서는 항상 가수 앞에 1이 붙는다는 점을 기억할 것)와 1 비트의 지수로 이루어지므로, [1].0000000 x 20에서 [1].1111111 x 21 사이의 값들을 표현할 수 있다. 이는 [1,4)에 해당한다.

제곱근 함수의 작동 과정은 0:1:7 형식으로 수량화된 수에 대한 연산(한 번의 테이블 참조)과 지수에 대한 또 다른 연산으로 나뉘어진다. 그리고 추가적인 작업을 통해서 두 개의 개별적인 연산들을 최적화하고, 최종적으로 가수와 지수를 하나의 32 비트 부동소수점 수(이것이 함수의 결과이다)로 결합한다. 다른 함수들 역시 이러한 대수적 수량화 방법으로 최적화할 수 있다. IEEE 형식에서는 한 번의 이동과 한 번의 마스크 연산만으로도 지수의 하위 비트들과 가수의 상위 비트들을 뽑아낼 수 있다. 지수로부터 ebits 개의 비트들을 뽑고 가수로부터 mbits 개의 비트들을 뽑아내는 방법은 다음과 같다.

bits = (n >> (23 - mbits)) & ((1 << (ebits + mbits)) - 1)

이 코드는 수 n의 가수와 지수 중 원하는 만큼의 비트들을 수 n의 가장 오른쪽으로 밀어서 이동시키고, 불필요한 비트들을 없애기 위해(마스킹) AND 연산을 수행하는 것이다.

부호 비트 역시 손쉽게 조작할 수 있다. 매개변수가 항상 양임이 확실하다던가, 또는 함수의 결과가 항상 양임이 확실하다면 부호 비트는 무시할 수 있다. 결과의 부호가 매개변수의 부호와 같다면(즉 f(-x) = -f(x)), 부호 비트를 저장해 두었다 다시 복원시키기만 하면 된다.

매개변수 값이 특정 범위로 제한되어 있는 함수의 경우, 지수와 대수로부터 뽑아낸 비트들을 마스킹함으로써 직접 테이블 색인을 구할 수 있다. 예를 들어서 정의역이 [1,16)이라면 지수에서 2 비트, 대수에서 4 비트(예를 들자면)를 뽑아내면 된다. 그러면 0:2:4 형식의 비트들이 되는데, 이는 1.0000 x 20에서 1.1111 x 23 사이의 이진 값들을 의미하며, 십진수로는 1.0에서 15.5 사이가 된다. 이 비트들만 뽑아내면 그 즉시 64 항목의 테이블에 대한 색인이 되는 것이다. 이러한 연산은 단 몇 사이클로 수행할 수 있으므로 속도가 빠르다. 그러나 정밀도가 더 많이 필요하다면 테이블을 키워야 하며, 테이블이 어느 정도 이상 커지면 캐시 적중률이 떨어지게 된다.

또 다른 방법은 제곱근 예에서와 같이 지수와 대수 연산을 분해하는 것이다. 함수 f(x)를 다음과 같이 분해할 수 있다고 하면:

f(x) = f(1.m x 2e) = f1(1.m) x 2f2(e)

8 비트의 가수 m을 이용해서 f1을 256 항목의 참조 테이블로 근사하고(approximate), f2는 지수 e에 대한 정수 연산으로서 직접 계산할 수 있다. 본질적으로 이것이 제곱근 예에서 쓰였던 기법이다.

대수적 수량화는 강력한 도구이나, 함수마다 비트들을 조작하는 방식이 서로 다를 경우가 많다. 완전히 일반화된 방법이 항상 가능한 것은 아니나, 이 글에서 설명한 방법들이 특정한 최적화 문제를 해결하는 데에는 도움이 될 수 있을 것이다.

성능 측정

코드를 최적화할 때에는 최적화 전과 최적화 후의 성능을 세심하게 측정해야 한다. 이론상으로는 멋진 최적화 기법도 실제 구현에서는 캐시의 행동방식이라던가 분기 예측 실패, 컴파일러의 멍청한 코드 생성 같은 요인들 때문에 '안하느니만 못한' 결과를 낳을 수 있다. 뭔가를 변경했다면 정말로 성능이 향상되었는지를 꼭 짚어봐야 한다.

컴파일러 최적화 옵션들을 활성화시키는 것도 중요하다. 적당한 곳이라면 인라인 함수들을 사용할 것. 또한, 인라인 함수를 사용했거나 컴파일러 설정을 바꿨다면 그 결과를 측정해 봐야 한다.

코드를 벤치마킹할 때 컴파일러 최적화가 테스트에 방해가 되지 않도록 해야 한다. 코드를 디스어셈블해 보고 의도한 대로 코드가 만들어졌는지 자세히 점검해야 할 것이다. 타이밍이 중요한 부분이라면 루프를 돌려서 시간을 끄는 것도 유용한 방법이 될 수 있다. 그러나 컴파일러의 최적화에 의해 루프 내부가 컴파일에서 제외되어 버린다면 시간 측정 결과는 믿을 수 없게 되어버린다.

펜티엄 컴퓨터의 경우 rdtsc(read time stamp counter) 명령을 이용해서 현재의 CPU 사이클 카운트를 얻을 수 있다. 인텔은 이 명령이 결과를 얻기 시작하기 전에 두 번 더 실행된다고 경고하고 있다. 인텔은 또한 좀 더 일관적인 타이밍 결과를 얻기 위해서는 cpuid 처럼 명령 캐시를 비우는 명령을 사용하도록 권장하고 있다. 절대적 시간을 얻어야 하는 경우, 사이클 카운트를 프로세스의 실행 속도(MHz)로 나누면 초 단위 시간을 얻을 수 있다.

세밀한 성능 측정에서 가장 신뢰할 수 있는 수단이 바로 사이클 카운터들이다. VTune이나 TrueTime 같은 도구들도 고수준 프로파일링에 유용하다(PC의 경우). 어떠한 밴치마킹이든 메모리 행동 방식은 최대한 실제 상황과 같게 만들어야 한다. 메모리 병목 현상은 요즘 프로세서에서 성능을 떨어뜨리는 주범이기 때문이다. 캐시 역시 마찬가지이다. 벤치마크에서의 캐시 상태는 실제 상황과 최대한 비슷해야 한다. 벤치마크의 경우 실제 시간 측정에 들어가기 전에 대상 알고리즘을 몇 번 정도 실행해 줘서 캐시를 미리 채워두는 방법도 고려해 보기 바란다. 그러나 그런 방법으로도 항상 실제 상황이 정확하게 반영되는 것은 아니다. 가장 좋은 방법은 실제로 게임을 돌리는 도중에 직접 성능을 측정하는 것이다. 좀 더 신뢰할 만한 결과를 얻기 위해서는 인터럽트들을 비활성화시키거나, 또는 결과를 여러 번 추출하고 비정상적인 결과는 제외시키는 등의 방법을 사용할 수도 있을 것이다.

이 글에서 언급한 모든 사이클 결과들은 인텔 펜티엄 II 450-MHz 컴퓨터에서 얻은 것이다. 각 연산은 하나의 루프 안에서 1000 번 반복되었으며, 그 전에 함수들을 미리 실행해서 명령 캐시와 데이터 캐시를 미리 채워두었다. 사이클 결과에는 루프에 의한 추가부담도 포함되어 있다. 벤치마크에 쓰인 실제 코드가 부록 CD에 들어 있다.

이 글에서 이야기한 참조 기법들은 참조 테이블이 캐시 안에 남아 있는 경우에만 효과가 있다. 대체로 렌더링 파이프라인이나 역학(physics, 力學) 엔진 내부 루프의 경우라면 테이블이 캐시 밖으로 밀려나지 않을 것이다. 그러나 코드 여기 저기에서 함수(테이블을 사용하는)를 호출한다면 테이블이 캐시 밖으로 밀려날 가능성이 많다. 참조 테이블을 캐시 안에 유지시키기 힘든 경우라면, 다항식 근사(polynomial approximation, 多項式 近似. 이에 대한 내용은 [Edwards00]에 잘 소개되어 있다)처럼 연산이 많고 메모리 접근이 적은 기법이 더 효율적일 수도 있다.

결론

이 글에서는 부동소수점 최적화라는 빙산의 일각만이 소개되었을 뿐이다. 이 글은 참조 테이블 기법을 주로 다루고 있다. 참조 테이블 기법을 사용하면 상당한 성능 향상을 얻을 수도 있지만, 캐시의 작동 방식을 주의해야 하며, 항상 구체적인 성능 측정도 잊지 말아야 한다. 때로는 좀 더 계산적인, 그리고 메모리를 덜 건드리는 방법(다항식 근사 등)을 통해 더욱 빠른 결과를 얻을 수도 있다. 이 글에서 살펴 본 기법들은 여러 가지 방식으로 확장, 개선될 수 있다. 또한 다른 기법들도 탐구해 볼 수 있을 것이다. 어떤 유명한 책의 제목에서처럼, 코드 최적화 기술에는 선(禪)이라는 것이 존재하며, 이 글처럼 짧은 개요를 통해서는 모든 가능성들을 가늠하기가 불가능하다.

참고자료

  • [Abrash94] Abrash, Michael, Zen of Code Optimization, Coriolis Group, 1994.
  • [Edwards00] Edwards, Eddie, "Polynomial Approximations to Trigonometric Functions", Game Programming Gems, Charles River Media, 2000. 번역서는 "삼각 함수들에 대한 다항식 근사", Game Programming Gems, 정보문화사, 2000.
  • [Intel01] 부동소수점 유닛과 FPU 데이터 형식에 대한 Intel 웹 페이지. PC를 중점으로 하고 있으나 기타 다른 IEEE 호환 컴퓨터에도 적용될 수 있다. http://developer.intel.com/design/intarch/techinfo/Pentium/fpu.htm
  • [Lalonde98] Lalonde, Paul, and Dawson, Robert, "A High Speed, Low Precision Square Root", Graphics Gems, Academic Press, 1998.
안정적인 DNS서비스 DNSEver DNS server, DNS service
Posted by 키르히아이스
,