diff --git a/ci/graal/common.json b/ci/graal/common.json index 62a81cf1bc..cb007bcd30 100644 --- a/ci/graal/common.json +++ b/ci/graal/common.json @@ -4,7 +4,7 @@ "Jsonnet files should not include this file directly but use ci/common.jsonnet instead." ], - "mx_version": "7.82.2", + "mx_version": "7.83.0", "COMMENT.jdks": "When adding or removing JDKs keep in sync with JDKs in ci/common.jsonnet", "jdks": { diff --git a/graalpython/com.oracle.graal.python.benchmarks/python/peak/OWNERS.toml b/graalpython/com.oracle.graal.python.benchmarks/python/peak/OWNERS.toml new file mode 100644 index 0000000000..34cb11f338 --- /dev/null +++ b/graalpython/com.oracle.graal.python.benchmarks/python/peak/OWNERS.toml @@ -0,0 +1,6 @@ +[[rule]] +files = "*" +any = [ + "francois.farquet@oracle.com", + "andrija.kolic@oracle.com", +] diff --git a/graalpython/com.oracle.graal.python.benchmarks/python/peak/deltablue.py b/graalpython/com.oracle.graal.python.benchmarks/python/peak/deltablue.py new file mode 100644 index 0000000000..67d0542e89 --- /dev/null +++ b/graalpython/com.oracle.graal.python.benchmarks/python/peak/deltablue.py @@ -0,0 +1,662 @@ +# Copyright (c) 2021, 2026, Oracle and/or its affiliates. All rights reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. Oracle designates this +# particular file as subject to the "Classpath" exception as provided +# by Oracle in the LICENSE file that accompanied this code. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA +# or visit www.oracle.com if you need additional information or have any +# questions. + +""" +deltablue.py +============ + +Ported for the PyPy project. +Contributed by Daniel Lindsley + +This implementation of the DeltaBlue benchmark was directly ported +from the `V8's source code`_, which was in turn derived +from the Smalltalk implementation by John Maloney and Mario +Wolczko. The original Javascript implementation was licensed under the GPL. + +It's been updated in places to be more idiomatic to Python (for loops over +collections, a couple magic methods, ``OrderedCollection`` being a list & things +altering those collections changed to the builtin methods) but largely retains +the layout & logic from the original. (Ugh.) + +""" +def warmupIterations(): + return 300 + + +def iterations(): + return 1000 + +# Used in the polybench harness for aggregating the iteration datapoints. +def summary(): + return { + "name": "OutlierRemovalAverageSummary", + "lower-threshold": 0.0, + "upper-threshold": 1.0, + } + +# The JS variant implements "OrderedCollection", which basically completely +# overlaps with ``list``. So we'll cheat. :D +class OrderedCollection(list): + pass + + +class Strength(object): + REQUIRED = None + STRONG_PREFERRED = None + PREFERRED = None + STRONG_DEFAULT = None + NORMAL = None + WEAK_DEFAULT = None + WEAKEST = None + + def __init__(self, strength, name): + super(Strength, self).__init__() + self.strength = strength + self.name = name + + @classmethod + def stronger(cls, s1, s2): + return s1.strength < s2.strength + + @classmethod + def weaker(cls, s1, s2): + return s1.strength > s2.strength + + @classmethod + def weakest_of(cls, s1, s2): + if cls.weaker(s1, s2): + return s1 + + return s2 + + @classmethod + def strongest(cls, s1, s2): + if cls.stronger(s1, s2): + return s1 + + return s2 + + def next_weaker(self): + strengths = { + 0: self.__class__.WEAKEST, + 1: self.__class__.WEAK_DEFAULT, + 2: self.__class__.NORMAL, + 3: self.__class__.STRONG_DEFAULT, + 4: self.__class__.PREFERRED, + # TODO: This looks like a bug in the original code. Shouldn't this be + # ``STRONG_PREFERRED? Keeping for porting sake... + 5: self.__class__.REQUIRED, + } + return strengths[self.strength] + + +# This is a terrible pattern IMO, but true to the original JS implementation. +Strength.REQUIRED = Strength(0, "required") +Strength.STONG_PREFERRED = Strength(1, "strongPreferred") +Strength.PREFERRED = Strength(2, "preferred") +Strength.STRONG_DEFAULT = Strength(3, "strongDefault") +Strength.NORMAL = Strength(4, "normal") +Strength.WEAK_DEFAULT = Strength(5, "weakDefault") +Strength.WEAKEST = Strength(6, "weakest") + + +class Constraint(object): + + def __init__(self, strength): + super(Constraint, self).__init__() + self.strength = strength + + def add_constraint(self): + global planner + self.add_to_graph() + planner.incremental_add(self) + + def satisfy(self, mark): + global planner + self.choose_method(mark) + + if not self.is_satisfied(): + if self.strength == Strength.REQUIRED: + print('Could not satisfy a required constraint!') + + return None + + self.mark_inputs(mark) + out = self.output() + overridden = out.determined_by + + if overridden is not None: + overridden.mark_unsatisfied() + + out.determined_by = self + + if not planner.add_propagate(self, mark): + print('Cycle encountered') + + out.mark = mark + return overridden + + def destroy_constraint(self): + global planner + if self.is_satisfied(): + planner.incremental_remove(self) + else: + self.remove_from_graph() + + def is_input(self): + return False + + +class UrnaryConstraint(Constraint): + + def __init__(self, v, strength): + super(UrnaryConstraint, self).__init__(strength) + self.my_output = v + self.satisfied = False + self.add_constraint() + + def add_to_graph(self): + self.my_output.add_constraint(self) + self.satisfied = False + + def choose_method(self, mark): + if self.my_output.mark != mark and \ + Strength.stronger(self.strength, self.my_output.walk_strength): + self.satisfied = True + else: + self.satisfied = False + + def is_satisfied(self): + return self.satisfied + + def mark_inputs(self, mark): + # No-ops. + pass + + def output(self): + # Ugh. Keeping it for consistency with the original. So much for + # "we're all adults here"... + return self.my_output + + def recalculate(self): + self.my_output.walk_strength = self.strength + self.my_output.stay = not self.is_input() + + if self.my_output.stay: + self.execute() + + def mark_unsatisfied(self): + self.satisfied = False + + def inputs_known(self, mark): + return True + + def remove_from_graph(self): + if self.my_output is not None: + self.my_output.remove_constraint(self) + self.satisfied = False + + +class StayConstraint(UrnaryConstraint): + + def __init__(self, v, string): + super(StayConstraint, self).__init__(v, string) + + def execute(self): + # The methods, THEY DO NOTHING. + pass + + +class EditConstraint(UrnaryConstraint): + + def __init__(self, v, string): + super(EditConstraint, self).__init__(v, string) + + def is_input(self): + return True + + def execute(self): + # This constraint also does nothing. + pass + + +class Direction(object): + # Hooray for things that ought to be structs! + NONE = 0 + FORWARD = 1 + BACKWARD = -1 + + +class BinaryConstraint(Constraint): + + def __init__(self, v1, v2, strength): + super(BinaryConstraint, self).__init__(strength) + self.v1 = v1 + self.v2 = v2 + self.direction = Direction.NONE + self.add_constraint() + + def choose_method(self, mark): + if self.v1.mark == mark: + if self.v2.mark != mark and Strength.stronger(self.strength, self.v2.walk_strength): + self.direction = Direction.FORWARD + else: + self.direction = Direction.BACKWARD + + if self.v2.mark == mark: + if self.v1.mark != mark and Strength.stronger(self.strength, self.v1.walk_strength): + self.direction = Direction.BACKWARD + else: + self.direction = Direction.NONE + + if Strength.weaker(self.v1.walk_strength, self.v2.walk_strength): + if Strength.stronger(self.strength, self.v1.walk_strength): + self.direction = Direction.BACKWARD + else: + self.direction = Direction.NONE + else: + if Strength.stronger(self.strength, self.v2.walk_strength): + self.direction = Direction.FORWARD + else: + self.direction = Direction.BACKWARD + + def add_to_graph(self): + self.v1.add_constraint(self) + self.v2.add_constraint(self) + self.direction = Direction.NONE + + def is_satisfied(self): + return self.direction != Direction.NONE + + def mark_inputs(self, mark): + self.input().mark = mark + + def input(self): + if self.direction == Direction.FORWARD: + return self.v1 + + return self.v2 + + def output(self): + if self.direction == Direction.FORWARD: + return self.v2 + + return self.v1 + + def recalculate(self): + ihn = self.input() + out = self.output() + out.walk_strength = Strength.weakest_of( + self.strength, ihn.walk_strength) + out.stay = ihn.stay + + if out.stay: + self.execute() + + def mark_unsatisfied(self): + self.direction = Direction.NONE + + def inputs_known(self, mark): + i = self.input() + return i.mark == mark or i.stay or i.determined_by is None + + def remove_from_graph(self): + if self.v1 is not None: + self.v1.remove_constraint(self) + + if self.v2 is not None: + self.v2.remove_constraint(self) + + self.direction = Direction.NONE + + +class ScaleConstraint(BinaryConstraint): + + def __init__(self, src, scale, offset, dest, strength): + self.direction = Direction.NONE + self.scale = scale + self.offset = offset + super(ScaleConstraint, self).__init__(src, dest, strength) + + def add_to_graph(self): + super(ScaleConstraint, self).add_to_graph() + self.scale.add_constraint(self) + self.offset.add_constraint(self) + + def remove_from_graph(self): + super(ScaleConstraint, self).remove_from_graph() + + if self.scale is not None: + self.scale.remove_constraint(self) + + if self.offset is not None: + self.offset.remove_constraint(self) + + def mark_inputs(self, mark): + super(ScaleConstraint, self).mark_inputs(mark) + self.scale.mark = mark + self.offset.mark = mark + + def execute(self): + if self.direction == Direction.FORWARD: + self.v2.value = self.v1.value * self.scale.value + self.offset.value + else: + self.v1.value = ( + self.v2.value - self.offset.value) / self.scale.value + + def recalculate(self): + ihn = self.input() + out = self.output() + out.walk_strength = Strength.weakest_of( + self.strength, ihn.walk_strength) + out.stay = ihn.stay and self.scale.stay and self.offset.stay + + if out.stay: + self.execute() + + +class EqualityConstraint(BinaryConstraint): + + def execute(self): + self.output().value = self.input().value + + +class Variable(object): + + def __init__(self, name, initial_value=0): + super(Variable, self).__init__() + self.name = name + self.value = initial_value + self.constraints = OrderedCollection() + self.determined_by = None + self.mark = 0 + self.walk_strength = Strength.WEAKEST + self.stay = True + + def __repr__(self): + # To make debugging this beast from pdb easier... + return '' % ( + self.name, + self.value + ) + + def add_constraint(self, constraint): + self.constraints.append(constraint) + + def remove_constraint(self, constraint): + self.constraints.remove(constraint) + + if self.determined_by == constraint: + self.determined_by = None + + +class Planner(object): + + def __init__(self): + super(Planner, self).__init__() + self.current_mark = 0 + + def incremental_add(self, constraint): + mark = self.new_mark() + overridden = constraint.satisfy(mark) + + while overridden is not None: + overridden = overridden.satisfy(mark) + + def incremental_remove(self, constraint): + out = constraint.output() + constraint.mark_unsatisfied() + constraint.remove_from_graph() + unsatisfied = self.remove_propagate_from(out) + strength = Strength.REQUIRED + # Do-while, the Python way. + repeat = True + + while repeat: + for u in unsatisfied: + if u.strength == strength: + self.incremental_add(u) + + strength = strength.next_weaker() + + repeat = strength != Strength.WEAKEST + + def new_mark(self): + self.current_mark += 1 + return self.current_mark + + def make_plan(self, sources): + mark = self.new_mark() + plan = Plan() + todo = sources + + while len(todo): + c = todo.pop(0) + + if c.output().mark != mark and c.inputs_known(mark): + plan.add_constraint(c) + c.output().mark = mark + self.add_constraints_consuming_to(c.output(), todo) + + return plan + + def extract_plan_from_constraints(self, constraints): + sources = OrderedCollection() + + for c in constraints: + if c.is_input() and c.is_satisfied(): + sources.append(c) + + return self.make_plan(sources) + + def add_propagate(self, c, mark): + todo = OrderedCollection() + todo.append(c) + + while len(todo): + d = todo.pop(0) + + if d.output().mark == mark: + self.incremental_remove(c) + return False + + d.recalculate() + self.add_constraints_consuming_to(d.output(), todo) + + return True + + def remove_propagate_from(self, out): + out.determined_by = None + out.walk_strength = Strength.WEAKEST + out.stay = True + unsatisfied = OrderedCollection() + todo = OrderedCollection() + todo.append(out) + + while len(todo): + v = todo.pop(0) + + for c in v.constraints: + if not c.is_satisfied(): + unsatisfied.append(c) + + determining = v.determined_by + + for c in v.constraints: + if c != determining and c.is_satisfied(): + c.recalculate() + todo.append(c.output()) + + return unsatisfied + + def add_constraints_consuming_to(self, v, coll): + determining = v.determined_by + cc = v.constraints + + for c in cc: + if c != determining and c.is_satisfied(): + # I guess we're just updating a reference (``coll``)? Seems + # inconsistent with the rest of the implementation, where they + # return the lists... + coll.append(c) + + +class Plan(object): + + def __init__(self): + super(Plan, self).__init__() + self.v = OrderedCollection() + + def add_constraint(self, c): + self.v.append(c) + + def __len__(self): + return len(self.v) + + def __getitem__(self, index): + return self.v[index] + + def execute(self): + for c in self.v: + c.execute() + + +# Main + +def chain_test(n): + """ + This is the standard DeltaBlue benchmark. A long chain of equality + constraints is constructed with a stay constraint on one end. An + edit constraint is then added to the opposite end and the time is + measured for adding and removing this constraint, and extracting + and executing a constraint satisfaction plan. There are two cases. + In case 1, the added constraint is stronger than the stay + constraint and values must propagate down the entire length of the + chain. In case 2, the added constraint is weaker than the stay + constraint so it cannot be accomodated. The cost in this case is, + of course, very low. Typical situations lie somewhere between these + two extremes. + """ + global planner + planner = Planner() + prev, first, last = None, None, None + + # We need to go up to n inclusively. + for i in range(n + 1): + name = "v%s" % i + v = Variable(name) + + if prev is not None: + EqualityConstraint(prev, v, Strength.REQUIRED) + + if i == 0: + first = v + + if i == n: + last = v + + prev = v + + StayConstraint(last, Strength.STRONG_DEFAULT) + edit = EditConstraint(first, Strength.PREFERRED) + edits = OrderedCollection() + edits.append(edit) + plan = planner.extract_plan_from_constraints(edits) + + for i in range(100): + first.value = i + plan.execute() + + if last.value != i: + print("Chain test failed.") + + +def projection_test(n): + """ + This test constructs a two sets of variables related to each + other by a simple linear transformation (scale and offset). The + time is measured to change a variable on either side of the + mapping and to change the scale and offset factors. + """ + global planner + planner = Planner() + scale = Variable("scale", 10) + offset = Variable("offset", 1000) + src = None + + dests = OrderedCollection() + + for i in range(n): + src = Variable("src%s" % i, i) + dst = Variable("dst%s" % i, i) + dests.append(dst) + StayConstraint(src, Strength.NORMAL) + ScaleConstraint(src, scale, offset, dst, Strength.REQUIRED) + + change(src, 17) + + if dst.value != 1170: + print("Projection 1 failed") + + change(dst, 1050) + + if src.value != 5: + print("Projection 2 failed") + + change(scale, 5) + + for i in range(n - 1): + if dests[i].value != (i * 5 + 1000): + print("Projection 3 failed") + + change(offset, 2000) + + for i in range(n - 1): + if dests[i].value != (i * 5 + 2000): + print("Projection 4 failed") + + +def change(v, new_value): + global planner + edit = EditConstraint(v, Strength.PREFERRED) + edits = OrderedCollection() + edits.append(edit) + + plan = planner.extract_plan_from_constraints(edits) + + for i in range(10): + v.value = new_value + plan.execute() + + edit.destroy_constraint() + + +# HOORAY FOR GLOBALS... Oh wait. +# In spirit of the original, we'll keep it, but ugh. +planner = None + + +def run(): + n = 1000 + chain_test(n) + projection_test(n) diff --git a/graalpython/com.oracle.graal.python.benchmarks/python/peak/fibonacci.py b/graalpython/com.oracle.graal.python.benchmarks/python/peak/fibonacci.py new file mode 100644 index 0000000000..b863844314 --- /dev/null +++ b/graalpython/com.oracle.graal.python.benchmarks/python/peak/fibonacci.py @@ -0,0 +1,57 @@ +# Copyright (c) 2021, 2026, Oracle and/or its affiliates. All rights reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. Oracle designates this +# particular file as subject to the "Classpath" exception as provided +# by Oracle in the LICENSE file that accompanied this code. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA +# or visit www.oracle.com if you need additional information or have any +# questions. + +def warmupIterations(): + return 0 + + +def iterations(): + return 1000 + + +def summary(): + return { + "name": "OutlierRemovalAverageSummary", + "lower-threshold": 0, + "upper-threshold": 0.2, + } + + +def fibonacci(n): + if n < 1: + return 0 + if n <= 2: + return 1 + return fibonacci(n - 1) + fibonacci(n - 2) + + +def run(): + number = 31 + fibo_is = 1346269 + + fibo = fibonacci(number) + + if fibo != fibo_is: + raise AssertionError(f"Unexpected result: {fibo}") + + return fibo diff --git a/graalpython/com.oracle.graal.python.benchmarks/python/peak/richards.py b/graalpython/com.oracle.graal.python.benchmarks/python/peak/richards.py new file mode 100644 index 0000000000..d9c5d1242a --- /dev/null +++ b/graalpython/com.oracle.graal.python.benchmarks/python/peak/richards.py @@ -0,0 +1,437 @@ +# Copyright 2008-2010 Isaac Gouy +# Copyright (c) 2013, 2014, Regents of the University of California +# Copyright (c) 2018, 2021, 2026, Oracle and/or its affiliates. +# All rights reserved. +# +# Revised BSD license +# +# This is a specific instance of the Open Source Initiative (OSI) BSD license +# template http://www.opensource.org/licenses/bsd-license.php +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# +# Redistributions of source code must retain the above copyright notice, this +# list of conditions and the following disclaimer. +# +# Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# +# Neither the name of "The Computer Language Benchmarks Game" nor the name of +# "The Computer Language Shootout Benchmarks" nor the name "nanobench" nor the +# name "bencher" nor the names of its contributors may be used to endorse or +# promote products derived from this software without specific prior written +# permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +# based on a Java version: +# Based on original version written in BCPL by Dr Martin Richards +# in 1981 at Cambridge University Computer Laboratory, England +# and a C++ version derived from a Smalltalk version written by +# L Peter Deutsch. +# Java version: Copyright (C) 1995 Sun Microsystems, Inc. +# Translation from C++, Mario Wolczko +# Outer loop added by Alex Jacoby +from __future__ import print_function + +def warmupIterations(): + return 0 + + +def iterations(): + return 1000 + +# Used in the polybench harness for aggregating the iteration datapoints. +def summary(): + return { + "name": "OutlierRemovalAverageSummary", + "lower-threshold": 0.0, + "upper-threshold": 0.2, + } + +# Task IDs +I_IDLE = 1 +I_WORK = 2 +I_HANDLERA = 3 +I_HANDLERB = 4 +I_DEVA = 5 +I_DEVB = 6 + +# Packet types +K_DEV = 1000 +K_WORK = 1001 + +# Packet + +BUFSIZE = 4 + +BUFSIZE_RANGE = list(range(BUFSIZE)) + +class Packet(object): + def __init__(self,l,i,k): + self.link = l + self.ident = i + self.kind = k + self.datum = 0 + self.data = [0] * BUFSIZE + + def append_to(self,lst): + self.link = None + if lst is None: + return self + else: + p = lst + next = p.link + while next is not None: + p = next + next = p.link + p.link = self + return lst + +# Task Records + +class TaskRec(object): + pass + +class DeviceTaskRec(TaskRec): + def __init__(self): + self.pending = None + +class IdleTaskRec(TaskRec): + def __init__(self): + self.control = 1 + self.count = 10000 + +class HandlerTaskRec(TaskRec): + def __init__(self): + self.work_in = None + self.device_in = None + + def workInAdd(self,p): + self.work_in = p.append_to(self.work_in) + return self.work_in + + def deviceInAdd(self,p): + self.device_in = p.append_to(self.device_in) + return self.device_in + +class WorkerTaskRec(TaskRec): + def __init__(self): + self.destination = I_HANDLERA + self.count = 0 +# Task + +class TaskState(object): + def __init__(self): + self.packet_pending = True + self.task_waiting = False + self.task_holding = False + + def packetPending(self): + self.packet_pending = True + self.task_waiting = False + self.task_holding = False + return self + + def waiting(self): + self.packet_pending = False + self.task_waiting = True + self.task_holding = False + return self + + def running(self): + self.packet_pending = False + self.task_waiting = False + self.task_holding = False + return self + + def waitingWithPacket(self): + self.packet_pending = True + self.task_waiting = True + self.task_holding = False + return self + + def isPacketPending(self): + return self.packet_pending + + def isTaskWaiting(self): + return self.task_waiting + + def isTaskHolding(self): + return self.task_holding + + def isTaskHoldingOrWaiting(self): + return self.task_holding or (not self.packet_pending and self.task_waiting) + + def isWaitingWithPacket(self): + return self.packet_pending and self.task_waiting and not self.task_holding + + + + + +tracing = False +layout = 0 + +def trace(a): + global layout + layout -= 1 + if layout <= 0: + print() + layout = 50 + print(a, end=' ') + + +TASKTABSIZE = 10 + +class TaskWorkArea(object): + def __init__(self): + self.taskTab = [None] * TASKTABSIZE + + self.taskList = None + + self.holdCount = 0 + self.qpktCount = 0 + +taskWorkArea = TaskWorkArea() + +class Task(TaskState): + + + def __init__(self,i,p,w,initialState,r): + self.link = taskWorkArea.taskList + self.ident = i + self.priority = p + self.input = w + + self.packet_pending = initialState.isPacketPending() + self.task_waiting = initialState.isTaskWaiting() + self.task_holding = initialState.isTaskHolding() + + self.handle = r + + taskWorkArea.taskList = self + taskWorkArea.taskTab[i] = self + + def fn(self,pkt,r): + raise NotImplementedError + + + def addPacket(self,p,old): + if self.input is None: + self.input = p + self.packet_pending = True + if self.priority > old.priority: + return self + else: + p.append_to(self.input) + return old + + + def runTask(self): + if self.isWaitingWithPacket(): + msg = self.input + self.input = msg.link + if self.input is None: + self.running() + else: + self.packetPending() + else: + msg = None + + return self.fn(msg,self.handle) + + + def waitTask(self): + self.task_waiting = True + return self + + + def hold(self): + taskWorkArea.holdCount += 1 + self.task_holding = True + return self.link + + + def release(self,i): + t = self.findtcb(i) + t.task_holding = False + if t.priority > self.priority: + return t + else: + return self + + + def qpkt(self,pkt): + t = self.findtcb(pkt.ident) + taskWorkArea.qpktCount += 1 + pkt.link = None + pkt.ident = self.ident + return t.addPacket(pkt,self) + + + def findtcb(self,id): + t = taskWorkArea.taskTab[id] + if t is None: + raise Exception("Bad task id %d" % id) + return t + + +# DeviceTask + + +class DeviceTask(Task): + def __init__(self,i,p,w,s,r): + Task.__init__(self,i,p,w,s,r) + + def fn(self,pkt,r): + d = r + assert isinstance(d, DeviceTaskRec) + if pkt is None: + pkt = d.pending + if pkt is None: + return self.waitTask() + else: + d.pending = None + return self.qpkt(pkt) + else: + d.pending = pkt + if tracing: trace(pkt.datum) + return self.hold() + + + +class HandlerTask(Task): + def __init__(self,i,p,w,s,r): + Task.__init__(self,i,p,w,s,r) + + def fn(self,pkt,r): + h = r + assert isinstance(h, HandlerTaskRec) + if pkt is not None: + if pkt.kind == K_WORK: + h.workInAdd(pkt) + else: + h.deviceInAdd(pkt) + work = h.work_in + if work is None: + return self.waitTask() + count = work.datum + if count >= BUFSIZE: + h.work_in = work.link + return self.qpkt(work) + + dev = h.device_in + if dev is None: + return self.waitTask() + + h.device_in = dev.link + dev.datum = work.data[count] + work.datum = count + 1 + return self.qpkt(dev) + +# IdleTask + + +class IdleTask(Task): + def __init__(self,i,p,w,s,r): + Task.__init__(self,i,0,None,s,r) + + def fn(self,pkt,r): + i = r + assert isinstance(i, IdleTaskRec) + i.count -= 1 + if i.count == 0: + return self.hold() + elif i.control & 1 == 0: + i.control //= 2 + return self.release(I_DEVA) + else: + i.control = i.control // 2 ^ 0xd008 + return self.release(I_DEVB) + + +# WorkTask + + +A = ord('A') + +class WorkTask(Task): + def __init__(self,i,p,w,s,r): + Task.__init__(self,i,p,w,s,r) + + def fn(self,pkt,r): + w = r + assert isinstance(w, WorkerTaskRec) + if pkt is None: + return self.waitTask() + + if w.destination == I_HANDLERA: + dest = I_HANDLERB + else: + dest = I_HANDLERA + + w.destination = dest + pkt.ident = dest + pkt.datum = 0 + + for i in BUFSIZE_RANGE: # xrange(BUFSIZE) + w.count += 1 + if w.count > 26: + w.count = 1 + pkt.data[i] = A + w.count - 1 + + return self.qpkt(pkt) + +def schedule(): + t = taskWorkArea.taskList + while t is not None: + pkt = None + + if tracing: + print("tcb =",t.ident) + + if t.isTaskHoldingOrWaiting(): + t = t.link + else: + if tracing: trace(chr(ord("0")+t.ident)) + t = t.runTask() + +def run(): + taskWorkArea.holdCount = 0 + taskWorkArea.qpktCount = 0 + + IdleTask(I_IDLE, 1, 10000, TaskState().running(), IdleTaskRec()) + + wkq = Packet(None, 0, K_WORK) + wkq = Packet(wkq , 0, K_WORK) + WorkTask(I_WORK, 1000, wkq, TaskState().waitingWithPacket(), WorkerTaskRec()) + + wkq = Packet(None, I_DEVA, K_DEV) + wkq = Packet(wkq , I_DEVA, K_DEV) + wkq = Packet(wkq , I_DEVA, K_DEV) + HandlerTask(I_HANDLERA, 2000, wkq, TaskState().waitingWithPacket(), HandlerTaskRec()) + + wkq = Packet(None, I_DEVB, K_DEV) + wkq = Packet(wkq , I_DEVB, K_DEV) + wkq = Packet(wkq , I_DEVB, K_DEV) + HandlerTask(I_HANDLERB, 3000, wkq, TaskState().waitingWithPacket(), HandlerTaskRec()) + + wkq = None + DeviceTask(I_DEVA, 4000, wkq, TaskState().waiting(), DeviceTaskRec()) + DeviceTask(I_DEVB, 5000, wkq, TaskState().waiting(), DeviceTaskRec()) + + schedule() + + return taskWorkArea.holdCount == 9297 and taskWorkArea.qpktCount == 23246 diff --git a/graalpython/com.oracle.graal.python.benchmarks/python/peak/sieve.py b/graalpython/com.oracle.graal.python.benchmarks/python/peak/sieve.py new file mode 100644 index 0000000000..36bc2c4ff0 --- /dev/null +++ b/graalpython/com.oracle.graal.python.benchmarks/python/peak/sieve.py @@ -0,0 +1,59 @@ +# Copyright (c) 2021, 2026, Oracle and/or its affiliates. All rights reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. Oracle designates this +# particular file as subject to the "Classpath" exception as provided +# by Oracle in the LICENSE file that accompanied this code. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA +# or visit www.oracle.com if you need additional information or have any +# questions. + +def warmupIterations(): + return 700 + + +def iterations(): + return 500 + + +def summary(): + return { + "name": "OutlierRemovalAverageSummary", + "lower-threshold": 0.0, + "upper-threshold": 0.2, + } + + +def run(): + number = 600000 + primes = list(range(0, number + 1)) + + i = 2 + while (i ** 2) <= number: + if primes[i] != 0: + for j in range(2, number): + if primes[i] * j > number: + break + else: + primes[primes[i] * j] = 0 + i += 1 + + count = 0 + for c in range(2, number + 1): + if primes[c] != 0: + count += 1 + + return count diff --git a/mx.graalpython/copyrights/overrides b/mx.graalpython/copyrights/overrides index fab68d4aed..89a22c6ca8 100644 --- a/mx.graalpython/copyrights/overrides +++ b/mx.graalpython/copyrights/overrides @@ -61,6 +61,10 @@ graalpython/com.oracle.graal.python.benchmarks/python/micro/special-len.py,zippy graalpython/com.oracle.graal.python.benchmarks/python/warmup/binarytrees3.py,benchmarks.copyright graalpython/com.oracle.graal.python.benchmarks/python/warmup/gcbench.py,pypy.copyright graalpython/com.oracle.graal.python.benchmarks/python/warmup/pads-integerpartitions.py,benchmarks.copyright +graalpython/com.oracle.graal.python.benchmarks/python/peak/deltablue.py,benchmarks.copyright +graalpython/com.oracle.graal.python.benchmarks/python/peak/fibonacci.py,benchmarks.copyright +graalpython/com.oracle.graal.python.benchmarks/python/peak/richards.py,benchmarks.copyright +graalpython/com.oracle.graal.python.benchmarks/python/peak/sieve.py,benchmarks.copyright graalpython/com.oracle.graal.python.cext/expat/ascii.h,expat.copyright graalpython/com.oracle.graal.python.cext/expat/asciitab.h,expat.copyright graalpython/com.oracle.graal.python.cext/expat/expat.h,expat.copyright diff --git a/mx.graalpython/polybench-stable-run-config.json b/mx.graalpython/polybench-stable-run-config.json index 16eb46946d..4fff4e65f9 100644 --- a/mx.graalpython/polybench-stable-run-config.json +++ b/mx.graalpython/polybench-stable-run-config.json @@ -771,5 +771,25 @@ "policy": "outlier-elimination-all-builds", "forks": "4x5", "focus": "0.0-0.4" + }, + "peak/deltablue.py": { + "policy": "outlier-elimination-all-builds", + "forks": "5x4", + "focus": "0.0-1.0" + }, + "peak/fibonacci.py": { + "policy": "outlier-elimination-all-builds", + "forks": "1x5", + "focus": "0.0-0.6" + }, + "peak/richards.py": { + "policy": "outlier-elimination-all-builds", + "forks": "4x5", + "focus": "0.1-0.5" + }, + "peak/sieve.py": { + "policy": "outlier-elimination-all-builds", + "forks": "4x5", + "focus": "0.8-1.0" } } diff --git a/mx.graalpython/suite.py b/mx.graalpython/suite.py index 3927be5d8b..95fff410f7 100644 --- a/mx.graalpython/suite.py +++ b/mx.graalpython/suite.py @@ -5,7 +5,7 @@ # METADATA # # -------------------------------------------------------------------------------------------------------------- - "mxversion": "7.81.0", + "mxversion": "7.83.0", "name": "graalpython", "versionConflictResolution": "latest", @@ -1709,6 +1709,9 @@ "./numpy/common.py": [ "file:graalpython/com.oracle.graal.python.benchmarks/python/micro/numpy/common.py", ], + "./peak/": [ + "file:graalpython/com.oracle.graal.python.benchmarks/python/peak/*.py", + ], # Warmup benchmarks "./warmup/": [ "file:benchmarks/warmup/*.py",