Update changelog.
[deb_x265.git] / source / test / testpool.cpp
CommitLineData
72b9787e
JB
1/*****************************************************************************
2 * Copyright (C) 2013 x265 project
3 *
4 * Authors: Steve Borho <steve@borho.org>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111, USA.
19 *
20 * This program is also available under a commercial proprietary license.
21 * For more information, contact us at license @ x265.com
22 *****************************************************************************/
23
24#include "common.h"
25#include "threadpool.h"
26#include "wavefront.h"
27#include "threading.h"
28#include "md5.h"
29#include "PPA/ppa.h"
30
31#include <sstream>
32#include <iostream>
33
34using namespace x265;
35
36struct CUData
37{
38 CUData()
39 {
40 memset(digest, 0, sizeof(digest));
41 }
42
43 unsigned char digest[16];
44};
45
46struct RowData
47{
48 RowData() : active(false), curCol(0) {}
49
50 Lock lock;
51 volatile bool active;
52 volatile int curCol;
53};
54
55// Create a fake frame class with manufactured data in each CU block. We
56// need to create an MD5 hash such that each CU's hash includes the hashes
57// of the blocks that would have HEVC data dependencies (left, top-left,
58// top, top-right). This will give us one deterministic output hash. We
59// then generate the same hash using the thread pool and wave-front parallelism
60// to verify the thread-pool behavior and the wave-front schedule data
61// structures.
62class MD5Frame : public WaveFront
63{
64private:
65
66 CUData *cu;
67 RowData *row;
68 int numrows;
69 int numcols;
70 Event complete;
71
72public:
73
74 MD5Frame(ThreadPool *pool) : WaveFront(pool), cu(0), row(0) {}
75
76 virtual ~MD5Frame()
77 {
78 // ensure no threads are lingering on FindJob() before allowing
79 // this object's vtable to be destroyed
80 JobProvider::flush();
81
82 delete[] this->cu;
83 delete[] this->row;
84 }
85
86 void initialize(int cols, int rows);
87
88 void encode();
89
90 void processRow(int row, int threadid);
91};
92
93void MD5Frame::initialize(int cols, int rows)
94{
95 this->cu = new CUData[rows * cols];
96 this->row = new RowData[rows];
97 this->numrows = rows;
98 this->numcols = cols;
99
100 if (!this->WaveFront::init(rows))
101 {
102 assert(!"Unable to initialize job queue");
103 }
104}
105
106void MD5Frame::encode()
107{
108 this->JobProvider::enqueue();
109
110 this->WaveFront::enqueueRow(0);
111
112 // NOTE: When EnableRow after enqueueRow at first row, we'd better call pokeIdleThread, it will release a thread to do job
113 this->WaveFront::enableRow(0);
114 this->m_pool->pokeIdleThread();
115
116 this->complete.wait();
117
118 this->JobProvider::dequeue();
119
120 unsigned int *outdigest = (unsigned int*)this->cu[this->numrows * this->numcols - 1].digest;
121
122 std::stringstream ss;
123
124 for (int i = 0; i < 4; i++)
125 {
126 ss << std::hex << outdigest[i];
127 }
128
129 if (ss.str().compare("da667b741a7a9d0ee862158da2dd1882"))
130 std::cout << "Bad hash: " << ss.str() << std::endl;
131}
132
133void MD5Frame::processRow(int rownum, int)
134{
135 // Called by worker thread
136 RowData &curRow = this->row[rownum];
137
138 assert(rownum < this->numrows && rownum >= 0);
139 assert(curRow.curCol < this->numcols);
140
141 while (curRow.curCol < this->numcols)
142 {
143 int id = rownum * this->numcols + curRow.curCol;
144 CUData &curCTU = this->cu[id];
145 MD5 hash;
146
147 // * Fake CTU processing *
148 PPAStartCpuEventFunc(encode_block);
149 memset(curCTU.digest, id, sizeof(curCTU.digest));
150 hash.update(curCTU.digest, sizeof(curCTU.digest));
151 if (curRow.curCol > 0)
152 hash.update(this->cu[id - 1].digest, sizeof(curCTU.digest));
153
154 if (rownum > 0)
155 {
156 if (curRow.curCol > 0)
157 hash.update(this->cu[id - this->numcols - 1].digest, sizeof(curCTU.digest));
158
159 hash.update(this->cu[id - this->numcols].digest, sizeof(curCTU.digest));
160 if (curRow.curCol < this->numcols - 1)
161 hash.update(this->cu[id - this->numcols + 1].digest, sizeof(curCTU.digest));
162 }
163
164 hash.finalize(curCTU.digest);
165 PPAStopCpuEventFunc(encode_block);
166
167 curRow.curCol++;
168
169 if (curRow.curCol >= 2 && rownum < this->numrows - 1)
170 {
171 ScopedLock below(this->row[rownum + 1].lock);
172
173 if (this->row[rownum + 1].active == false &&
174 this->row[rownum + 1].curCol + 2 <= curRow.curCol)
175 {
176 // set active indicator so row is only enqueued once
177 // row stays marked active until blocked or done
178 this->row[rownum + 1].active = true;
179 this->WaveFront::enqueueRow(rownum + 1);
180 this->WaveFront::enableRow(rownum + 1);
181 }
182 }
183
184 ScopedLock self(curRow.lock);
185
186 if (rownum > 0 &&
187 curRow.curCol < this->numcols - 1 &&
188 this->row[rownum - 1].curCol < curRow.curCol + 2)
189 {
190 // row is blocked, quit job
191 curRow.active = false;
192 return;
193 }
194 }
195
196 // * Row completed *
197
198 if (rownum == this->numrows - 1)
199 this->complete.trigger();
200}
201
202int main(int, char **)
203{
204 ThreadPool *pool;
205
206 PPA_INIT();
207
208 pool = ThreadPool::allocThreadPool(1);
209 {
210 MD5Frame frame(pool);
211 frame.initialize(60, 40);
212 frame.encode();
213 }
214 pool->release();
215 pool = ThreadPool::allocThreadPool(2);
216 {
217 MD5Frame frame(pool);
218 frame.initialize(60, 40);
219 frame.encode();
220 }
221 pool->release();
222 pool = ThreadPool::allocThreadPool(4);
223 {
224 MD5Frame frame(pool);
225 frame.initialize(60, 40);
226 frame.encode();
227 }
228 pool->release();
229 pool = ThreadPool::allocThreadPool(8);
230 {
231 MD5Frame frame(pool);
232 frame.initialize(60, 40);
233 frame.encode();
234 }
235 pool->release();
236
237 return 0;
238}