You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
137 lines
4.3 KiB
137 lines
4.3 KiB
// Copyright 2015 The Gemmlowp Authors. All Rights Reserved.
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
#include <atomic> // NOLINT
|
|
#include <vector>
|
|
#include <iostream>
|
|
#include <cstdlib>
|
|
|
|
#include "../internal/multi_thread_gemm.h"
|
|
#include "../profiling/pthread_everywhere.h"
|
|
#include "test.h"
|
|
|
|
namespace gemmlowp {
|
|
|
|
class Thread {
|
|
public:
|
|
Thread(BlockingCounter* blocking_counter, int number_of_times_to_decrement)
|
|
: blocking_counter_(blocking_counter),
|
|
number_of_times_to_decrement_(number_of_times_to_decrement),
|
|
made_the_last_decrement_(false),
|
|
finished_(false) {
|
|
#if defined GEMMLOWP_USE_PTHREAD
|
|
// Limit the stack size so as not to deplete memory when creating
|
|
// many threads.
|
|
pthread_attr_t attr;
|
|
int err = pthread_attr_init(&attr);
|
|
if (!err) {
|
|
size_t stack_size;
|
|
err = pthread_attr_getstacksize(&attr, &stack_size);
|
|
if (!err && stack_size > max_stack_size_) {
|
|
err = pthread_attr_setstacksize(&attr, max_stack_size_);
|
|
}
|
|
if (!err) {
|
|
err = pthread_create(&thread_, &attr, ThreadFunc, this);
|
|
}
|
|
}
|
|
if (err) {
|
|
std::cerr << "Failed to create a thread.\n";
|
|
std::abort();
|
|
}
|
|
#else
|
|
pthread_create(&thread_, nullptr, ThreadFunc, this);
|
|
#endif
|
|
}
|
|
|
|
~Thread() { Join(); }
|
|
|
|
bool Join() {
|
|
while (!finished_.load()) {
|
|
}
|
|
return made_the_last_decrement_;
|
|
}
|
|
|
|
private:
|
|
Thread(const Thread& other) = delete;
|
|
|
|
void ThreadFunc() {
|
|
for (int i = 0; i < number_of_times_to_decrement_; i++) {
|
|
Check(!made_the_last_decrement_);
|
|
made_the_last_decrement_ = blocking_counter_->DecrementCount();
|
|
}
|
|
finished_.store(true);
|
|
}
|
|
|
|
static void* ThreadFunc(void* ptr) {
|
|
static_cast<Thread*>(ptr)->ThreadFunc();
|
|
return nullptr;
|
|
}
|
|
|
|
static constexpr size_t max_stack_size_ = 256 * 1024;
|
|
BlockingCounter* const blocking_counter_;
|
|
const int number_of_times_to_decrement_;
|
|
pthread_t thread_;
|
|
bool made_the_last_decrement_;
|
|
// finished_ is used to manually implement Join() by busy-waiting.
|
|
// I wanted to use pthread_join / std::thread::join, but the behavior
|
|
// observed on Android was that pthread_join aborts when the thread has
|
|
// already joined before calling pthread_join, making that hard to use.
|
|
// It appeared simplest to just implement this simple spinlock, and that
|
|
// is good enough as this is just a test.
|
|
std::atomic<bool> finished_;
|
|
};
|
|
|
|
void test_blocking_counter(BlockingCounter* blocking_counter, int num_threads,
|
|
int num_decrements_per_thread,
|
|
int num_decrements_to_wait_for) {
|
|
std::vector<Thread*> threads;
|
|
blocking_counter->Reset(num_decrements_to_wait_for);
|
|
for (int i = 0; i < num_threads; i++) {
|
|
threads.push_back(new Thread(blocking_counter, num_decrements_per_thread));
|
|
}
|
|
blocking_counter->Wait();
|
|
|
|
int num_threads_that_made_the_last_decrement = 0;
|
|
for (int i = 0; i < num_threads; i++) {
|
|
if (threads[i]->Join()) {
|
|
num_threads_that_made_the_last_decrement++;
|
|
}
|
|
delete threads[i];
|
|
}
|
|
Check(num_threads_that_made_the_last_decrement == 1);
|
|
}
|
|
|
|
void test_blocking_counter() {
|
|
BlockingCounter* blocking_counter = new BlockingCounter;
|
|
|
|
// repeating the entire test sequence ensures that we test
|
|
// non-monotonic changes.
|
|
for (int repeat = 1; repeat <= 2; repeat++) {
|
|
for (int num_threads = 1; num_threads <= 5; num_threads++) {
|
|
for (int num_decrements_per_thread = 1;
|
|
num_decrements_per_thread <= 4 * 1024;
|
|
num_decrements_per_thread *= 16) {
|
|
test_blocking_counter(blocking_counter, num_threads,
|
|
num_decrements_per_thread,
|
|
num_threads * num_decrements_per_thread);
|
|
}
|
|
}
|
|
}
|
|
delete blocking_counter;
|
|
}
|
|
|
|
} // end namespace gemmlowp
|
|
|
|
int main() { gemmlowp::test_blocking_counter(); }
|