fixeria has submitted this change. (
https://gerrit.osmocom.org/c/osmocom-bb/+/39536?usp=email )
Change subject: trx_toolkit/*: Represent bursts as arrays instead of lists
......................................................................
trx_toolkit/*: Represent bursts as arrays instead of lists
Continuing fake_trx profiling story I noticed that on rx path a
noticeable time is spent in converting from ubits to sbits via list
comprehensions. By changing burst representation from py list, which
stores each item as full python object, to an array, which stores each
item as just byte, and by leveraging bytearray.translate, we can speed
up that conversion by ~ 10x:
before:
In [1]: from data_msg import Msg
In [2]: burst = [0, 1] * (142//2)
In [3]: burst
Out[3]:
[0,
1,
0,
1,
0,
...
0,
1]
In [4]: Msg.ubit2sbit(burst)
Out[4]:
[127,
-127,
127,
-127,
...
127,
-127]
In [5]: %timeit Msg.ubit2sbit(burst)
3.01 µs ± 43.3 ns per loop (mean ± std. dev. of 7 runs, 100,000 loops each)
after:
In [2]: burst = bytearray([0, 1] * (142//2))
In [3]: burst
Out[3]: bytearray(b'\x00\x01\x00\x01...\x00\x01')
In [4]: Msg.ubit2sbit(burst)
Out[4]: array('b', [127, -127, 127, -127, ... 127, -127])
In [5]: %timeit Msg.ubit2sbit(burst)
325 ns ± 12.1 ns per loop (mean ± std. dev. of 7 runs, 1,000,000 loops each)
Change-Id: I7314e9e79752e06fa86b9e346a9beacc5e59579e
---
M src/target/trx_toolkit/data_msg.py
M src/target/trx_toolkit/gsm_shared.py
M src/target/trx_toolkit/rand_burst_gen.py
M src/target/trx_toolkit/test_data_msg.py
4 files changed, 36 insertions(+), 25 deletions(-)
Approvals:
fixeria: Looks good to me, approved
Jenkins Builder: Verified
pespin: Looks good to me, but someone else must approve
diff --git a/src/target/trx_toolkit/data_msg.py b/src/target/trx_toolkit/data_msg.py
index 898a4ae..b5993d2 100644
--- a/src/target/trx_toolkit/data_msg.py
+++ b/src/target/trx_toolkit/data_msg.py
@@ -23,8 +23,13 @@
from typing import List
from enum import Enum
+from array import array
from gsm_shared import *
+# _bu2s converts unsigned byte to signed.
+def _bu2s(x):
+ return struct.unpack('b', struct.pack('B', x))[0]
+
class Modulation(Enum):
""" Modulation types defined in 3GPP TS 45.002 """
ModGMSK = (0b0000, 1 * GMSK_BURST_LEN)
@@ -63,7 +68,7 @@
KNOWN_VERSIONS = (0, 1)
def __init__(self, fn = None, tn = None, burst = None, ver = 0):
- self.burst = burst
+ self.burst = burst # bytes|bytearray for ubit, array[b] for sbit, array[B] for usbit
self.ver = ver
self.fn = fn
self.tn = tn
@@ -126,24 +131,29 @@
return result
@staticmethod
- def usbit2sbit(bits: List[int]) -> List[int]:
+ def usbit2sbit(bits): # array[B] -> array[b]
''' Convert unsigned soft-bits {254..0} to soft-bits {-127..127}.
'''
- return [-127 if (b == 0xff) else 127 - b for b in bits]
+ return array('b', bits.tobytes().translate(Msg._tab_usbit2sbit))
+ _tab_usbit2sbit = array('b', [-127 if (b == 0xff) else 127 - b for b in
range(0x100)])
@staticmethod
- def sbit2usbit(bits: List[int]) -> List[int]:
+ def sbit2usbit(bits): # array[b] -> array[B]
''' Convert soft-bits {-127..127} to unsigned soft-bits {254..0}.
'''
- return [127 - b for b in bits]
+ return array('B', bits.tobytes().translate(Msg._tab_sbit2usbit))
+ _tab_sbit2usbit = array('B', [127 - _bu2s(b) for b in range(0x100)])
@staticmethod
- def sbit2ubit(bits: List[int]) -> List[int]:
+ def sbit2ubit(bits): # array[b] -> bytearray
''' Convert soft-bits {-127..127} to bits {1..0}. '''
- return [int(b < 0) for b in bits]
+ return bytearray(bits).translate(Msg._tab_sbit2ubit)
+ _tab_sbit2ubit = array('B', [int(_bu2s(b) < 0) for b in range(0x100)])
@staticmethod
- def ubit2sbit(bits: List[int]) -> List[int]:
+ def ubit2sbit(bits: bytearray): # -> array[b]
''' Convert bits {1..0} to soft-bits {-127..127}. '''
- return [-127 if b else 127 for b in bits]
+ return array('b', bits.translate(Msg._tab_ubit2sbit))
+ _tab_ubit2sbit = array('b', [-127 if b else 127 for b in range(0x100)])
+
def validate(self):
''' Validate the message fields (throws ValueError). '''
@@ -328,13 +338,13 @@
# Distinguish between GSM and EDGE
if length >= EDGE_BURST_LEN:
- self.burst = list(burst[:EDGE_BURST_LEN])
+ self.burst = bytearray(burst[:EDGE_BURST_LEN])
else:
- self.burst = list(burst[:GMSK_BURST_LEN])
+ self.burst = bytearray(burst[:GMSK_BURST_LEN])
def rand_burst(self, length = GMSK_BURST_LEN):
''' Generate a random message specific burst. '''
- self.burst = [random.randint(0, 1) for _ in range(length)]
+ self.burst = bytearray([random.randint(0, 1) for _ in range(length)])
def trans(self, ver = None):
''' Transform this message into RxMsg. '''
@@ -659,7 +669,7 @@
def parse_burst(self, burst):
''' Parse message specific burst. '''
- burst = list(burst)
+ burst = array('B', burst)
if self.ver == 0x00:
burst = self._parse_burst_v0(burst)
@@ -673,7 +683,7 @@
if length is None:
length = self.mod_type.bl
- self.burst = [random.randint(-127, 127) for _ in range(length)]
+ self.burst = array('b', [random.randint(-127, 127) for _ in range(length)])
def trans(self, ver = None):
''' Transform this message to TxMsg. '''
diff --git a/src/target/trx_toolkit/gsm_shared.py b/src/target/trx_toolkit/gsm_shared.py
index 5f59ba6..977d2aa 100644
--- a/src/target/trx_toolkit/gsm_shared.py
+++ b/src/target/trx_toolkit/gsm_shared.py
@@ -81,7 +81,7 @@
self.tsc_set = tsc_set
# Generate Training Sequence bits
- self.seq = [int(x) for x in seq_str]
+ self.seq = bytearray(int(x) for x in seq_str)
@classmethod
def pick(self, burst):
diff --git a/src/target/trx_toolkit/rand_burst_gen.py
b/src/target/trx_toolkit/rand_burst_gen.py
index c474c75..6cdab30 100644
--- a/src/target/trx_toolkit/rand_burst_gen.py
+++ b/src/target/trx_toolkit/rand_burst_gen.py
@@ -25,7 +25,7 @@
class RandBurstGen:
# GSM 05.02 Chapter 5.2.6 Dummy Burst
- db_bits = [
+ db_bits = bytearray([
0, 0, 0,
1, 1, 1, 1, 1, 0, 1, 1, 0, 1, 1, 1, 0, 1, 1, 0,
0, 0, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 1, 1, 1, 0,
@@ -37,7 +37,7 @@
1, 1, 1, 0, 1, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 1,
0, 0, 1, 0, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0,
0, 0, 0,
- ]
+ ])
# Pick a random TSC for a given burst type
def get_rand_tsc(self, bt):
@@ -72,11 +72,11 @@
# Tailing bits
buf += [0] * 3
- return buf
+ return bytearray(buf)
# Generate a frequency correction burst
def gen_fb(self):
- return [0] * GMSK_BURST_LEN
+ return bytearray([0] * GMSK_BURST_LEN)
# Generate a synchronization burst
def gen_sb(self, tsc = None):
@@ -99,7 +99,7 @@
# Tailing bits
buf += [0] * 3
- return buf
+ return bytearray(buf)
# Generate a dummy burst
def gen_db(self):
@@ -126,4 +126,4 @@
# Guard period
buf += [0] * 60
- return buf
+ return bytearray(buf)
diff --git a/src/target/trx_toolkit/test_data_msg.py
b/src/target/trx_toolkit/test_data_msg.py
index ec6c2ec..13a64a6 100644
--- a/src/target/trx_toolkit/test_data_msg.py
+++ b/src/target/trx_toolkit/test_data_msg.py
@@ -19,6 +19,7 @@
# GNU General Public License for more details.
import unittest
+from array import array
from data_msg import Msg, TxMsg, RxMsg
@@ -136,8 +137,8 @@
# Validate bit conversations
def test_bit_conv(self):
- usbits_ref = list(range(0, 256))
- sbits_ref = list(range(-127, 128))
+ usbits_ref = array('B', range(0, 256))
+ sbits_ref = array('b', range(-127, 128))
# Test both usbit2sbit() and sbit2usbit()
sbits = Msg.usbit2sbit(usbits_ref)
@@ -147,10 +148,10 @@
# Test both sbit2ubit() and ubit2sbit()
ubits = Msg.sbit2ubit(sbits_ref)
- self.assertEqual(ubits, ([1] * 127 + [0] * 128))
+ self.assertEqual(ubits, bytearray([1] * 127 + [0] * 128))
sbits = Msg.ubit2sbit(ubits)
- self.assertEqual(sbits, ([-127] * 127 + [127] * 128))
+ self.assertEqual(sbits, array('b', [-127] * 127 + [127] * 128))
def _test_transform(self, msg):
# Prepare given messages
--
To view, visit
https://gerrit.osmocom.org/c/osmocom-bb/+/39536?usp=email
To unsubscribe, or for help writing mail filters, visit
https://gerrit.osmocom.org/settings?usp=email
Gerrit-MessageType: merged
Gerrit-Project: osmocom-bb
Gerrit-Branch: master
Gerrit-Change-Id: I7314e9e79752e06fa86b9e346a9beacc5e59579e
Gerrit-Change-Number: 39536
Gerrit-PatchSet: 2
Gerrit-Owner: kirr <kirr(a)nexedi.com>
Gerrit-Reviewer: Jenkins Builder
Gerrit-Reviewer: fixeria <vyanitskiy(a)sysmocom.de>
Gerrit-Reviewer: pespin <pespin(a)sysmocom.de>
Gerrit-CC: osmith <osmith(a)sysmocom.de>