안녕하세요. 다름이 아니라 스프링 기본편 수업을 듣고, 혼자 해보다가 궁금증이 생겨서 글을 씁니다. Primary 어노테이션을 적용하면 스프링 컨테이너에서 확인 하고 저 어노테이션이 붙은걸로 의존관계 자동 주입을 해준다는 것은 이해했습니다. 자주 사용한다고 말씀 해주셔서 혼자 만들어보면서 문제 없이 사용했습니다. Qualifier같은 경우는 따로 MainDiscountPolicy를 만들어서 사용할 RateDiscountPolicy 나 FixDiscountPolicy 상단에 Qualifier를 붙여 사용한다는 것도 이해를 했는데요. 최종적으로 생성자 자동주입을 사용할 땐 @RequiredArgsConstructor 를 사용하게 되면서 코드를 간소화 하기에 primary를 사용할 거 같은데 만약 qualifier를 사용하게 될 상황이 온다면@RequiredArgsConstructor 이 부분을 삭제하고 다시 생성자를 만들면서 적용을 시켜야 하는 건가요? 제가 설명을 잘 못하는 거 같아서 사진도 덧붙입니다... 이 상황에서 orderserviceImpl 상단에 @RequiredArgsConstructor를 제거 하고, 다시 생성자를 만들어 @Autowired를 붙여줘 @MainDiscountPolicy 사용했습니다. 근데 실무에서도 이것도 사용한다고 말씀하셨는데, 그러면 테스트 코드를 작성해서 할 때마다 @RequiredArgsConstructor를 제거 하고 다시 이렇게 만들어줘서 돌리는지 궁금합니다. primary로 써도 손 쉬운데 뭔가 번거로운 거 같은데 사용하나 해서 질문 올립니다. 감사합니다
divide & conquer 전략으로 lockguard를 쓰는 thread, atomic 쓰는 thread, promise 쓰는 thread 이렇게 만드는 건데 애초에 divide & conquer 전략이 무엇인지도 잘 모르겠고 코드를 배운대로 짠 거 같은데 자꾸 오류가 나서 막막합니다. 도움을 조금 주시면 감사하겠습니다. 밑에는 제가 짠 코드입니다. // 여태까지 멀티 스레딩을 사용하는 여러가지 문법들을 배워봤다. // 이번에는 멀티 스레딩으로 벡터 내적을 계산하는 예제를 통해서 실제 문법이 어떻게 사용이 되는 지 그리고 차이가 어떻게 나는 지 등등을 한 번 직접 실험하면서 살펴본다. #include <iostream> #include <chrono> #include <mutex> #include <random> #include <thread> #include <utility> #include <vector> #include <atomic> #include <future> #include <numeric> // std::inner_product #include <execution> // parallel execution using namespace std; mutex mtx; // mutex를 꼭 전역 변수로 선언할 필요는 없다. 여러 개의 스레드가 공통적으로 접근할 수 있는 scope에 있으면 된다. // 그리고 mutex도 항상 존재할 필요는 없다. mutex로 lockguard를 만들어주고 싶은 부분이 작동할 때만 mutex가 살아있으면 된다. // 그러니깐 코딩할 때 mutex를 어떤 클래스의 멤버로 집어넣어도 되고 scope에 넣어도 된다. void dotProductNaive(const vector<int>& v0, const vector<int>& v1, const unsigned i_start, const unsigned i_end, unsigned long long& sum) { for (unsigned i = i_start; i < i_end; ++i) sum += v0[i] * v1[i]; } void dotProductLock(const vector<int>& v0, const vector<int>& v1, const unsigned i_start, const unsigned i_end, unsigned long long& sum) { //std::scoped_lock lock(mtx); // 속도를 높이기 위해 scoped_lock을 for문 밖으로 옮기면 어떨까라고 생각할 수 있다. // scoped_lock이 작동하는 범위를 scope 전체가 되도록 옮기니깐 속도가 오히려 훨씬 빨라졌다. // 그러면 오히려 밖으로 빼면 되나 생각할 수 있는데 지금 cout으로 출력된 값을 보니깐 start, end, start, end, ... 이렇게 나왔다. // 이 전체가 lock이기 때문에 병렬이 아니라, 동시적으로 실행이 된 게 아니라 순차적으로 실행이 됐다는 의미이다. // 실제로 이런 방식으로 병렬처리를 하는 것은 의미가 없다는 거다. // 오히려 스레드를 생성하는 오버헤드만 커지게 된다. cout << "Thread start " << endl; for (unsigned i = i_start; i < i_end; ++i) { std::scoped_lock lock(mtx); // C++ 17 // 초반에 말했었지만 scoped_lock은 영역이 작은 쪽에 들어가는게 좋은데 작은 영역에 들어오긴 했지만 너무 빈번하게 자주 실행이 되어버리면 오히려 느려진다. // 멀티 스레딩을 안 하느니만 못하는 최악의 결과가 나오게 된다. 그래서 멀티 스레딩을 처음 시도를 하는 경우에는 오히려 퍼포먼스가 떨어지거나 혹은 퍼포먼스는 안 떨어지는 대신에 결과가 나쁘거나 이런 식으로 작동이 될 수 있다. // 그런데 이것도 경우에 따라서 또 다르다. 락이 걸린 scope 전후로 어마어마하게 일이 많으면 상대적으로 멀티 스레딩의 효율이 높아지기도 하는데 일단은 극단적인 경우를 가지고 실수를 할 수 있는 부분에 대해서 먼저 설명을 했다. sum += v0[i] * v1[i]; } cout << "Thread end " << endl; } void dotProductAtomic(const vector<int>& v0, const vector<int>& v1, const unsigned i_start, const unsigned i_end, atomic<unsigned long long>& sum) { for (unsigned i = i_start; i < i_end; ++i) { sum += v0[i] * v1[i]; // atomic 연산이 일반연산보다 느리기 때문에 빈번하게 호출되는 곳에 atomic 연산을 넣어버리면 느려진다. } } auto dotProductFuture(const vector<int>& v0, const vector<int>& v1, const unsigned i_start, const unsigned i_end) { int sum = 0; // local sum for (unsigned i = i_start; i < i_end; ++i) { sum += v0[i] * v1[i]; } return sum; } int main() { /* v0 = { 1, 2, 3 } v1 = { 4, 5, 6 } v0_dot_v1 = 1*4 + 2*5 + 3*6; */ const long long n_data = 100'000'000; const unsigned n_threads = 4; /* initialize vectors */ std::vector<int> v0, v1; v0.reserve(n_data); v1.reserve(n_data); // reserve()로 push할 때의 효율을 높였다. random_device seed; mt19937 engine(seed()); uniform_int_distribution<> uniformDist(1, 10); for (long long i = 0; i < n_data; ++i) { v0.push_back(uniformDist(engine)); v1.push_back(uniformDist(engine)); } cout << "std::inner_product" << endl; { const auto sta = chrono::steady_clock::now(); const auto sum = std::inner_product(v0.begin(), v0.end(), v1.begin(), 0ull); // auto인데도 unsigned long long으로 자동 추론을 할 수 있게 된 이유는 0을 0ull으로 표기를 해서 넣어줬기 때문이다. const chrono::duration<double> dur = chrono::steady_clock::now() - sta; cout << dur.count() << endl; cout << sum << endl; cout << endl; } // 멀티 스레딩 프로그래밍을 한다면 정답을 계산을 하고 그것과 정답이 맞는지 비교하고 그리고 퍼포먼스를 또 비교하는 방식으로 작업을 하는 것이 좋다. cout << "Naive" << endl; { const auto sta = chrono::steady_clock::now(); unsigned long long sum = 0; vector<thread> threads; threads.resize(n_threads); const unsigned n_per_thread = n_data / n_threads; // assumes remainder = 0 for (unsigned t = 0; t < n_threads; ++t) threads[t] = std::thread(dotProductNaive, std::ref(v0), std::ref(v1), t * n_per_thread, (t + 1) * n_per_thread, std::ref(sum)); for (unsigned t = 0; t < n_threads; ++t) threads[t].join(); const chrono::duration<double> dur = chrono::steady_clock::now() - sta; cout << dur.count() << endl; cout << sum << endl; cout << endl; } // 스레드 개수를 임의의 개수로 사용할 수 있게 하기 위해서 조금 코드가 복잡해졌다. // 실행해보면 시간도 더 걸렸고 무엇보다 답이 틀리게 나온다. 레이스 컨디션 때문에 그렇다. // sum에다가 값을 더하고 있는데 여러 개의 스레드가 동시에 값을 건드리니깐 문제가 생겨서 값이 제대로 더해지지 않고 있다. cout << "Lockguard" << endl; { const auto sta = chrono::steady_clock::now(); unsigned long long sum = 0; vector<thread> threads; threads.resize(n_threads); const unsigned n_per_thread = n_data / n_threads; // assumes remainder = 0 for (unsigned t = 0; t < n_threads; ++t) threads[t] = std::thread(dotProductLock, std::ref(v0), std::ref(v1), t * n_per_thread, (t + 1) * n_per_thread, std::ref(sum)); for (unsigned t = 0; t < n_threads; ++t) threads[t].join(); const chrono::duration<double> dur = chrono::steady_clock::now() - sta; cout << dur.count() << endl; cout << sum << endl; cout << endl; } cout << "Atomic" << endl; { const auto sta = chrono::steady_clock::now(); atomic<unsigned long long> sum = 0; vector<thread> threads; threads.resize(n_threads); const unsigned n_per_thread = n_data / n_threads; // assumes remainder = 0 for (unsigned t = 0; t < n_threads; ++t) threads[t] = std::thread(dotProductAtomic, std::ref(v0), std::ref(v1), t * n_per_thread, (t + 1) * n_per_thread, std::ref(sum)); for (unsigned t = 0; t < n_threads; ++t) threads[t].join(); const chrono::duration<double> dur = chrono::steady_clock::now() - sta; cout << dur.count() << endl; cout << sum << endl; cout << endl; } cout << "Future" << endl; { const auto sta = chrono::steady_clock::now(); unsigned long long sum = 0; vector<std::future<int>> futures; // unsigned long long으로 받지 않은 이유는 부분의 합은 integer 범위 안에 들어오기 때문이다. futures.resize(n_threads); const unsigned n_per_thread = n_data / n_threads; // assumes remainder = 0 for (unsigned t = 0; t < n_threads; ++t) futures[t] = std::async(dotProductFuture, std::ref(v0), std::ref(v1), t * n_per_thread, (t + 1) * n_per_thread); // 전역 변수 역할을 하는 sum이 들어갈 필요가 없다. 왜냐하면 dotProductFuture()에서는 local sum을 계산해서 return 해주고 있기 때문이다. for (unsigned t = 0; t < n_threads; ++t) sum += futures[t].get(); // .join()을 하는 대신에 .get()을 받아서 sum에다가 더해주고 있다. const chrono::duration<double> dur = chrono::steady_clock::now() - sta; cout << dur.count() << endl; cout << sum << endl; cout << endl; } // future 쓰는게 무조건 좋구나 thread 나빠 이렇게 생각할 수도 있다. 그런데 꼭 그렇진 않다. //TODO: use divide and conquer strategy for std::thread, std::thread & std::atomic //TODO: use divide and conquer strategy for std::thread & std::promise /* divide and conquer strategy for lockguard */ cout << "Divide Lockguard" << endl; { const auto sta = chrono::steady_clock::now(); vector<unsigned long long> divide_sum; divide_sum.resize(n_threads); vector<thread> threads; threads.resize(n_threads); const unsigned n_per_thread = n_data / n_threads; // assumes remainder = 0 for (unsigned t = 0; t < n_threads; ++t) threads[t] = std::thread(dotProductLock, std::ref(v0), std::ref(v1), t * n_per_thread, (t + 1) * n_per_thread, std::ref(divide_sum[t])); for (unsigned t = 0; t < n_threads; ++t) threads[t].join(); unsigned long long sum = 0; for (unsigned t = 0; t < n_threads; ++t) sum += divide_sum[t]; const chrono::duration<double> dur = chrono::steady_clock::now() - sta; cout << dur.count() << endl; cout << sum << endl; cout << endl; } /* divide and conquer strategy for atomic */ cout << "Divide Atomic" << endl; { const auto sta = chrono::steady_clock::now(); vector<atomic<unsigned long long>> divide_sum; divide_sum.resize(n_threads); vector<thread> threads; threads.resize(n_threads); const unsigned n_per_thread = n_data / n_threads; // assumes remainder = 0 for (unsigned t = 0; t < n_threads; ++t) threads[t] = std::thread(dotProductAtomic, std::ref(v0), std::ref(v1), t * n_per_thread, (t + 1) * n_per_thread, std::ref(divide_sum[t])); for (unsigned t = 0; t < n_threads; ++t) threads[t].join(); unsigned long long sum = 0; for (unsigned t = 0; t < n_threads; ++t) sum += divide_sum[t]; const chrono::duration<double> dur = chrono::steady_clock::now() - sta; cout << dur.count() << endl; cout << sum << endl; cout << endl; } /* divide and conquer strategy for promise */ cout << "Promise" << endl; { const auto sta = chrono::steady_clock::now(); unsigned long long sum = 0; vector<std::promise<int>> proms; proms.resize(n_threads); vector<std::future<int>> futures; futures.resize(n_threads); vector<thread> threads; threads.resize(n_threads); const unsigned n_per_thread = n_data / n_threads; // assumes remainder = 0 for (unsigned t = 0; t < n_threads; ++t) { futures[t] = proms[t].get_future(); threads[t] = std::thread([&](std::promise<int>&& prom) { prom.set_value(dotProductFuture(std::ref(v0), std::ref(v1), t* n_per_thread, (t + 1)* n_per_thread)); }, std::move(proms[t])); } for (unsigned t = 0; t < n_threads; ++t) sum += futures[t].get(); for (unsigned t = 0; t < n_threads; ++t) threads[t].join(); const chrono::duration<double> dur = chrono::steady_clock::now() - sta; cout << dur.count() << endl; cout << sum << endl; cout << endl; } cout << "std::transform_reduce" << endl; { const auto sta = chrono::steady_clock::now(); //const auto sum = std::transform_reduce(std::execution::seq, v0.begin(), v0.end(), v1.begin(), 0ull); const auto sum = std::transform_reduce(std::execution::par, v0.begin(), v0.end(), v1.begin(), 0ull); const chrono::duration<double> dur = chrono::steady_clock::now() - sta; cout << dur.count() << endl; cout << sum << endl; cout << endl; } // std::inner_product의 병렬 버전이 std::transform_reduce()이다. std::transform_reduce() 말고도 std::sort() 같은 함수들이 전부 병렬 처리를 지원해준다. 그래서 기본적인 것들을 빠르게 구현할 때는 병렬 처리를 직접 구현할 필요는 없다. // 하지만 병렬 처리 함수들을 사용하기에 앞서 멀티 스레딩을 먼저 조금 알고 있으면 사용할 때 조금 더 유리할 거고 둘째로 std에서 만들어준 코드들이 모든 문제들을 전부 병렬 처리 해주지는 않는다. 그리고 여전히 병렬 처리했을 때 문제가 생길 여지가 분명히 존재한다. return 0; }
안녕하세요! 제 코드 오류 해결을 못해서 강의까지 듣고 이해했습니다. 선생님께서 하신 코드는 이해했는데 하지만 제가 짠 코드가 왜 틀리는지 잘 이해가 안 가서 질문드립니다! N, M = map(int,input().split()) List = list(map(int,input().split())) cnt = 0 x_cnt = 0 while(cnt < N ): Sum = 0 for i in range(cnt,N): Sum += List[i] # index 오류! if Sum == M : x_cnt += 1 break elif Sum > M : break cnt += 1 print(x_cnt) in 1, 2 ,3 까지는 맞지만 4, 5에서 값이 커지면 sum += List[ i ] 부분에서 IndexError: list index out of range 나옵니다. 왜 인덱스 범위가 벗어나는지 이유를 모르겠습니당.. 선생님 방금 추가로 얻은 정보인데요! 제가 선생님과 비슷한 방식으로 풀었다가 그것도 똑같이 인덱스 범위 오류가 나서 아예 선생님 코드를 복붙을 했는데도 인덱스 범위 오류가 나는 거에요!! 이거는 제 파이썬 버전이나 컴퓨터 설정에서 문제가 있는 걸까요?
현재 노드 버전이 올라가서 노드도 16.17로 내리고 나머지도 다 버전 내려서 버전에 맞게 설정하고 yarn add next@12.1.0 react@17.0.2 react-dom@17.0.2 --exact 까지 했는데 노션에 있는 것 처럼 페이지와 api 여러 등등이 뜨지 않습니다 뭐가 문제일까요?ㅠㅠ
fire fox에 burp suite로 네트워크 설정을 했는데 그냥 fire fox에서 하면 burp suite에 아무것도 안 떠요. 사실 원래도 버프 스위트에서 오픈 브라우저를 하면 샌드박스가 없다고 실행이 안돼서 샌드박스 없이 하는 설정을 켜서 했거든요? 혹시 이것 때문에 그런가요? 만약 그러면 샌드박스 설치하는 법도 알려주세요.
학습하는 분들께 도움이 되고, 더 좋은 답변을 드릴 수 있도록 질문전에 다음을 꼭 확인해주세요. 1. 강의 내용과 관련된 질문을 남겨주세요. 2. 인프런의 질문 게시판과 자주 하는 질문(링크)을 먼저 확인해주세요. (자주 하는 질문 링크: https://bit.ly/3fX6ygx) 3. 질문 잘하기 메뉴얼(링크)을 먼저 읽어주세요. (질문 잘하기 메뉴얼 링크: https://bit.ly/2UfeqCG) 질문 시에는 위 내용은 삭제하고 다음 내용을 남겨주세요. ========================================= [질문 템플릿] 1. 강의 내용과 관련된 질문인가요? (예/아니오) 2. 인프런의 질문 게시판과 자주 하는 질문에 없는 내용인가요? (예/아니오) 3. 질문 잘하기 메뉴얼을 읽어보셨나요? (예/아니오) [질문 내용] pathvariable 을 쓰는 경우와 requestparam 을 쓰는 경우가 나뉘어져 있나요? 아니면 둘의 역할이 달라 다르게 쓰는 건가요?
function solution(m, product) { let count = 0; let sumResult = []; for (let i = 0; i < product.length; i++) { product[i][0] /= 2; let sum = 0; let priceSum = [...product].map((price) => price[0] + price[1]); priceSum.sort((a, b) => a - b); for (let j = 0; j < priceSum.length; j++) { sum += priceSum[j]; count++; if (sum > m) { sumResult.push(j); break; } } product[i][0] *= 2; } return Math.max(...sumResult); } 4-4 졸업선물 풀어보았는데요 강사님과 너무 다르게 풀어서 시간복잡도 면에서 자신이 없네요ㅜ
[질문 템플릿] 1. 강의 내용과 관련된 질문인가요? (예) 2. 인프런의 질문 게시판과 자주 하는 질문에 없는 내용인가요? (예) 3. 질문 잘하기 메뉴얼을 읽어보셨나요? (예) [질문 내용] JPA를 이용한 페이징 처리를 공부하다가 문득 궁금한점이 생겼습니다. Page 및 Slice, Sort의 처리와 더불어 Repository의 기능을 통해서 값을 처리하는것을 배웠는데요. 그런데 만약 2개 이상의 테이블을 Join한 상태라면 페이징 처리를 어떤 방법으로 하는지 궁금합니다. 예를들어 Page<Member, Order> 이런식으로는 어렵고, 1개의 Repository 안에 직접적인 조인쿼리 방식으로 값을 불러와서 적용해야 할까요? 아니면 MappingTable을 이용하거나 Member와 Order라는 조인쿼리에서 실질적으로 보여주는 필드값들만 DTO형태로 만들어서 Page<MemberViewDTO> 이런식으로 적용해야되는것일까요?! 아니면 이럴때 적용하는것이 QueryDSL이라는것인지 궁금합니다.(아직 QueryDSL을 배우진 않았고, 들어만 봤습니다.) 2개의 테이블에서 뽑아낸 DB값들을 Page 처리하기위해선 어떠한 방식으로 할 수 있는지 궁금합니다.
- 학습 관련 질문을 남겨주세요. 상세히 작성하면 더 좋아요! - 먼저 유사한 질문이 있었는지 검색해보세요. - 서로 예의를 지키며 존중하는 문화를 만들어가요. - 잠깐! 인프런 서비스 운영 관련 문의는 1:1 문의하기를 이용해주세요. 안녕하세요 강사님 카프카 서버기동에서 주키퍼와 카프카를 실행한후에 주키퍼부터 내릴경우 카프카 실행이 안되는 이유가 궁금합니다.
안녕하세요 강사님 현재 리덕스 공부중에 있습니다. createStore를 쓰려고 했더니 deprecated 메시지가 뜨면서 reactjs/toolkit 패키지의 configureStore 메서드를 쓰라고 합니다. 이렇게 공부한 함수가 deprecated 되거나 잘 쓰지 않는 함수 일때는 그래도 redux를 이해하는데 기본이 되는 함수이니 계속 공부를 해야 되나요? 아니면 새로운 메소드를 공부해 보는 편이 좋나요??